ThreeJS Dual Depth Peel OIT 注意点

992 阅读3分钟

image.png

  • OIT需要对模型进行分类
    • 透明mesh transparentObjects
    • 非透明mesh opaqueObjects
  • 多层材质的透明与非透明
    • 有一层为不透明,则为非透明mesh
    • 全是透明,则为透明mesh
  • 透明mesh与非透明mesh区别不大
    • 非透明物体很多属性都不会去动,而透明物体很多属性都会去动
    • 非透明物体和透明物体transparent在oit中都是为false (即不blend)
    • 所有物体的premultipliedAlpha都应该为false
    • 透明mesh设置了opacity属性(rgba的a)
    • 多层材质的时候,最后一层不blend,其他层的透明blend
    • 在compositeScene,剥离后的贴图,a通道会预乘颜色,blend功能也被打开
  • 渲染管线
    • 没有透明: three画家排序
      • ThreeJS自带的
    • 有透明: oit管线
      • 把不透明的进行隐藏 然后SSAO后处理
      • 进行oit处理 这个时候透明和非透明的都在这里drawcall
      • drawcall完毕 最后drawcall背景
      • renderTarget 设为null 背景和地板设为隐藏
      • 其他后处理
  • 关于visible
    • 虽然所有都被分类为透明/非透明的mesh,但我个人觉得visible为false就不要加在oit里面
  • 射线与高亮的问题
  • 透明物体很多物体是否需要复原物体材质的属性,因为设置了很多属性
  • 新物体需要归进透明类/非透明类

  • 全局shader的声明与设置 作用于深度材质和每一个Mesh中
    • 变量意义
      • uLayer 作为depthOnbeforeCompile是否启用的开关
      • uPrevDepthTexture 用来深度对比与剥离
      • uScreenSize 搭配gl_FragCoord.xy用来生成UV的
    • 整体意义
      • 深度剥离,就是根据深度来discard掉较近的
var globalPeelUniforms = {
    uLayer: { value: 0 },
    uPrevDepthTexture: { value: null },
    uScreenSize: { value: new THREE.Vector2(1, 1) },
};
function depthOnbeforeCompile(shader) {
    shader.uniforms.uScreenSize = globalPeelUniforms.uScreenSize;
    shader.uniforms.uPrevDepthTexture =
            globalPeelUniforms.uPrevDepthTexture;
    shader.uniforms.uLayer = globalPeelUniforms.uLayer;

    shader.fragmentShader = `
            uniform vec2 uScreenSize;
            uniform sampler2D uPrevDepthTexture;
            uniform int uLayer;
            ${shader.fragmentShader}
    `;
    //peel depth
    shader.fragmentShader = shader.fragmentShader.replace(
            /}$/gm,
            `
            if(uLayer != 0 ){
                vec2 screenPos = gl_FragCoord.xy * uScreenSize; //uv
                float prevDepth = unpackRGBAToDepth(texture2D(uPrevDepthTexture,screenPos)); //获取贴图深度
                if(prevDepth  - gl_FragCoord.z >= 0. ){ //只保留后面部分 
                        discard; 
                }
            }
        }
            `
    );
}
depthMaterial = new THREE.MeshDepthMaterial();
depthMaterial.side = getSide();
depthMaterial.depthPacking = THREE.RGBADepthPacking;
depthMaterial.onBeforeCompile = depthOnbeforeCompile;

  • 两个Scene
    • scene 一个scene用来存储所有物体的
    • compositeScene 一个scene用来展示剥离后的rendertarget贴图的

  • compositeScene的展示结果平面 image.png image.png
  • 左边是贴图,右边是乘以a之后的结果
  • 需要乘个a 不然颜色会完全一样
var compositeScene = new THREE.Scene();
compositeMaterial = new THREE.ShaderMaterial({
  uniforms: {
    uTextureA: { value: null },
    uBlit: { value: 0 },
    uStencil: { value: 0 }
  },
  vertexShader: `
    varying vec2 vUv;
    void main(){
        vUv = uv;
        gl_Position = vec4(position.xy,0.,1.); //全屏
    }
                                `,
  fragmentShader: `
    varying vec2 vUv;
    uniform sampler2D uTextureA;
    uniform int uBlit;
    uniform int uStencil;
    void main(){
        vec4 src = texture2D(uTextureA,vUv);
        if(uBlit == 0 )
        {
            gl_FragColor = src;
        }
        else {
            gl_FragColor = src;
            gl_FragColor.xyz *= gl_FragColor.a;
        }
    }
                                `,
  transparent: true, //透明打开
  depthTest: false, //深度测试
  depthWrite: false, //深度写入
  blending: THREE.CustomBlending,
  blendEquation: THREE.AddEquation,
  blendSrc: THREE.OneMinusDstAlphaFactor, //融合方式 第一个为source
  blendDst: THREE.OneFactor,
  blendDstAlpha: null,
  blendSrcAlpha: null
});

const planeGeometry = new THREE.PlaneBufferGeometry(2, 2, 1, 1);
const compositePlane = new THREE.Mesh(planeGeometry, compositeMaterial);
compositePlane.frustumCulled = false;
compositeScene.add(compositePlane);

  • 三个缓冲区即可满足Dual Depth peel
//#region  renderTarget
renderer.setRenderTarget();
renderer.setClearColor(0x888888, 1);
renderer.clear();

renderer.setRenderTarget(targets[2]);
renderer.setClearColor(0, 0);
renderer.clear();

renderer.setRenderTarget(targets[1]);
renderer.setClearColor(0xffffff, 1);
renderer.clear();

renderer.setRenderTarget(targets[0]);
renderer.setClearColor(0, 0);
renderer.clear();
//#endregion

  • 初始化三个缓冲区
//透明和非透明都存在的场景 targets[0]
renderer.render(scene, camera, targets[0]);
//只有透明的场景 且targets[1]是所有透明物体的深度图
opaqueObjects.forEach((o) => (o.visible = false));
transparentObjects.forEach((o) => (o.visible = true));
scene.overrideMaterial = depthMaterial;
renderer.render(scene, camera, targets[1], false);
scene.overrideMaterial = null;
//存在透明与非透明的targets[0] compositeScene展示targets[0]贴图  然后生成targets[2]
renderer.setRenderTarget(targets[0]);
renderer.render(scene, camera, targets[0], false);

compositeMaterial.blendSrc = THREE.OneMinusDstAlphaFactor; //第一张 为source
compositeMaterial.blendDst = THREE.OneFactor;
compositeMaterial.uniforms.uTextureA.value = targets[0];
compositeMaterial.uniforms.uBlit.value = 1;
renderer.render(compositeScene, camera, targets[2]);

  • 不断剥离的过程
  • 一般layers是4
  • 由于renderer没在targets[2]做clear,所有过去的图像在叠加
  • renderTarget:1,切换缓冲区;2,获取截图(在bindFramework为null的时候,生成texture)
for (let i = 0; i < options.layers; i++) {
  // flip和flop就是0/1  flip最初始1 然后是0
  const flip = i % 2; 
  const flop = (i + 1) % 2;

  globalPeelUniforms.uPrevDepthTexture.value = targets[flop]; //最初是0 有所有物体 应该没有深度图

  renderer.setRenderTarget(targets[flip]); //最初是1 只有透明物体 
  renderer.setClearColor(0, 0); //背景是透明
  renderer.clear(true, true, false); //清空深度和颜色
  renderer.render(scene, camera, targets[flip], false); //最初应该是

  compositeMaterial.uniforms.uTextureA.value = targets[flip]; //叠加
  renderer.render(compositeScene, camera, targets[2]);

  renderer.setRenderTarget(targets[flip]);
  renderer.setClearColor(0xffffff, 1); //不写这个 会让场景变亮一些
  renderer.clear(true, true, false);

  scene.overrideMaterial = depthMaterial;
  renderer.render(scene, camera, targets[flip], false);
  scene.overrideMaterial = null;
}

  • 跑完深度剥离的shader
  • 透明物体隐藏,如果不隐藏颜色会加深
globalPeelUniforms.uLayer.value = 0; //不跑那个shader了
transparentObjects.forEach((o) => (o.visible = false));  //否则会出现多个 导致颜色又变了
renderer.render(scene, camera);

compositeMaterial.uniforms.uBlit.value = 0;
compositeMaterial.blendSrc = THREE.OneFactor;  //
compositeMaterial.blendDst = THREE.OneMinusSrcAlphaFactor; //最后一张是作为Destination了
compositeMaterial.blendSrcAlpha = null;
compositeMaterial.blendDstAlpha = null;
compositeMaterial.uniforms.uTextureA.value = targets[2];

renderer.render(compositeScene, camera);