如何美化一个 three.js 的场景渲染

呀哈喽!这里是 alphardex。

在使用 three.js 开发时,有时会感觉场景的渲染不是非常美观,本文就来一步一步地将一个不是很美观的场景重新焕发它的生机。

场景美化前:
before.png

场景美化后:
after.png

准备工作

首先在码上掘金上打开并 fork 这个初始场景

https://code.juejin.cn/pen/7209892060004876344

环境贴图

尽管已经有了 2 个光照,但模型还是显得非常暗,第一个任务就是要增加模型的亮度

该模型的主要材质是MeshStandardMaterial,它是一种基于PBR的材质,PBR 材质比较容易受光照的影响,但效果最显著的还是要靠环境贴图

环境贴图往往就是 360° 的全景照片,通常可以在polyhaven网站上找到,这里选用了最常见的potsdamer_platz来模拟城市的环境光

kokomi.AssetManager中加载 HDR 素材(加载的背后用到了RGBELoader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const am = new kokomi.AssetManager(
this,
[
{
name: "hdr",
type: "hdrTexture",
path: "https://kokomi-demo-1259280366.cos.ap-nanjing.myqcloud.com/potsdamer_platz_1k.hdr",
},
...
],
{
useDracoLoader: true,
}
);

THREE.PMREMGenerator提取出 envmap,应用到scene.environment即可

1
2
3
4
5
6
7
const getEnvmapFromHDRTexture = (renderer, texture) => {
const pmremGenerator = new THREE.PMREMGenerator(renderer);
pmremGenerator.compileEquirectangularShader();
const envmap = pmremGenerator.fromEquirectangular(texture).texture;
pmremGenerator.dispose();
return envmap;
};
1
2
const envMap = getEnvmapFromHDRTexture(this.renderer, am.items["hdr"]);
this.scene.environment = envMap;

1.png

输出编码

three.js 默认的输出编码是线性编码(THREE.LinearEncoding),这种编码会使场景看上去偏暗淡一点

为了获得一个色彩更加明艳的场景,我们需要将编码改为THREE.sRGBEncoding

1
this.renderer.outputEncoding = THREE.sRGBEncoding;

2.png

后期处理

如果说以上的两步是必经之路的话,那这一步可以算是画龙点睛了

Bloom滤镜是最常用的滤镜之一,它可以照亮整个场景

SMAA滤镜也是很常用的滤镜,它能消除场景中的锯齿,尤其是对这种顶点很多的模型颇为有效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
this.scene.background = this.scene.background.convertSRGBToLinear();

const composer = new POSTPROCESSING.EffectComposer(this.renderer, {
frameBufferType: THREE.HalfFloatType,
multisampling: 8,
});
this.composer = composer;

composer.addPass(new POSTPROCESSING.RenderPass(this.scene, this.camera));

const bloom = new POSTPROCESSING.BloomEffect({
blendFunction: POSTPROCESSING.BlendFunction.ADD,
mipmapBlur: true,
luminanceThreshold: 1,
});

const smaa = new POSTPROCESSING.SMAAEffect();

const effectPass = new POSTPROCESSING.EffectPass(this.camera, bloom, smaa);
composer.addPass(effectPass);

this.renderer.autoClear = true;

使用了它们后,会发现场景除了无锯齿外并没有什么明显的变化,别急,好戏还在后头~

在这个场景中,车灯、轮胎灯、尾灯都用到了自发光贴图emissive map,而这个贴图的显示效果跟材质的某个参数息息相关,这个参数是emissiveIntensity,将它调高就会使自发光的效果更加显著

此外,我们也要将材质的tonemapping给关闭,因为 tonemapping 会自动把颜色的 RGB 值限制在 0-1 内,达不到自发光的要求,解除了这个限制后,我们就会发现那些地方亮起来了

还有一个注意点:在Bloom滤镜中,我们把luminanceThreshold设为了 1,这是为了防止场景的其他部分发亮,仅仅让那些有emissive map的材质发亮

3.png

完成代码

https://code.juejin.cn/pen/7209889563153481765

Author: alphardex
Link: https://alphardex.github.io/mygo/posts/44091/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.