Three.js带Depth实现分区分层渲染辉光特效渲染器(修复深度错误)

如何实现
1.基于EffectComposer后期渲染器。
2.借助UnrealBloomPass渲染通道,实现辉光效果。
3.接住ShaderPass,保此原有material。
4.单帧两次渲染,分别渲染辉光部分,还原正常部分。
5.相比直接分层两次渲染,修复了深度信息丢失导致的层级问题。

二、代码

<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″ />
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″ />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<script type=”x-shader/x-vertex” id=”vertexshader”>

varying vec2 vUv;

void main() {

vUv = uv;

gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );

}
</script>

<script type=”x-shader/x-fragment” id=”fragmentshader”>

uniform sampler2D baseTexture;
uniform sampler2D bloomTexture;

varying vec2 vUv;

void main() {

gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );

}
</script>
<script type=”module”>
import * as THREE from “./node_modules/three/build/three.module.js”;
import { OrbitControls } from “./node_modules/three/examples/jsm/controls/OrbitControls.js”;
import { EffectComposer } from “./node_modules/three/examples/jsm/postprocessing/EffectComposer.js”;
import { UnrealBloomPass } from “./node_modules/three/examples/jsm/postprocessing/UnrealBloomPass.js”;
import { RenderPass } from “./node_modules/three/examples/jsm/postprocessing/RenderPass.js”;
import { ShaderPass } from “./node_modules/three/examples/jsm/postprocessing/ShaderPass.js”;
import { FXAAShader } from “./node_modules/three/examples/jsm/shaders/FXAAShader.js”;

let scene, camera, renderer, bloomComposer, finalComposer;

const ENTIRE_SCENE = 0,
BLOOM_SCENE = 1;

const bloomLayer = new THREE.Layers();
bloomLayer.set(BLOOM_SCENE);
const darkMaterial = new THREE.MeshBasicMaterial({ color: “black” });
const materials = {};
const params = {
exposure: 0,
bloomStrength: 1.5,
bloomThreshold: 0,
bloomRadius: 0,
};

const init = () => {
// 场景
scene = new THREE.Scene();
// 相机
camera = new THREE.PerspectiveCamera(
70,
window.innerWidth / window.innerHeight,
1,
100000
);
camera.position.set(50, 50, 50);
camera.position.y = 50;
// 渲染器
renderer = new THREE.WebGLRenderer({
antialias: true,
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.toneMapping = THREE.ReinhardToneMapping;
document.body.appendChild(renderer.domElement);
// 环境光
const light = new THREE.AmbientLight(0xffffff, 0.6);
light.layers.enable(0);
light.layers.enable(1);
scene.add(light);
// 控制器
const controls = new OrbitControls(camera, renderer.domElement);
scene.add(new THREE.AxesHelper(100));
window.onresize = () => {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
};
};

const initComposer = () => {
const effectFXAA = new ShaderPass(FXAAShader);
effectFXAA.uniforms[“resolution”].value.set(
0.6 / window.innerWidth,
0.6 / window.innerHeight
);
effectFXAA.renderToScreen = true;
const renderScene = new RenderPass(scene, camera);
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5,
0.4,
0.85
);
bloomPass.threshold = params.bloomThreshold;
bloomPass.strength = params.bloomStrength;
bloomPass.radius = params.bloomRadius;

bloomComposer = new EffectComposer(renderer);
bloomComposer.renderToScreen = false;
bloomComposer.addPass(renderScene);
bloomComposer.addPass(bloomPass);
bloomComposer.addPass(effectFXAA);

const finalPass = new ShaderPass(
new THREE.ShaderMaterial({
uniforms: {
baseTexture: { value: null },
bloomTexture: { value: bloomComposer.renderTarget2.texture },
},
vertexShader: document.getElementById(“vertexshader”).textContent,
fragmentShader: document.getElementById(“fragmentshader”)
.textContent,
defines: {},
}),
“baseTexture”
);
finalPass.needsSwap = true;

finalComposer = new EffectComposer(renderer);
finalComposer.addPass(renderScene);
finalComposer.addPass(finalPass);
finalComposer.addPass(effectFXAA);
};

const render = () => {
scene.traverse((obj) => {
if (obj.isMesh && bloomLayer.test(obj.layers) === false) {
materials[obj.uuid] = obj.material;
obj.material = darkMaterial;
}
});
bloomComposer.render();
scene.traverse((obj) => {
if (materials[obj.uuid]) {
obj.material = materials[obj.uuid];
delete materials[obj.uuid];
}
});
finalComposer.render();
requestAnimationFrame(render);
};

const main = () => {
init();
initComposer();

const geometry = new THREE.BoxGeometry(20, 20, 10);
// 正常方块
const normalMtl = new THREE.MeshLambertMaterial({ color: 0x00ffff });
const normalBox = new THREE.Mesh(geometry, normalMtl);
normalBox.position.z = -5;
normalBox.layers.set(0);
scene.add(normalBox);

// 发光方块
const bloomMtl = new THREE.MeshLambertMaterial({ color: 0xff5500 });
const bloomBox = new THREE.Mesh(geometry, bloomMtl);
bloomBox.position.z = 5;
bloomBox.layers.enable(1);
scene.add(bloomBox);
};
main();
render();
</script>
</body>
</html>

Three.js 修复UnrealBloomPass导致背景失效问题

import {
AdditiveBlending,
Color,
LinearFilter,
MeshBasicMaterial,
RGBAFormat,
ShaderMaterial,
UniformsUtils,
Vector2,
Vector3,
WebGLRenderTarget
} from “../../../build/three.module.js”;
import { Pass } from “../postprocessing/Pass.js”;
import { CopyShader } from “../shaders/CopyShader.js”;
import { LuminosityHighPassShader } from “../shaders/LuminosityHighPassShader.js”;

/**
* UnrealBloomPass is inspired by the bloom pass of Unreal Engine. It creates a
* mip map chain of bloom textures and blurs them with different radii. Because
* of the weighted combination of mips, and because larger blurs are done on
* higher mips, this effect provides good quality and performance.
*
* Reference:
* – https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/
*/
var UnrealBloomPass = function (resolution, strength, radius, threshold) {

Pass.call(this);

this.strength = (strength !== undefined) ? strength : 1;
this.radius = radius;
this.threshold = threshold;
this.resolution = (resolution !== undefined) ? new Vector2(resolution.x, resolution.y) : new Vector2(256, 256);

// create color only once here, reuse it later inside the render function
this.clearColor = new Color(0, 0, 0);

// render targets
var pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat };
this.renderTargetsHorizontal = [];
this.renderTargetsVertical = [];
this.nMips = 5;
var resx = Math.round(this.resolution.x / 2);
var resy = Math.round(this.resolution.y / 2);

this.renderTargetBright = new WebGLRenderTarget(resx, resy, pars);
this.renderTargetBright.texture.name = “UnrealBloomPass.bright”;
this.renderTargetBright.texture.generateMipmaps = false;

for (var i = 0; i < this.nMips; i++) {

var renderTargetHorizonal = new WebGLRenderTarget(resx, resy, pars);

renderTargetHorizonal.texture.name = “UnrealBloomPass.h” + i;
renderTargetHorizonal.texture.generateMipmaps = false;

this.renderTargetsHorizontal.push(renderTargetHorizonal);

var renderTargetVertical = new WebGLRenderTarget(resx, resy, pars);

renderTargetVertical.texture.name = “UnrealBloomPass.v” + i;
renderTargetVertical.texture.generateMipmaps = false;

this.renderTargetsVertical.push(renderTargetVertical);

resx = Math.round(resx / 2);

resy = Math.round(resy / 2);

}

// luminosity high pass material

if (LuminosityHighPassShader === undefined)
console.error(“UnrealBloomPass relies on LuminosityHighPassShader”);

var highPassShader = LuminosityHighPassShader;
this.highPassUniforms = UniformsUtils.clone(highPassShader.uniforms);

this.highPassUniforms[“luminosityThreshold”].value = threshold;
this.highPassUniforms[“smoothWidth”].value = 0.01;

this.materialHighPassFilter = new ShaderMaterial({
uniforms: this.highPassUniforms,
vertexShader: highPassShader.vertexShader,
fragmentShader: highPassShader.fragmentShader,
defines: {}
});

// Gaussian Blur Materials
this.separableBlurMaterials = [];
var kernelSizeArray = [3, 5, 7, 9, 11];
var resx = Math.round(this.resolution.x / 2);
var resy = Math.round(this.resolution.y / 2);

for (var i = 0; i < this.nMips; i++) {

this.separableBlurMaterials.push(this.getSeperableBlurMaterial(kernelSizeArray[i]));

this.separableBlurMaterials[i].uniforms[“texSize”].value = new Vector2(resx, resy);

resx = Math.round(resx / 2);

resy = Math.round(resy / 2);

}

// Composite material
this.compositeMaterial = this.getCompositeMaterial(this.nMips);
this.compositeMaterial.uniforms[“blurTexture1”].value = this.renderTargetsVertical[0].texture;
this.compositeMaterial.uniforms[“blurTexture2”].value = this.renderTargetsVertical[1].texture;
this.compositeMaterial.uniforms[“blurTexture3”].value = this.renderTargetsVertical[2].texture;
this.compositeMaterial.uniforms[“blurTexture4”].value = this.renderTargetsVertical[3].texture;
this.compositeMaterial.uniforms[“blurTexture5”].value = this.renderTargetsVertical[4].texture;
this.compositeMaterial.uniforms[“bloomStrength”].value = strength;
this.compositeMaterial.uniforms[“bloomRadius”].value = 0.1;
this.compositeMaterial.needsUpdate = true;

var bloomFactors = [1.0, 0.8, 0.6, 0.4, 0.2];
this.compositeMaterial.uniforms[“bloomFactors”].value = bloomFactors;
this.bloomTintColors = [new Vector3(1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, 1),
new Vector3(1, 1, 1), new Vector3(1, 1, 1)];
this.compositeMaterial.uniforms[“bloomTintColors”].value = this.bloomTintColors;

// copy material
if (CopyShader === undefined) {

console.error(“UnrealBloomPass relies on CopyShader”);

}

var copyShader = CopyShader;

this.copyUniforms = UniformsUtils.clone(copyShader.uniforms);
this.copyUniforms[“opacity”].value = 1.0;

this.materialCopy = new ShaderMaterial({
uniforms: this.copyUniforms,
vertexShader: copyShader.vertexShader,
fragmentShader: copyShader.fragmentShader,
blending: AdditiveBlending,
depthTest: false,
depthWrite: false,
transparent: true
});

this.enabled = true;
this.needsSwap = false;

this.oldClearColor = new Color();
this.oldClearAlpha = 1;

this.basic = new MeshBasicMaterial();

this.fsQuad = new Pass.FullScreenQuad(null);

};

UnrealBloomPass.prototype = Object.assign(Object.create(Pass.prototype), {

constructor: UnrealBloomPass,

dispose: function () {

for (var i = 0; i < this.renderTargetsHorizontal.length; i++) {

this.renderTargetsHorizontal[i].dispose();

}

for (var i = 0; i < this.renderTargetsVertical.length; i++) {

this.renderTargetsVertical[i].dispose();

}

this.renderTargetBright.dispose();

},

setSize: function (width, height) {

var resx = Math.round(width / 2);
var resy = Math.round(height / 2);

this.renderTargetBright.setSize(resx, resy);

for (var i = 0; i < this.nMips; i++) {

this.renderTargetsHorizontal[i].setSize(resx, resy);
this.renderTargetsVertical[i].setSize(resx, resy);

this.separableBlurMaterials[i].uniforms[“texSize”].value = new Vector2(resx, resy);

resx = Math.round(resx / 2);
resy = Math.round(resy / 2);

}

},

render: function (renderer, writeBuffer, readBuffer, deltaTime, maskActive) {

this.oldClearColor.copy(renderer.getClearColor());
this.oldClearAlpha = renderer.getClearAlpha();
var oldAutoClear = renderer.autoClear;
renderer.autoClear = false;

renderer.setClearColor(this.clearColor, 0);

if (maskActive) renderer.state.buffers.stencil.setTest(false);

// Render input to screen

if (this.renderToScreen) {

this.fsQuad.material = this.basic;
this.basic.map = readBuffer.texture;

renderer.setRenderTarget(null);
renderer.clear();
this.fsQuad.render(renderer);

}

// 1. Extract Bright Areas

this.highPassUniforms[“tDiffuse”].value = readBuffer.texture;
this.highPassUniforms[“luminosityThreshold”].value = this.threshold;
this.fsQuad.material = this.materialHighPassFilter;

renderer.setRenderTarget(this.renderTargetBright);
renderer.clear();
this.fsQuad.render(renderer);

// 2. Blur All the mips progressively

var inputRenderTarget = this.renderTargetBright;

for (var i = 0; i < this.nMips; i++) {

this.fsQuad.material = this.separableBlurMaterials[i];

this.separableBlurMaterials[i].uniforms[“colorTexture”].value = inputRenderTarget.texture;
this.separableBlurMaterials[i].uniforms[“direction”].value = UnrealBloomPass.BlurDirectionX;
renderer.setRenderTarget(this.renderTargetsHorizontal[i]);
renderer.clear();
this.fsQuad.render(renderer);

this.separableBlurMaterials[i].uniforms[“colorTexture”].value = this.renderTargetsHorizontal[i].texture;
this.separableBlurMaterials[i].uniforms[“direction”].value = UnrealBloomPass.BlurDirectionY;
renderer.setRenderTarget(this.renderTargetsVertical[i]);
renderer.clear();
this.fsQuad.render(renderer);

inputRenderTarget = this.renderTargetsVertical[i];

}

// Composite All the mips

this.fsQuad.material = this.compositeMaterial;
this.compositeMaterial.uniforms[“bloomStrength”].value = this.strength;
this.compositeMaterial.uniforms[“bloomRadius”].value = this.radius;
this.compositeMaterial.uniforms[“bloomTintColors”].value = this.bloomTintColors;

renderer.setRenderTarget(this.renderTargetsHorizontal[0]);
renderer.clear();
this.fsQuad.render(renderer);

// Blend it additively over the input texture

this.fsQuad.material = this.materialCopy;
this.copyUniforms[“tDiffuse”].value = this.renderTargetsHorizontal[0].texture;

if (maskActive) renderer.state.buffers.stencil.setTest(true);

if (this.renderToScreen) {

renderer.setRenderTarget(null);
this.fsQuad.render(renderer);

} else {

renderer.setRenderTarget(readBuffer);
this.fsQuad.render(renderer);

}

// Restore renderer settings

renderer.setClearColor(this.oldClearColor, this.oldClearAlpha);
renderer.autoClear = oldAutoClear;

},

getSeperableBlurMaterial: function (kernelRadius) {

return new ShaderMaterial({

defines: {
“KERNEL_RADIUS”: kernelRadius,
“SIGMA”: kernelRadius
},

uniforms: {
“colorTexture”: { value: null },
“texSize”: { value: new Vector2(0.5, 0.5) },
“direction”: { value: new Vector2(0.5, 0.5) }
},

vertexShader:
“varying vec2 vUv;\n\
void main() {\n\
vUv = uv;\n\
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
}”,

fragmentShader:
“#include <common>\
varying vec2 vUv;\n\
uniform sampler2D colorTexture;\n\
uniform vec2 texSize;\
uniform vec2 direction;\
\
float gaussianPdf(in float x, in float sigma) {\
return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;\
}\
void main() {\n\
vec2 invSize = 1.0 / texSize;\
float fSigma = float(SIGMA);\
float weightSum = gaussianPdf(0.0, fSigma);\
float alphaSum = 0.0;\
vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;\
for( int i = 1; i < KERNEL_RADIUS; i ++ ) {\
float x = float(i);\
float w = gaussianPdf(x, fSigma);\
vec2 uvOffset = direction * invSize * x;\
vec4 sample1 = texture2D( colorTexture, vUv + uvOffset);\
vec4 sample2 = texture2D( colorTexture, vUv – uvOffset);\
diffuseSum += (sample1.rgb + sample2.rgb) * w;\
alphaSum += (sample1.a + sample2.a) * w;\
weightSum += 2.0 * w;\
}\
gl_FragColor = vec4(diffuseSum/weightSum, alphaSum/weightSum);\n\
}”
});

},

getCompositeMaterial: function (nMips) {

return new ShaderMaterial({

defines: {
“NUM_MIPS”: nMips
},

uniforms: {
“blurTexture1”: { value: null },
“blurTexture2”: { value: null },
“blurTexture3”: { value: null },
“blurTexture4”: { value: null },
“blurTexture5”: { value: null },
“dirtTexture”: { value: null },
“bloomStrength”: { value: 1.0 },
“bloomFactors”: { value: null },
“bloomTintColors”: { value: null },
“bloomRadius”: { value: 0.0 }
},

vertexShader:
“varying vec2 vUv;\n\
void main() {\n\
vUv = uv;\n\
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
}”,

fragmentShader:
“varying vec2 vUv;\
uniform sampler2D blurTexture1;\
uniform sampler2D blurTexture2;\
uniform sampler2D blurTexture3;\
uniform sampler2D blurTexture4;\
uniform sampler2D blurTexture5;\
uniform sampler2D dirtTexture;\
uniform float bloomStrength;\
uniform float bloomRadius;\
uniform float bloomFactors[NUM_MIPS];\
uniform vec3 bloomTintColors[NUM_MIPS];\
\
float lerpBloomFactor(const in float factor) { \
float mirrorFactor = 1.2 – factor;\
return mix(factor, mirrorFactor, bloomRadius);\
}\
\
void main() {\
gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) + \
lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) + \
lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) + \
lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) + \
lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) );\
}”
});

}

});

UnrealBloomPass.BlurDirectionX = new Vector2(1.0, 0.0);
UnrealBloomPass.BlurDirectionY = new Vector2(0.0, 1.0);

export { UnrealBloomPass };

————————————————
版权声明:本文为CSDN博主「漫 漫,」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_44375977/article/details/111470352

WEBGL学习网(WebGLStudy.COM)专注提供WebGL 、ThreeJS、BabylonJS等WEB3D开发案例源码下载。
声明信息:
1. 本站部分资源来源于用户上传和网络,如有侵权请邮件联系站长:1218436398@qq.com!我们将尽快处理。
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源打赏售价用于赞助本站提供的服务支出(包括不限服务器、网络带宽等费用支出)!
7.欢迎加QQ群学习交流:549297468 ,或者搜索微信公众号:WebGL学习网
WEBGL学习网 » Three.js带Depth实现分区分层渲染辉光特效渲染器(修复深度错误)

发表评论

提供优质的WebGL、ThreeJS源码

立即查看 了解详情