【问题标题】:How to add fading reflection with THREE.js?如何使用 THREE.js 添加褪色反射?
【发布时间】:2020-10-13 22:46:20
【问题描述】:

我使用 THREE.js 创建了这本旋转书。不幸的是,我对 OpenGL 知之甚少,所以我不知道如何制作 Fading 反射。

window.onload = function() {
  // Create the renderer and add it to the page's body element
  var renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.shadowMap.enabled = true;

  document.body.appendChild(renderer.domElement);

  // Create the scene to hold the object
  var scene = new THREE.Scene();

  // Create the camera
  var camera = new THREE.PerspectiveCamera(
    35, // Field of view
    window.innerWidth / window.innerHeight, // Aspect ratio
    0.1, // Near plane distance
    1000 // Far plane distance
  );

  // Position the camera
  camera.position.set(-15, 10, 20);
  camera.lookAt(scene.position);

  // Add the lights

  var light = new THREE.PointLight(0xffffff, 0.4);
  light.position.set(10, 10, 10);
  scene.add(light);

  ambientLight = new THREE.AmbientLight(0xbbbbbb);
  scene.add(ambientLight);

  // Load the textures (book images)
  var textureLoader = new THREE.TextureLoader();
  //front
  var bookCoverTexture = textureLoader.load("https://i.imgur.com/sBzG9bm.jpeg");
  //side
  var bookSpineTexture = textureLoader.load("https://i.imgur.com/ZFgZgnx.png");
  //back
  var bookBackTexture = textureLoader.load("https://i.imgur.com/s4Kbbr9.jpeg");
  //pages
  var bookPagesTexture = textureLoader.load("https://i.imgur.com/2Mul6cU.jpeg");
  //top + bottom pages
  var bookPagesTopBottomTexture = textureLoader.load(
    "https://i.imgur.com/OXCKChN.jpg"
  );

  // Use the linear filter for the textures to avoid blurriness
  bookCoverTexture.minFilter = bookSpineTexture.minFilter = bookBackTexture.minFilter = bookPagesTexture.minFilter = bookPagesTopBottomTexture.minFilter =
    THREE.LinearFilter;

  // Create the materials

  var bookCover = new THREE.MeshLambertMaterial({
    color: 0xffffff,
    map: bookCoverTexture
  });
  var bookSpine = new THREE.MeshLambertMaterial({
    color: 0xffffff,
    map: bookSpineTexture
  });
  var bookBack = new THREE.MeshLambertMaterial({
    color: 0xffffff,
    map: bookBackTexture
  });
  var bookPages = new THREE.MeshLambertMaterial({
    color: 0xffffff,
    map: bookPagesTexture
  });
  var bookPagesTopBottom = new THREE.MeshLambertMaterial({
    color: 0xffffff,
    map: bookPagesTopBottomTexture
  });

  var materials = [
    bookPages, // Right side
    bookSpine, // Left side
    bookPagesTopBottom, // Top side
    bookPagesTopBottom, // Bottom side
    bookCover, // Front side
    bookBack // Back side
  ];

  // Create the book and add it to the scene
  var book = new THREE.Mesh(
    new THREE.BoxGeometry(7, 10, 2.2, 4, 4, 1),
    materials
  );
  book.layers.enable(1);
  scene.add(book);

  var geometry = new THREE.PlaneBufferGeometry(20, 20);

  var mirror = new THREE.Reflector(geometry, {
    clipBias: 0.003,
    textureWidth: 1024 * window.devicePixelRatio,
    textureHeight: 1024 * window.devicePixelRatio,
    color: 0x889999,
    recursion: 1
  });
  mirror.rotateX(-Math.PI / 2);
  mirror.position.y = -5.2;
  mirror.material.transparent = true;
  mirror.material.alpha = 0.1;

  scene.add(mirror);

  // Create the orbit controls for the camera
  controls = new THREE.OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;
  controls.dampingFactor = 0.25;
  controls.enablePan = false;
  controls.enableZoom = false;

  // Begin the animation
  animate();

  /*
    Animate a frame
  */

  function animate() {
    book.rotation.y += 0.01;

    // Update the orbit controls
    controls.update();

    // Render the frame
    renderer.render(scene, camera);

    // Keep the animation going
    requestAnimationFrame(animate);
  }
};
    <script src="https://unpkg.com/three@0.111.0/build/three.js"></script>
    <script src="https://unpkg.com/three@0.111.0/examples/js/controls/OrbitControls.js"></script>
    <script src="https://unpkg.com/three@0.111.0/examples/js/objects/Reflector.js"></script>
    
<div style="padding: 20px; position: absolute;">
</div>

经过一些研究,我确实遇到了这篇关于创建褪色反射的帖子,但我自己无法做到。

https://discourse.threejs.org/t/creating-a-fading-reflection/3831

不胜感激。

【问题讨论】:

    标签: javascript three.js 3d


    【解决方案1】:

    我已使用 three.js 论坛中的 THREE.Reflector 的修改版本更新了您的代码。

    window.onload = function() {
      // Create the renderer and add it to the page's body element
      var renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
      renderer.setPixelRatio(window.devicePixelRatio);
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.shadowMap.enabled = true;
    
      document.body.appendChild(renderer.domElement);
    
      // Create the scene to hold the object
      var scene = new THREE.Scene();
    
      // Create the camera
      var camera = new THREE.PerspectiveCamera(
        35, // Field of view
        window.innerWidth / window.innerHeight, // Aspect ratio
        0.1, // Near plane distance
        1000 // Far plane distance
      );
    
      // Position the camera
      camera.position.set(-15, 10, 20);
      camera.lookAt(scene.position);
    
      // Add the lights
    
      var light = new THREE.PointLight(0xffffff, 0.4);
      light.position.set(10, 10, 10);
      scene.add(light);
    
      ambientLight = new THREE.AmbientLight(0xbbbbbb);
      scene.add(ambientLight);
    
      // Load the textures (book images)
      var textureLoader = new THREE.TextureLoader();
      //front
      var bookCoverTexture = textureLoader.load("https://i.imgur.com/sBzG9bm.jpeg");
      //side
      var bookSpineTexture = textureLoader.load("https://i.imgur.com/ZFgZgnx.png");
      //back
      var bookBackTexture = textureLoader.load("https://i.imgur.com/s4Kbbr9.jpeg");
      //pages
      var bookPagesTexture = textureLoader.load("https://i.imgur.com/2Mul6cU.jpeg");
      //top + bottom pages
      var bookPagesTopBottomTexture = textureLoader.load(
        "https://i.imgur.com/OXCKChN.jpg"
      );
    
      // Use the linear filter for the textures to avoid blurriness
      bookCoverTexture.minFilter = bookSpineTexture.minFilter = bookBackTexture.minFilter = bookPagesTexture.minFilter = bookPagesTopBottomTexture.minFilter =
        THREE.LinearFilter;
    
      // Create the materials
    
      var bookCover = new THREE.MeshLambertMaterial({
        color: 0xffffff,
        map: bookCoverTexture
      });
      var bookSpine = new THREE.MeshLambertMaterial({
        color: 0xffffff,
        map: bookSpineTexture
      });
      var bookBack = new THREE.MeshLambertMaterial({
        color: 0xffffff,
        map: bookBackTexture
      });
      var bookPages = new THREE.MeshLambertMaterial({
        color: 0xffffff,
        map: bookPagesTexture
      });
      var bookPagesTopBottom = new THREE.MeshLambertMaterial({
        color: 0xffffff,
        map: bookPagesTopBottomTexture
      });
    
      var materials = [
        bookPages, // Right side
        bookSpine, // Left side
        bookPagesTopBottom, // Top side
        bookPagesTopBottom, // Bottom side
        bookCover, // Front side
        bookBack // Back side
      ];
    
      // Create the book and add it to the scene
      var book = new THREE.Mesh(
        new THREE.BoxGeometry(7, 10, 2.2, 4, 4, 1),
        materials
      );
      book.layers.enable(1);
      scene.add(book);
    
      var geometry = new THREE.PlaneBufferGeometry(20, 20);
    
      var mirror = new THREE.Reflector(geometry, {
        clipBias: 0.003,
        textureWidth: 1024 * window.devicePixelRatio,
        textureHeight: 1024 * window.devicePixelRatio,
        color: 0x889999,
        recursion: 1
      });
      mirror.rotateX(-Math.PI / 2);
      mirror.position.y = -5.2;
      mirror.material.transparent = true;
      mirror.material.alpha = 0.1;
    
      scene.add(mirror);
    
      // Create the orbit controls for the camera
      controls = new THREE.OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;
      controls.dampingFactor = 0.25;
      controls.enablePan = false;
      controls.enableZoom = false;
    
      // Begin the animation
      animate();
    
      /*
        Animate a frame
      */
    
      function animate() {
        book.rotation.y += 0.01;
    
        // Update the orbit controls
        controls.update();
    
        // Render the frame
        renderer.render(scene, camera);
    
        // Keep the animation going
        requestAnimationFrame(animate);
      }
    };
    <script src="https://unpkg.com/three@0.116.1/build/three.js"></script>
    <script src="https://unpkg.com/three@0.116.1/examples/js/controls/OrbitControls.js"></script>
    
    <script>
    THREE.Reflector = function ( geometry, options ) {
    
        THREE.Mesh.call( this, geometry );
    
        this.type = 'Reflector';
    
        var scope = this;
    
        options = options || {};
    
        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 || THREE.Reflector.ReflectorShader;
    
        //
    
        var reflectorPlane = new THREE.Plane();
        var normal = new THREE.Vector3();
        var reflectorWorldPosition = new THREE.Vector3();
        var cameraWorldPosition = new THREE.Vector3();
        var rotationMatrix = new THREE.Matrix4();
        var lookAtPosition = new THREE.Vector3( 0, 0, - 1 );
        var clipPlane = new THREE.Vector4();
        var viewport = new THREE.Vector4();
    
        var view = new THREE.Vector3();
        var target = new THREE.Vector3();
        var q = new THREE.Vector4();
    
        var textureMatrix = new THREE.Matrix4();
        var virtualCamera = new THREE.PerspectiveCamera();
    
        var parameters = {
            minFilter: THREE.LinearFilter,
            magFilter: THREE.LinearFilter,
            format: THREE.RGBFormat,
            stencilBuffer: false
        };
    
        var renderTarget = new THREE.WebGLRenderTarget( textureWidth, textureHeight, parameters );
        
        renderTarget.depthBuffer = true;
        renderTarget.depthTexture = new THREE.DepthTexture();
        renderTarget.depthTexture.type = THREE.UnsignedShortType;
    
        if ( ! THREE.Math.isPowerOfTwo( textureWidth ) || ! THREE.Math.isPowerOfTwo( textureHeight ) ) {
    
            renderTarget.texture.generateMipmaps = false;
    
        }
    
        var material = new THREE.ShaderMaterial( {
            uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
            fragmentShader: shader.fragmentShader,
            vertexShader: shader.vertexShader,
            transparent: true
        } );
    
        material.uniforms.tDiffuse.value = renderTarget.texture;
        material.uniforms.tDepth.value = renderTarget.depthTexture;
        material.uniforms.color.value = color;
        material.uniforms.textureMatrix.value = textureMatrix;
    
        this.material = material;
    
        this.onBeforeRender = function ( renderer, scene, camera ) {
    
            reflectorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
            cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
    
            rotationMatrix.extractRotation( scope.matrixWorld );
    
            normal.set( 0, 0, 1 );
            normal.applyMatrix4( rotationMatrix );
    
            view.subVectors( reflectorWorldPosition, cameraWorldPosition );
    
            // Avoid rendering when reflector is facing away
    
            if ( view.dot( normal ) > 0 ) return;
    
            view.reflect( normal ).negate();
            view.add( reflectorWorldPosition );
    
            rotationMatrix.extractRotation( camera.matrixWorld );
    
            lookAtPosition.set( 0, 0, - 1 );
            lookAtPosition.applyMatrix4( rotationMatrix );
            lookAtPosition.add( cameraWorldPosition );
    
            target.subVectors( reflectorWorldPosition, lookAtPosition );
            target.reflect( normal ).negate();
            target.add( reflectorWorldPosition );
    
            virtualCamera.position.copy( view );
            virtualCamera.up.set( 0, 1, 0 );
            virtualCamera.up.applyMatrix4( rotationMatrix );
            virtualCamera.up.reflect( normal );
            virtualCamera.lookAt( target );
    
            virtualCamera.far = camera.far; // Used in WebGLBackground
    
            virtualCamera.updateMatrixWorld();
            virtualCamera.projectionMatrix.copy( camera.projectionMatrix );
    
            this.material.uniforms.cameraNear.value = camera.near;
            this.material.uniforms.cameraFar.value = camera.far;
    
            // Update the texture matrix
            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
            );
            textureMatrix.multiply( virtualCamera.projectionMatrix );
            textureMatrix.multiply( virtualCamera.matrixWorldInverse );
            textureMatrix.multiply( scope.matrixWorld );
    
            // Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html
            // Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
            reflectorPlane.setFromNormalAndCoplanarPoint( normal, reflectorWorldPosition );
            reflectorPlane.applyMatrix4( virtualCamera.matrixWorldInverse );
    
            clipPlane.set( reflectorPlane.normal.x, reflectorPlane.normal.y, reflectorPlane.normal.z, reflectorPlane.constant );
    
            var projectionMatrix = virtualCamera.projectionMatrix;
    
            q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
            q.y = ( Math.sign( clipPlane.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
            clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) );
    
            // Replacing the third row of the projection matrix
            projectionMatrix.elements[ 2 ] = clipPlane.x;
            projectionMatrix.elements[ 6 ] = clipPlane.y;
            projectionMatrix.elements[ 10 ] = clipPlane.z + 1.0 - clipBias;
            projectionMatrix.elements[ 14 ] = clipPlane.w;
    
            // Render
    
            renderTarget.texture.encoding = renderer.outputEncoding;
    
            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 );
    
            renderer.state.buffers.depth.setMask( true ); // make sure the depth buffer is writable so it can be properly cleared, see #18897
    
            if ( renderer.autoClear === false ) renderer.clear();
            renderer.render( scene, virtualCamera );
    
            renderer.xr.enabled = currentXrEnabled;
            renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
    
            renderer.setRenderTarget( currentRenderTarget );
            // Restore viewport
    
            var bounds = camera.bounds;
    
            if ( bounds !== undefined ) {
    
                var size = renderer.getSize();
                var pixelRatio = renderer.getPixelRatio();
    
                viewport.x = bounds.x * size.width * pixelRatio;
                viewport.y = bounds.y * size.height * pixelRatio;
                viewport.z = bounds.z * size.width * pixelRatio;
                viewport.w = bounds.w * size.height * pixelRatio;
    
                renderer.state.viewport( viewport );
    
            }
    
            scope.visible = true;
    
        };
    
        this.getRenderTarget = function () {
    
            return renderTarget;
    
        };
    
    };
    
    THREE.Reflector.prototype = Object.create( THREE.Mesh.prototype );
    THREE.Reflector.prototype.constructor = THREE.Reflector;
    
    THREE.Reflector.ReflectorShader = {
    
        uniforms: {
    
            'color': {
                type: 'c',
                value: null
            },
    
            'tDiffuse': {
                type: 't',
                value: null
            },
            
            'tDepth': {
                type: 't',
                value: null
            },
    
            'textureMatrix': {
                type: 'm4',
                value: null
            },
            
            'cameraNear': {
                type: 'f',
                value: 0
            },
            
            'cameraFar': {
                type: 'f',
                value: 0
            },
    
        },
    
        vertexShader: [
            'uniform mat4 textureMatrix;',
            'varying vec4 vUv;',
    
            'void main() {',
    
            '   vUv = textureMatrix * vec4( position, 1.0 );',
    
            '   gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
    
            '}'
        ].join( '\n' ),
    
        fragmentShader: [
            '#include <packing>',
            'uniform vec3 color;',
            'uniform sampler2D tDiffuse;',
            'uniform sampler2D tDepth;',
            'uniform float cameraNear;',
            'uniform float cameraFar;',
            'varying vec4 vUv;',
    
            'float blendOverlay( float base, float blend ) {',
    
            '   return( base < 0.5 ? ( 2.0 * base * blend ) : ( 1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );',
    
            '}',
    
            'vec3 blendOverlay( vec3 base, vec3 blend ) {',
    
            '   return vec3( blendOverlay( base.r, blend.r ), blendOverlay( base.g, blend.g ), blendOverlay( base.b, blend.b ) );',
    
            '}',
            
            'float readDepth( sampler2D depthSampler, vec4 coord ) {',
                    
            '   float fragCoordZ = texture2DProj( depthSampler, coord ).x;',
            '   float viewZ = perspectiveDepthToViewZ( fragCoordZ, cameraNear, cameraFar );',
            '   return viewZToOrthographicDepth( viewZ, cameraNear, cameraFar );',
                
            '}',
    
            'void main() {',
    
            '   vec4 base = texture2DProj( tDiffuse, vUv );',
            ' float depth = readDepth( tDepth, vUv );',
            '   gl_FragColor = vec4( blendOverlay( base.rgb, color ), 1.0 - ( depth * 50000.0 ) );',
    
            '}'
        ].join( '\n' )
    };
    </script>
        
    <div style="padding: 20px; position: absolute;">
    </div>

    【讨论】:

    • 它运行完美,谢谢!。我看到您修改了股票反射器以添加对褪色的支持。您是否认为还有其他方法可以在不重写整个内容的情况下做到这一点?仅据我所知(尽管代码对我的用例很有用)。
    • 不,Reflector 需要深度纹理才能达到此效果。 Reflector 的默认版本不支持此功能(以避免相应的开销,因为大多数用例不需要淡入淡出)。
    • 非常感谢。您能否解释一下如何只为反射器而不是整个场景添加模糊。这就是我苦苦挣扎的地方。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多