更新于 

Layer 图层

Layer

ThreeJs提供序号在0-31之间共32个图层,
一个物体可以同时存在在不同的图层上,
只有与相机同图层的物体才能被观察到。

Layer类提供了一些简单的操作:

  • set 指定为某一图层
  • enableAll 开启全部图层
  • enable 开启某一图层
图层操作切换物体可见性
图层操作切换物体可见性

图层+渲染通道控制

可以使用图层来控制某个物体,或物体的某个部分是否需要叠加渲染效果。

UnrealBloom这个渲染效果为例,
未经渲染的原始效果如下:

我们需要将所有物体的纹理变成深色:

将需要应用效果的物体从中排除,
保持原来的材质,
这里用到的是层级控制:

添加UnrealBloom的渲染效果,
(深色纹理不受unrealBloom效果的影响)

最后一步:将物体的原始纹理映射重新取出

上面的步骤是three渲染的流程,实际实现流程如下:

步骤1: 绘制原始图像

1
2
3
4
5
6
7
8
9
10
11
12
13
let model, mixer
const loader = new GLTFLoader()
loader.load('assets/fantasy_sword.glb', gltf => {
model = gltf.scene
scene.add(model)
model.position.set(0, 126, -127)
mixer = new THREE.AnimationMixer(model)
const action = mixer.clipAction(
THREE.AnimationClip.findByName(gltf.animations,'Idle')
)
action.play()
})

步骤2:添加Bloom效果合成器

1
2
3
4
5
6
7
8
9
10
const renderScene = new RenderPass(scene, camera)
const unrealBloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5,
1,
0.1
)
const bloomComposer = new EffectComposer(renderer)
bloomComposer.addPass(renderScene)
bloomComposer.addPass(unrealBloomPass)
1
2
3
4
function animate() {
// ...
bloomComposer.render()
}
1
2
3
4
window.addEventListener('resize', function() {
// ...
bloomComposer.setSize(window.innerWidth, window.innerHeight)
});

步骤3:添加Bloom和原始图像的混合合成器

这个合成器包含3个主要的通道:

  • 原始基层图像通道 renderPass
  • 合成Bloom和基层会用到的着色器通道 shaderPass
  • 输出最终效果的通道 outputPass

我们只需要用bloomRender的渲染效果进行合成,
并不需要将它渲染到场景中:

1
bloomComposer.renderToScreen = false

对多个纹理进行合成,需要用到ShaderPass,
ShaderPass接收ShaderMaterial作为参数,
在html中准备好vertexShader和fragmentShader:

VertexShader
1
2
3
4
5
6
7
<script type="x-vertex" id="v-shader">
varying vec2 vUv;
void main(){
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
FragmentShadre
1
2
3
4
5
6
7
8
<script type="x-fragment" id="f-shader">
varying vec2 vUv;
uniform sampler2D baseTexture;
uniform sampler2D bloomTexture;
void main(){
gl_FragColor = (texture2D(baseTexture, vUv) + vec4(1.0)*texture2D(bloomTexture, vUv));
}
</script>

其中gl_FragColor可以拆分成3个部分:

  • 原始渲染纹理 texture2D(baseTexture, vUv)
  • bloom渲染效果纹理 texture2D(bloomTexture, vUv)
  • 渲染效果强度 vec4(1.0)

创建ShaderPass:

1
2
3
4
5
6
7
8
9
10
const mixPass = new ShaderPass(
new THREE.ShaderMaterial({
uniforms: {
baseTexture: { value: null },
bloomTexture: { value: bloomComposer.renderTarget2.texture }
},
vertexShader: document.getElementById('v-shader').textContent,
fragmentShader: document.getElementById('f-shader').textContent,
}),'baseTexture'
)

创建渲染最后效果的合成器:

1
2
3
4
const outputPass = new OutputPass()
finalComposer.addPass(renderScene)
finalComposer.addPass(mixPass)
finalComposer.addPass(outputPass)
1
2
3
4
function animate() {
// ...
finalComposer.render()
}
1
2
3
4
window.addEventListener('resize', function() {
// ...
finalComposer.setSize(window.innerWidth, window.innerHeight)
});

步骤4:加入图层控制材质

创建Bloom图层、暗色纹理、纹理存储对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const BLOOM_LAYER = 1
const bloomLayer = new THREE.Layers()
bloomLayer.set(BLOOM_LAYER)
const darkMaterial = new THREE.MeshStandardMaterial({ color: 0x000000 })
const materials = {}

function storeMaterial(obj) {
if (obj.isMesh && !bloomLayer.test(obj.layers)) {
materials[obj.uuid] = obj.material
obj.material = darkMaterial
}
}

function restoreMaterial(obj) {
if (materials[obj.uuid]) {
obj.material = materials[obj.uuid]
delete materials[obj.uuid]
}
}

在loop函数内进行图层控制操作:

1
2
3
4
5
6
7
function animate() {
// ...
scene.traverse(storeMaterial) // 不在bloom层的变暗
bloomComposer.render() // bloom通道生成效果纹理
scene.traverse(restoreMaterial) // 恢复变暗部分的纹理
finalComposer.render() // 混合bloom与原始效果
}

做好这些工作之后,就可以通过图层控制的方式,
给不同部分添加渲染效果:

1
2
3
4
5
6
7
8
9
10
11
function createGUI() { 
const gui = new GUI()
const configs = {}
const names = ['Object_11', 'Object_12', 'Object_13', 'Object_14']
names.forEach(name => {
configs[name] = false
gui.add(configs, name).onChange(val => {
model.getObjectByName(name).layers.toggle(BLOOM_LAYER)
})
})
}