標籤:

在 OpenGL 中應用查找表(LUT)技術時,如何避免紋理數據自動歸一化導致的圖像異常?

現有一個用於鏡頭畸變矯正的LUT,文件內容類似於

[ fixed(xy) for xy in origImage ] #python

將源圖的像素坐標map到一個新的坐標上,然後用兩位元組(ushort)表示一個,一個目標點剛好能用一個32位RGBA像素來表示。把這張查找錶轉成圖像顯示出來大概是這個樣子:

原LUT內容按位元組順序排列為 Xl,Xh,Yl,Yh(一個short的低位元組,高位元組)對應到像素格式為R=&>Xl, G=&>Xh, B=&>Yl, A=&>Yh。上圖去掉了透明度通道不然對這樣一個比較小的表來說Yh基本都是012,看起來就全透明的。

從圖裡可以看到從左到右紅色有明顯的周期現象,這是由於紅色表示X坐標的低位元組,當坐標超過255時由於進位會導致紅色重新變為0,並使Xh+1,也就是G分量+1,只不過G分量在這裡佔比太小看不出來而已;代表Yl位元組的B分量也有明顯的隨上到下周期變化現象。

好了那麼現在問題來了,把這張圖載入為紋理後,在fragment shader里訪問時,顏色會自動歸一化為1以內的浮點數,我像這樣還原一個坐標時:

vec4 sample = texture2D(fixingLUT,texcoord);
vec2 xi = sample.xy;
vec2 yi = sample.zw;

float rxi = xi.y*256.0+xi.x;// Xh*256+Xl
float ryi = yi.y*256.0+yi.x;

rxi = rxi*255.0/1280.0;// *255=&>轉回整型數據,/1280,圖像寬度為1280,模擬歸一化uv坐標
ryi = ryi*255.0/720.0;//高為720

在上圖R B分量進位處的邊緣產生了明顯的裂縫,如下

感覺應該是由於某種「精度不足」導致的,但說不上來確切的地方,是不是因為將點數據除以1280轉為歸一化uv坐標的時候丟失了精度,還是其它什麼原因,sampler2D的精度只有lowp,是不是也跟這個有關係?

這種問題如何規避?,能不能將查找表內的數據轉換一下存放?

ps. GLES2.0

----------------------------

經@Jare Guo 提醒,現象產生原因應該是由於進位,R從255到0的中途可能被插值出中間的數比如127,那麼問題的重點就改為如何改善表產生的數據避免這種影響?比如能不能避免相鄰像素間出現這種大跨度的跳變或保證就算產生插值也是一個平滑的結果。

ps1. es2.0雖然是「過時了」,但嵌入式的東西真不好說一定新東西比較好用,要考慮bsp支持,這裡還受制於qt版本,另外還要考慮有一大部分不是自己寫的代碼

ps2. 其實後來經同事經驗指點這個問題解決了的,但想看看各位有么有什麼其它的解決方案,如果碰巧說出了與現在類似的那就詳細了解下原理


把這張圖載入為紋理後,在fragment shader里訪問時,顏色會自動歸一化為1以內的浮點數,

這是錯誤的。你如果用浮點紋理格式,不會有這個歸一化。


首先其實這個數據不應該是一個紋理

而應該是一個Image 因為這個數據不需要也不應該濾波 mipmap 或者各向異性這些功能

直接用 RG16UI作為圖像格式

(上面有人說 因為是2個位元組合併為一個數字 所以用RGBA格式導致每個位元組分開插值是錯的 沒錯 但是就算用RG16UI這種格式 也不應該濾波 因為濾波是根據周圍像素線性插值的 而這裡實際上是非線性的 線性插值結果是不太對的 會有輕微扭曲 所以正確的做法是初始化時根據設備解析度生成同樣解析度的精確圖像 然後一直使用)

著色器中uimage2D 這樣就不會歸一化

用imageLoad(uimage2D,ivec2)訪問圖像數據

你說OpenES2.0里沒有這些?

首先第一OpenGLES支持動態載入和擴展的

最低兼容OpenGLES2.0不等於只能用OpenGLES2.0的coreprofile

而是應該根據設備實際情況動態載入版本或功能

設備不支持OpenGLES3.x不證明所有這些功能都不支持

有些設備硬體和驅動可能支持更高版本 但是系統版本太低

有些設備硬體和驅動支持大部分OpenGLES3.x功能 但是不完全

所以無論如何 應該先檢查設備是否支持更高版本的OpenGLES 如果支持就動態載入高版本

如果不支持 那麼應該檢查你需要的功能是否在該設備支持的擴展中

如果支持就用擴展功能

如果實在是不支持的 那麼才是想一些workaround


你的表不能啟用線性過濾,因為存的數值是RG(BA)合併使用的,有進位的,不能像普通紋理那樣RGBA四個分量拆開來單獨插值。

例如從 ff00(255) 插值到 0001(256) 時,結果會突然跳到 7f00(127)


可以試試在保存lut時,使用範圍更大的像素格式!聽你描述,你用的是R8G8B8A8的格式,可以試試R16G16B16A16等範圍更大的像素格式來保存!


推薦閱讀:

不同GLSL版本之間的區別?
OpenGL ES2 對象樹繪製與 VBO組織問題?
現代OpenGL是怎麼繪製曲面的?
請問OpenGL的的頂點數據中position必須與color一一對應嗎?
如何正確理解 opengl 的 vao ?

TAG:OpenGL | OpenGLES |