THREE.js 中 sRGB 纹理的伽玛校正处理

问题描述 投票:0回答:1

我无法让 Three.js 正确处理我的 sRGB 纹理。我该怎么办?

这是 sRGB 测试图像,应均匀显示 50% 灰度:

它主要由棋盘图案中交替的黑白像素组成。黑色 = (0,0,0) 白色 = (255,255,255) 这应导致平均线性强度为 50%。 中间有三个强度为 50% 的灰色色块,在 sRGB 中编码为 (188,188,188)(如下所述)。

解释:(参见维基百科文章sRGB 这表示标准化 50% 强度应获得 sRGB 值 (1.055*0.5^(1/2.4))-0.055 = 0.735358,在 8 位 sRGB 中约为 187.516,因此将其编码为 188 的逻辑。

从视觉上看,在任何经过适当校准的显示器上本地查看时,纹理显然是正确的,并且在这篇文章中看起来可能不错,除非您的浏览器缩小了图像。 (浏览器也往往无法进行伽玛校正下采样,因此,如果您已经可以轻松看到三个条,请以其原始分辨率查看图像。)

当缩小该图像时,大多数简单的软件只会对 sRGB 编码进行平均,并得到太暗的虚假强度。黑色和白色像素将被天真地平均为 (0+255)/2,以获得颜色 (128,128,128),其强度仅为约 22% (((128/255)+0.055)/(1.055)^2.4 ) =~ 0.215861。这比所需的 50% 强度要暗得多,50% 是 0% 和 100% 的同等贡献的平均强度。直观上来说,“一半亮度”就是 50%。

在 OpenGL 中,这很容易。您只需将纹理视为 sRGB 并使用 sRGB 帧缓冲区即可。采样时纹理样本从 sRGB 编码转换为线性,写入帧缓冲区时线性值被编码为 sRGB,一切顺利。

我完全不知道如何在 Three.js 框架内实现这一点。有人可以帮忙吗?

我尝试过设置...

renderer.gammaFactor = 2.2;
renderer.gammaInput = true;
renderer.gammaOutput = true;`

...但确实看起来 mipmap 生成没有正确地将纹理视为 sRGB。加载纹理时我没有做任何特别的事情——只是使用了 THREE.TextureLoader。

var texture = textureLoader.load( 'images/gamma_srgb.png' );

但是这里的质量很差,图像的棋盘部分变得太暗,但图像的纯色区域却亮得多。

仅供参考,我确实使用

{antialias: true}
创建了 WebGLRenderer,并且我确实将纹理上的
maxAnisotropy
设置为渲染器的最大各向异性值。

编辑:在 jsfiddle 中添加了重现。 https://jsfiddle.net/fvusxp1g/

three.js
1个回答
1
投票

正如 @WestLangley 的评论中所指出的,以下代码可以使纹理正确显示,并且不会被冲掉或不饱和:

var texture = textureLoader.load( 'images/gamma_srgb.png' );
texture.encoding = THREE.sRGBEncoding;
© www.soinside.com 2019 - 2024. All rights reserved.