­

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/