微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

javascript – 如何混合多层与不透明度?

使用three.js,我想对单个场景应用单独的后处理效果,然后将所有这些场景组合成最终渲染.为了做到这一点,我使用的是3.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>

我为每一个后处理着色器我想申请一个独立的影响作曲家实例. (在本例中,我使用相同的径向模糊着色两次为简单起见).

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混合来绘制到renderbuffer.

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

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

然而,这个过程并不能正确地混合这些层

这是我得到的

混合前首先(正确)

混合前二次(正确)

两个通过混合如所述(不正确)
太暗了

prefultipliedAlpha disabled(不正确)
太透明了

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

为什么平方太透明了的时候premultipliedAlpha被禁用?

如何将两层混合在一起,使它们看起来与混合前相同?

解决方法

将Shader Pass blendSrc更改为ONE可以解决问题.例如
sp.material.blending = THREE.CustomBlending;
sp.material.blendSrc = THREE.OneFactor;

我相信这是因为径向模糊着色器的功能尤其如此.首先,渲染通过颜色缓冲区被清除为黑色透明颜色,并在其中绘制一个不透明的正方形.然后,径向模糊着色器会将这些不透明像素与其周围的透明像素模糊.这具有超越任何非透明像素的alpha的效果.

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

原文地址:https://www.jb51.cc/js/153748.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐