三个带有透明度的js后处理解决方案

Three js postProcessing solution with transparency

我发现了使用 three.js 创建后处理效果的解决方案:
https://medium.com/@luruke/simple-postprocessing-in-three-js-91936ecadfb7 (由 Luigi De Rosa 制作)

这是一个很好的方法。不幸的是,我无法在最终渲染器中添加透明度。我应该在我的后处理片段着色器中添加一个透明组件吗?

const fragmentShader = `precision highp float;
    uniform sampler2D uScene;
    uniform vec2 uResolution;
    uniform float uTime;

    void main() {
        vec2 uv = gl_FragCoord.xy / uResolution.xy;
        vec3 color = vec3(uv, 1.0);

        //simple distortion effect
        uv.y += sin(uv.x*30.0+uTime*10.0)/40.0;
        uv.x -= sin(uv.y*10.0-uTime)/40.0;

        color = texture2D(uScene, uv).rgb;

        gl_FragColor = vec4(color, 1.0);

    }
`;

谢谢

编辑 1:

我将属性 transparent:true 添加到 RawShaderMaterial

我将新 THREE.WebGLRenderTarget 的格式更改为 THREE.RGBAFormat 而不是 THREE.RGBFormat

我还在片段着色器的末尾添加了这些行:

gl_FragColor = vec4(color, 1.0);
vec4 tex = texture2D( uScene, uv );
if(tex.a < 0.0) {
    gl_FragColor.a = 1.0;
}

可是我还是没看透我的canvas

编辑 2:

这是后处理的片段 class

    let renderer, camera, scene, W = window.innerWidth, H = window.innerHeight, geometry, material, mesh;

    initWebgl();
    function initWebgl(){

        renderer = new THREE.WebGLRenderer( { alpha: true, antialias: true } );
        renderer.setPixelRatio( window.devicePixelRatio );
        renderer.setSize( W, H );
        document.querySelector('.innerCanvas').appendChild( renderer.domElement );

        camera = new THREE.OrthographicCamera(-W/H/2, W/H/2, 1/2, -1/2, -0.1, 0.1);
        scene = new THREE.Scene();
       
        geometry = new THREE.PlaneBufferGeometry(0.5, 0.5);
        material = new THREE.MeshNormalMaterial();
        
        mesh = new THREE.Mesh( geometry , material );
        scene.add(mesh);

      
    } 

    function rafP(){
        requestAnimationFrame(rafP);

        // renderer.render(scene, camera);
        post.render(scene, camera);

    }
    
    
    const vertexShader = `precision highp float;
        attribute vec2 position;
        void main() {
          // Look ma! no projection matrix multiplication,
          // because we pass the values directly in clip space coordinates.
          gl_Position = vec4(position, 1.0, 1.0);
        }`;

    const fragmentShader = `precision highp float;
        uniform sampler2D uScene;
        uniform vec2 uResolution;
        uniform float uTime;

        void main() {
            vec2 uv = gl_FragCoord.xy / uResolution.xy;
            vec3 color = vec3(uv, 1.0);

            uv.y += sin(uv.x*20.0)/10.0;

            color = texture2D(uScene, uv).rgb;
            
            gl_FragColor = vec4(color, 1.0);

            vec4 tex = texture2D( uScene, uv );
            // if(tex.a - percent < 0.0) {
            if(tex.a < 0.0) {
                gl_FragColor.a = 1.0;
                //or without transparent = true use
                // discard; 
            }
        
        }`;

    //PostProcessing
    class PostFX {
        constructor(renderer) {
            this.renderer = renderer;
            this.scene = new THREE.Scene();
            // three.js for .render() wants a camera, even if we're not using it :(
            this.dummyCamera = new THREE.OrthographicCamera();
            this.geometry = new THREE.BufferGeometry();

            // Triangle expressed in clip space coordinates
            const vertices = new Float32Array([
              -1.0, -1.0,
              3.0, -1.0,
              -1.0, 3.0
            ]);

            this.geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 2));

            this.resolution = new THREE.Vector2();
            this.renderer.getDrawingBufferSize(this.resolution);

            this.target = new THREE.WebGLRenderTarget(this.resolution.x, this.resolution.y, {
                format: THREE.RGBAFormat,  //THREE.RGBFormat
                stencilBuffer: false,
                depthBuffer: true
            });

            this.material = new THREE.RawShaderMaterial({
                fragmentShader,
                vertexShader,
                uniforms: {
                    uScene: { value: this.target.texture },
                    uResolution: { value: this.resolution }
                },
                transparent:true
            });

            // TODO: handle the resize -> update uResolution uniform and this.target.setSize()

            this.triangle = new THREE.Mesh(this.geometry, this.material);
            // Our triangle will be always on screen, so avoid frustum culling checking
            this.triangle.frustumCulled = false;
            this.scene.add(this.triangle);
        }

        render(scene, camera) {
            this.renderer.setRenderTarget(this.target);
            this.renderer.render(scene, camera);
            this.renderer.setRenderTarget(null);
            this.renderer.render(this.scene, this.dummyCamera);

            console.log(this.renderer);
        }
    }

    post = new PostFX(renderer); 
    rafP();
body{
  margin:0;
  padding:0;
  background:#00F;
 }
 .innerCanvas{
  position:fixed;
  top:0;
  left:0;
  width:100%;
  height:100%;
 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.js"></script>

<div class="innerCanvas"></div>

在 Alpha 通道上,0 表示完全透明,1 表示完全不透明。

在这种情况下,您唯一需要做的就是传递 gl_FragColor 纹理样本的结果。你甚至不需要担心它的价值。

gl_FragColor = texture2D(uScene, uv);

JSFiddle