繁体   English   中英

如何使用不透明度混合多个图层?

[英]How to blend multiple layers with opacity?

使用three.js,我想对单个场景应用单独的后处理效果,然后将所有这些场景组合成最终渲染。 为此,我使用的是three.js Effects Composer。

 const radialBlurShader = { uniforms: { "tDiffuse": { value: null }, }, vertexShader: ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform sampler2D tDiffuse; varying vec2 vUv; const float strength = 0.7; const int samples = 50; void main() { vec4 col = vec4(0); vec2 dir = vec2(0.5) - vUv; for (int i = 0; i < samples; i++) { float amount = strength * float(i) / float(samples); vec4 sample = texture2D(tDiffuse, vUv + dir * amount) + texture2D(tDiffuse, vUv - dir * amount); col += sample; } gl_FragColor = 0.5 * col / float(samples); } ` }; const renderWidth = window.innerWidth; const renderHeight = window.innerHeight; const renderer = new THREE.WebGLRenderer({ antialias: true, }); renderer.setSize(renderWidth, renderHeight); document.body.appendChild(renderer.domElement); const camera = new THREE.PerspectiveCamera(45, renderWidth / renderHeight, 0.1, 1000); camera.position.z = 10; var geometry = new THREE.PlaneGeometry(1, 1); var material = new THREE.MeshBasicMaterial({ color: 0x0000ff, transparent: true }); function makeEC(scene) { const ec = new THREE.EffectComposer(renderer); const rp = new THREE.RenderPass(scene, camera); const sp = new THREE.ShaderPass(radialBlurShader); rp.clearColor = 0xffffff; rp.clearAlpha = 0; sp.renderToScreen = true; sp.material.transparent = true; sp.material.blending = THREE.CustomBlending; ec.addPass(rp); ec.addPass(sp); return ec; } const scene1 = new THREE.Scene(); const mesh1 = new THREE.Mesh(geometry, material); mesh1.position.x = 2; scene1.add(mesh1); ec1 = makeEC(scene1); const scene2 = new THREE.Scene(); const mesh2 = new THREE.Mesh(geometry, material); mesh2.position.x = -2; scene2.add(mesh2); ec2 = makeEC(scene2); renderer.setClearColor(0xffffaa, 1); renderer.autoClear = false; renderer.clear(); ec1.render(); ec2.render(); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r82/three.js"></script> <script src="https://rawgit.com/mrdoob/three.js/r82/examples/js/shaders/CopyShader.js"></script> <script src="https://rawgit.com/mrdoob/three.js/r82/examples/js/postprocessing/EffectComposer.js"></script> <script src="https://rawgit.com/mrdoob/three.js/r82/examples/js/postprocessing/RenderPass.js"></script> <script src="https://rawgit.com/mrdoob/three.js/r82/examples/js/postprocessing/ShaderPass.js"></script> 

我为每个要应用的后处理着色器创建一个单独的Effects Composer实例。 (在这个例子中,为了简单起见,我使用相同的径向模糊着色器两次。)

function makeEC(scene) {
    const ec = new THREE.EffectComposer(renderer);
    const rp = new THREE.RenderPass(scene, camera);
    const sp = new THREE.ShaderPass(radialBlurShader);

    rp.clearColor = 0xffffff;
    rp.clearAlpha = 0;

    sp.renderToScreen = true;
    sp.material.transparent = true;

    // defaults to SRC_ALPHA, ONE_MINUS_SRC_ALPHA
    sp.material.blending = THREE.CustomBlending;

    ec.addPass(rp);
    ec.addPass(sp);
    return ec;
}

const ec1 = makeEC(scene1);
const ec2 = makeEC(scene2);

如您所见,我将渲染过程清除为透明背景。 然后着色器通道将使用典型的SRC_ALPHA,ONE_MINUS_SRC_ALPHA混合绘制到渲染缓冲区。

我的渲染代码看起来像这样

renderer.setClearColor(0xffffaa, 1);
renderer.autoClear = false;
renderer.clear();
ec1.render();
ec2.render();

但是,此过程不会正确地将这些层混合在一起

这就是我得到的

在此输入图像描述

混合前的第一次通过(正确)

在此输入图像描述

混合前的第二遍(正确)

在此输入图像描述

两个通道如所描述的那样混合(不正确)太暗

在此输入图像描述

premultipliedAlpha禁用(不正确)太透明了

当两个层混合在一起时,为什么正方形太暗?

当禁用预乘的Alpha时,为什么方块太透明了?

如何将两个图层混合在一起,使它们看起来与混合前一样?

将Shader Pass blendSrc更改为ONE可以解决问题。 例如

sp.material.blending = THREE.CustomBlending;
sp.material.blendSrc = THREE.OneFactor;

我相信这是有效的,因为径向模糊着色器的功能尤其如此。 首先,渲染通道颜色缓冲区被清除为黑色透明颜色,并在其内部绘制不透明的正方形。 然后,径向模糊着色器使用其周围的透明像素模糊这些不透明像素。 这具有预乘任何非透明像素的α的效果。

此时,将纹理绘制到缓冲区不再需要将源像素乘以Alpha通道。 这是正确的解释吗?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM