ThreeJS 不可忽略的事情 – Gamma色彩空間
- 2019 年 11 月 25 日
- 筆記
設計:為什麼模型的顏色跟我在建模工具看到的不一樣?
開發:引擎的光照和建模工具不太一樣,我調一下~
設計:還是不一樣~
開發:我再調整一下~
…
還在苦惱的調光照嗎,有木有想過,其實不一定是光照的原因,來看看這兩張用了同一光照的threejs渲染對比圖:

第二個效果和建模工具更加相似,主要區別是第一張圖直接導入了模型和貼圖,第二張圖在導入貼圖時做了色彩空間轉換。
下面來看看這是怎麼轉換的吧~
色彩空間介紹

我們來看看上圖這兩個灰度條,第一個是線性的從黑到白,第二個是以人類感知為準的灰度條,當人類18%左右的亮度的光源時,就能感覺到這是50%的亮度了。這就是為什麼要有不同的色彩空間。
先了解一下這幾個術語:
1. linear顏色空間:物理上的線性顏色空間,當電腦需要對sRGB像素運行影像處理演算法時,一般會採用線性顏色空間計算。
2. sRGB顏色空間: sRGB是當今一般電子設備及互聯網影像上的標準顏色空間。較適應人眼的感光。sRGB的gamma與2.2的標準gamma非常相似,所以在從linear轉換為sRGB時可通過轉換為gamma2.2替代。
3. gamma轉換:線性與非線性顏色空間的轉換可通過gamma空間進行轉換。

在著色器中色值的提取與色彩的計算操作一般都是在線性空間。在webgl中,貼圖或者顏色以srgb傳入時,必須轉換為線性空間。計算完輸出後再將線性空間轉為srgb空間。

ThreeJS 色彩空間轉換
故在ThreeJS中,當我們為材質單獨設置貼圖和顏色時,需要進行色彩空間轉換。具體的轉換threejs會在著色器中進行,我們只需要關注為貼圖指定好色彩空間,或者直接調用轉換函數。
具體步驟如下:
1. 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();
2. linear轉gamma2.2
渲染計算後的模型仍在linear空間,展示到螢幕時需要通過gamma校正,將linear轉換回sRGB空間,也就是進行gamma校正,threejs中可通過設置gammaOutput和gammaFactor,進行gamma校正,校正後的gamma2.2顏色空間與sRGB相似。
// 定義gammaOutput和gammaFactor renderer.gammaOutput = true; renderer.gammaFactor = 2.2; //電腦顯示器的gammaFactor為2.2
需要注意的是:
1. 若採用 GLTFLoader 導入帶貼圖的模型,GLTFLoader 將在渲染前自動把貼圖設置為 THREE.sRGBEncoding,故不需要手動設置貼圖 encoding。在 GLTFLoader 之前,threejs 也沒有很好地處理色彩空間這回事,所以大家需要排查一下其他 loader 有沒有這個 bug。
2. 使用不受光照影響的材質,例如 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/