使用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();
&#13;
<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>
&#13;
我为要应用的每个后处理着色器创建一个单独的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时,为什么方块太透明了?
如何将两个图层混合在一起,使它们看起来与混合前相同?
答案 0 :(得分:1)
将Shader Pass blendSrc更改为ONE可解决问题。 e.g。
sp.material.blending = THREE.CustomBlending;
sp.material.blendSrc = THREE.OneFactor;
我相信这是有效的,因为径向模糊着色器的功能尤其如此。首先,渲染通道颜色缓冲区被清除为黑色透明颜色,并在其内部绘制不透明的正方形。然后,径向模糊着色器使用其周围的透明像素模糊这些不透明像素。这具有预乘任何非透明像素的alpha的效果。
此时将纹理绘制到缓冲区并不需要将源像素再次乘以alpha通道。这是正确的解释吗?