Как смешивать несколько слоев с непрозрачностью?

Используя three.js, я хотел бы применить отдельные эффекты последующей обработки к отдельным сценам, а затем объединить все эти сцены в окончательный рендеринг. Для этого я использую композитор эффектов. 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>

Ответ 1

Изменение параметра Shader Pass blendSrc в ONE устраняет проблему. например.

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

Я считаю, что это работает из-за того, как в основном работает радиальный размытие шейдеров. Сначала цветной буфер оформления передачи очищается до черного прозрачного цвета и внутри него нарисован непрозрачный квадрат. Радиальный размытый шейдер затем размывает эти непрозрачные пиксели с прозрачными пикселями вокруг него. Это приводит к преждевременному распространению альфа любых непрозрачных пикселей.

В этот момент рисование текстуры в буфере не требует больше увеличения исходных пикселей альфа-каналом. Это правильное объяснение?