3D场景还原技巧

3D场景和2D页面不同,其效果是多种因素综合后决定的,因此还原设计师的3D场景,需要了解一些注意事项才行。

渲染器

常规的renderer设置这里就不赘述了,主要是下面几个设置,一定不要忘记了:

1
2
3
4
// 色彩空间编码,因为我用到了设计师给的gltf模型,所以使用更符合人眼的sRGBEncoding,而不是默认的linearEncoding
renderer.outputEncoding = THREE.sRGBEncoding;
// 正确的物理灯光照射
renderer.physicallyCorrectLights = true;

renderer.outputEncoding如果采用默认的线性编码(LinearEncoding),看起来颜色会特别深。不过有一些场景,设计师会故意要求用LinearEncoding,比如想要表现科技感的场景。

环境

就一句话:必须要有HDR环境贴图。

环境贴图对效果的影响是最大的,没有之一,因此一定要让设计师提供一个HDR环境贴图给你。

HDR格式;

计算机在表示图象的时候是用8bit(256)级或16bit(65536)级来区分图象的亮度的,但这区区几百或几万无法再现真实自然的光照情况。HDR文件是一种特殊图形文件格式,它的每一个像素除了普通的RGB信息,还有该点的实际亮度信息。普通的图形文件每个象素只有0~255的灰度范围,这实际上是不够的。想象一下太阳的发光强度和一个纯黑的物体之间的灰度范围或者说亮度范围的差别,远远超过了256个级别。因此,一张普通的白天风景图片,看上去白云和太阳可能都呈现是同样的灰度/亮度,都是纯白色,但实际上白云和太阳之间实际的亮度不可能一样,他们之间的亮度差别是巨大的。因此,普通的图形文件格式是很不精确的,远远没有纪录到现实世界的实际状况。所以,现在我们就要介绍一下高动态范围图像(简称HDRI)。HDR全称是高动态范围。这是一个简单的术语,HDR图片是使用多张不同曝光的图片,然后再用软件将它们组合成一张图片。它的优势是最终你可以得到一张无论在阴影部分还是高光部分都有细节的图片。

如果加载的贴图是 exr/hdr 格式的,则都属于 hdr格式的环境贴图;

此外,有些png格式的图也是包含hdr信息的,比如RGBM / RGBE编码。

因为使用HDR缓冲区会浪费太多内存。实际上,我们可以对HDR值(RGB)进行编码并将它们存储到LDR缓冲区(RGBA)中。有许多编码方法。通常,我们可以使用RGBM或RGBE。一些弱的硬件如 WII 偏向使用RGBM编码,而强大的设备如PS3、XBOX360 可以使用RGBE编码。

LDR格式;

加载贴图是 png/jpg 格式的,而且图片种不包含hdr信息,即 非RGBM / RGBE编码。

如何调整HDR的亮度

HDR亮度是不能调整的,只能调整环境的亮度。

但是renderer.outputEncoding会影响整体环境的亮度,设置为sRGBEncoding会更亮一些,LinearEncoding会更暗一些。

模型

设计师提供的模型,一般都是一个组,里面有很多小元素构成,因此我们处理模型的时候,一定要记得遍历里面的小元素,逐个进行处理。

统一设计标准

注意要提前跟设计师协定好,在他们制作模型的时候,要保证:

  • 模型全部都以相同的中心点作为原点进行定位

  • 模型尺寸保持一致,比如都是1cm大小

这样后面我们通过程序加载模型的时候,才方便进行统一的缩放、定位。

纹理贴图的编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  function traverseMaterials(object, callback) {
object.traverse(node => {
if (!node.isMesh) {
return;
}

const materials = Array.isArray(node.material) ? node.material : [node.material];

materials.forEach(material => {
// TODO(https://github.com/mrdoob/three.js/pull/18235): Clean up.
material.depthWrite = !material.transparent;
callback(material);
});
});
}

function updateTextureEncoding(gltf) {
const encoding = THREE.sRGBEncoding;
traverseMaterials(gltf.scene, material => {
if (material.map) {
material.map.encoding = encoding;
}
if (material.emissiveMap) {
material.emissiveMap.encoding = encoding;
}
if (material.map || material.emissiveMap) {
material.needsUpdate = true;
}
});
}

光照

效果对比

用最近的一个项目的示例,展示下这几个因素对于最终效果的影响。

1、什么都没有

1

2、加上HDR环境贴图

2

3、设置了WebGLRenderer.outputEncoding = THREE.sRGBEncoding

3

4、设置了WebGLRenderer.physicallyCorrectLights = true

4