(function (global, factory) { if (typeof define === "function" && define.amd) { define(["exports", "three"], factory); } else if (typeof exports !== "undefined") { factory(exports, require("three")); } else { var mod = { exports: {} }; factory(mod.exports, global.three); global.Refractor = mod.exports; } })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _three) { "use strict"; Object.defineProperty(_exports, "__esModule", { value: true }); _exports.Refractor = void 0; function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } Object.defineProperty(subClass, "prototype", { value: Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }), writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } var Refractor = /*#__PURE__*/function (_Mesh) { _inherits(Refractor, _Mesh); var _super = _createSuper(Refractor); function Refractor(geometry) { var _this; var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classCallCheck(this, Refractor); _this = _super.call(this, geometry); _this.type = 'Refractor'; var scope = _assertThisInitialized(_this); var color = options.color !== undefined ? new _three.Color(options.color) : new _three.Color(0x7F7F7F); var textureWidth = options.textureWidth || 512; var textureHeight = options.textureHeight || 512; var clipBias = options.clipBias || 0; var shader = options.shader || Refractor.RefractorShader; // var virtualCamera = new _three.PerspectiveCamera(); virtualCamera.matrixAutoUpdate = false; virtualCamera.userData.refractor = true; // var refractorPlane = new _three.Plane(); var textureMatrix = new _three.Matrix4(); // render target var parameters = { minFilter: _three.LinearFilter, magFilter: _three.LinearFilter, format: _three.RGBFormat }; var renderTarget = new _three.WebGLRenderTarget(textureWidth, textureHeight, parameters); if (!_three.MathUtils.isPowerOfTwo(textureWidth) || !_three.MathUtils.isPowerOfTwo(textureHeight)) { renderTarget.texture.generateMipmaps = false; } // material _this.material = new _three.ShaderMaterial({ uniforms: _three.UniformsUtils.clone(shader.uniforms), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, transparent: true // ensures, refractors are drawn from farthest to closest }); _this.material.uniforms['color'].value = color; _this.material.uniforms['tDiffuse'].value = renderTarget.texture; _this.material.uniforms['textureMatrix'].value = textureMatrix; // functions var visible = function () { var refractorWorldPosition = new _three.Vector3(); var cameraWorldPosition = new _three.Vector3(); var rotationMatrix = new _three.Matrix4(); var view = new _three.Vector3(); var normal = new _three.Vector3(); return function visible(camera) { refractorWorldPosition.setFromMatrixPosition(scope.matrixWorld); cameraWorldPosition.setFromMatrixPosition(camera.matrixWorld); view.subVectors(refractorWorldPosition, cameraWorldPosition); rotationMatrix.extractRotation(scope.matrixWorld); normal.set(0, 0, 1); normal.applyMatrix4(rotationMatrix); return view.dot(normal) < 0; }; }(); var updateRefractorPlane = function () { var normal = new _three.Vector3(); var position = new _three.Vector3(); var quaternion = new _three.Quaternion(); var scale = new _three.Vector3(); return function updateRefractorPlane() { scope.matrixWorld.decompose(position, quaternion, scale); normal.set(0, 0, 1).applyQuaternion(quaternion).normalize(); // flip the normal because we want to cull everything above the plane normal.negate(); refractorPlane.setFromNormalAndCoplanarPoint(normal, position); }; }(); var updateVirtualCamera = function () { var clipPlane = new _three.Plane(); var clipVector = new _three.Vector4(); var q = new _three.Vector4(); return function updateVirtualCamera(camera) { virtualCamera.matrixWorld.copy(camera.matrixWorld); virtualCamera.matrixWorldInverse.copy(virtualCamera.matrixWorld).invert(); virtualCamera.projectionMatrix.copy(camera.projectionMatrix); virtualCamera.far = camera.far; // used in WebGLBackground // The following code creates an oblique view frustum for clipping. // see: Lengyel, Eric. “Oblique View Frustum Depth Projection and Clipping”. // Journal of Game Development, Vol. 1, No. 2 (2005), Charles River Media, pp. 5–16 clipPlane.copy(refractorPlane); clipPlane.applyMatrix4(virtualCamera.matrixWorldInverse); clipVector.set(clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.constant); // calculate the clip-space corner point opposite the clipping plane and // transform it into camera space by multiplying it by the inverse of the projection matrix var projectionMatrix = virtualCamera.projectionMatrix; q.x = (Math.sign(clipVector.x) + projectionMatrix.elements[8]) / projectionMatrix.elements[0]; q.y = (Math.sign(clipVector.y) + projectionMatrix.elements[9]) / projectionMatrix.elements[5]; q.z = -1.0; q.w = (1.0 + projectionMatrix.elements[10]) / projectionMatrix.elements[14]; // calculate the scaled plane vector clipVector.multiplyScalar(2.0 / clipVector.dot(q)); // replacing the third row of the projection matrix projectionMatrix.elements[2] = clipVector.x; projectionMatrix.elements[6] = clipVector.y; projectionMatrix.elements[10] = clipVector.z + 1.0 - clipBias; projectionMatrix.elements[14] = clipVector.w; }; }(); // This will update the texture matrix that is used for projective texture mapping in the shader. // see: http://developer.download.nvidia.com/assets/gamedev/docs/projective_texture_mapping.pdf function updateTextureMatrix(camera) { // this matrix does range mapping to [ 0, 1 ] textureMatrix.set(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0); // we use "Object Linear Texgen", so we need to multiply the texture matrix T // (matrix above) with the projection and view matrix of the virtual camera // and the model matrix of the refractor textureMatrix.multiply(camera.projectionMatrix); textureMatrix.multiply(camera.matrixWorldInverse); textureMatrix.multiply(scope.matrixWorld); } // function render(renderer, scene, camera) { scope.visible = false; var currentRenderTarget = renderer.getRenderTarget(); var currentXrEnabled = renderer.xr.enabled; var currentShadowAutoUpdate = renderer.shadowMap.autoUpdate; renderer.xr.enabled = false; // avoid camera modification renderer.shadowMap.autoUpdate = false; // avoid re-computing shadows renderer.setRenderTarget(renderTarget); if (renderer.autoClear === false) renderer.clear(); renderer.render(scene, virtualCamera); renderer.xr.enabled = currentXrEnabled; renderer.shadowMap.autoUpdate = currentShadowAutoUpdate; renderer.setRenderTarget(currentRenderTarget); // restore viewport var viewport = camera.viewport; if (viewport !== undefined) { renderer.state.viewport(viewport); } scope.visible = true; } // _this.onBeforeRender = function (renderer, scene, camera) { // Render renderTarget.texture.encoding = renderer.outputEncoding; // ensure refractors are rendered only once per frame if (camera.userData.refractor === true) return; // avoid rendering when the refractor is viewed from behind if (!visible(camera) === true) return; // update updateRefractorPlane(); updateTextureMatrix(camera); updateVirtualCamera(camera); render(renderer, scene, camera); }; _this.getRenderTarget = function () { return renderTarget; }; return _this; } return _createClass(Refractor); }(_three.Mesh); _exports.Refractor = Refractor; Refractor.prototype.isRefractor = true; Refractor.RefractorShader = { uniforms: { 'color': { value: null }, 'tDiffuse': { value: null }, 'textureMatrix': { value: null } }, vertexShader: /* glsl */ "\n\n\t\tuniform mat4 textureMatrix;\n\n\t\tvarying vec4 vUv;\n\n\t\tvoid main() {\n\n\t\t\tvUv = textureMatrix * vec4( position, 1.0 );\n\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\n\t\t}", fragmentShader: /* glsl */ "\n\n\t\tuniform vec3 color;\n\t\tuniform sampler2D tDiffuse;\n\n\t\tvarying vec4 vUv;\n\n\t\tfloat blendOverlay( float base, float blend ) {\n\n\t\t\treturn( base < 0.5 ? ( 2.0 * base * blend ) : ( 1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );\n\n\t\t}\n\n\t\tvec3 blendOverlay( vec3 base, vec3 blend ) {\n\n\t\t\treturn vec3( blendOverlay( base.r, blend.r ), blendOverlay( base.g, blend.g ), blendOverlay( base.b, blend.b ) );\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\tvec4 base = texture2DProj( tDiffuse, vUv );\n\t\t\tgl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 );\n\n\t\t}" }; });