javascript - 如何混合不透明度的多层?

标签 javascript three.js

使用 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);

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

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

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

但是,这个过程并没有正确地将图层混合在一起

这是我得到的

enter image description here

混合前的第一遍(正确)

enter image description here

混合前的第二遍(正确)

enter image description here

两个 channel 都按照描述混合(不正确) 太黑了

enter image description here

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

为什么将两个图层混合在一起时方 block 太暗?

为什么禁用 premultipliedAlpha 时方 block 太透明?

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

最佳答案

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

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

我相信这是可行的,因为径向模糊着色器的功能特别重要。首先,渲染 channel 颜色缓冲区被清除为黑色透明颜色,并在其中绘制一个不透明的正方形。径向模糊着色器然后用它周围的透明像素模糊这些不透明像素。这具有预乘任何非透明像素的 alpha 的效果。

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

关于javascript - 如何混合不透明度的多层?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40649459/

相关文章:

three.js - 错误的光照贴图渲染

javascript - 在 Three.js 中动态更改对象尺寸

javascript - Jquery 是否有任何内置方法可以单独查找可验证元素?

表单中的 JavaScript 变量

javascript - 让 HTML5 数字输入旋转框像 mozilla 一样在 chrome 上始终可见?

javascript - 取消将对象加载到 THREE.js 中的场景中

javascript - THREE.js - 无法在本地加载纹理

3d - 在 child 中反转 parent 的旋转,所以 child 在世界中看起来没有旋转

javascript - 为什么要将空对象传递给 jQuery 的扩展方法?

php - 哪一端创建 session /cookie?