在使用threejs构建模型场景时,经常感觉场景中模型的颜色与建模工具中看到的不太一样,而且不管怎么调灯光还是模型颜色效果还是不太理想。。。
其实可能不是你的问题,所以不要轻易怀疑自己,当时也是找了很多文章,下面分享给大家
使用threejs构建场景中导入模型后,在灯光,贴图一定的情况下,场景中模型的颜色与建模工具中看到的往往会有些许差异,颜色总是很僵硬,少了些许生动。
问题分析:
造成这一问题的原因可能有这几方面:
这里我们只讨论关于颜色转换的问题,其他可以去
threejs开发文档 找下原因
这里简单介绍一下,色彩空间也称色彩模型(又称色彩空间或色彩系统)它的用途是在某些标准下用通常可接受的方式对彩色加以说明。本质上,色彩模型是坐标系统和子空间的阐述。位于系统的每种色彩都有单个点表示。采用的大多数色彩模型都是面向硬件或面向应用的。色彩空间从提出到现在已经有上百种,大部分只是局部的改变或专用于某一领域。色彩空间有许多种,常用有RGB,YUV,HSV,HSI等。
详细了解 色彩空间介绍
其次还有几个术语:
linear颜色空间:物理上的线性颜色空间,当计算机需要对sRGB像素运行图像处理算法时,一般会采用线性颜色空间计算。
sRGB颜色空间: sRGB是当今一般电子设备及互联网图像上的标准颜色空间。较适应人眼的感光。sRGB的gamma与2.2的标准gamma非常相似,所以在从linear转换为sRGB时可通过转换为gamma2.2替代。
gamma转换:线性与非线性颜色空间的转换可通过gamma空间进行转换。
上图这两个灰度条,第一个是线性的从黑到白,第二个是以人类感知为准的灰度条,当人类18%左右的亮度的光源时,就能感觉到这是50%的亮度了。这就是为什么要有不同的色彩空间。
在着色器中色值的提取与色彩的计算操作一般都是在线性空间着色器就是计算机渲染图像的一个程序
。在webgl中,贴图或者颜色以srgb传入时,必须转换为线性空间。计算完输出后再将线性空间转为srgb空间。
Shaders着色器
前面内容就当是扫盲,感兴趣个自行学习了解,重点是解决问题 。
在ThreeJS中,当我们为材质单独设置贴图和颜色时,需要进行色彩空间转换。具体的转换threejs会在着色器中进行,我们只需要关注为贴图指定好色彩空间,或者直接调用转换函数。
具体代码如下:
sRGB转Linear
A. 对于贴图的颜色处理方式:
threejs 需要在线性颜色空间(linear colorspace)里渲染模型的材质,而从一般软件中导出的模型中包含颜色信息的贴图一般都是sRGB颜色空间(sRGB colorspace),故需要先将sRGB转换为Linear。
然而 threejs 在导入材质时,会默认将贴图编码格式定义为Three.LinearEncoding,故需将带颜色信息的贴图(baseColorTexture, emissiveTexture, 和 specularGlossinessTexture)手动指定为Three.sRGBEncoding,threejs在渲染时判断贴图为sRGB后,会自动将贴图转换为Linear再进行渲染计算。
// 设置加载texture的encoding
const loadTex = (callback) => {
const textureLoader = new THREE.TextureLoader();
textureLoader.load( "./assets/texture/tv-processed0.png", function(texture){
texture.encoding = THREE.sRGBEncoding;
});
...
}
B. 对于color的处理方式:
在直接定义 threejs material 的 color 值时,需要进行如下的转换:
const material = new THREE.MeshPhongMaterial( {
color: 0xBBBBBB
} );
material.color.convertSRGBToLinear();
渲染计算后的模型仍在linear空间,展示到屏幕时需要通过gamma校正,将linear转换回sRGB空间,也就是进行gamma校正,threejs中可通过设置gammaOutput和gammaFactor,进行gamma校正,校正后的gamma2.2颜色空间与sRGB相似。
// 定义gammaOutput和gammaFactor
renderer.gammaOutput = true;
renderer.gammaFactor = 2.2; //电脑显示屏的gammaFactor为2.2
若采用 GLTFLoader 导入带贴图的模型,GLTFLoader 将在渲染前自动把贴图设置为 THREE.sRGBEncoding,故不需要手动设置贴图 encoding。在 GLTFLoader 之前,threejs 也没有很好地处理色彩空间这回事,所以大家需要排查一下其他 loader 有没有这个 bug。
使用不受光照影响的材质,例如 MeshBasicMaterial,着色器不需要做复杂的计算,故不需要进行色彩空间转换。
相关资料文章:
1. https://threejs.org/docs/index.html#examples/en/loaders/GLTFLoader
2. https://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/
3. https://discoverthreejs.com/tips-and-tricks/
4. https://cloud.tencent.com/developer/article/1543647