1. 程式人生 > >關於混合(Blend)和浮點紋理的一件小事

關於混合(Blend)和浮點紋理的一件小事

最近在寫一個系統模擬程式,需要儘可能真實地模擬人眼因瞳孔而產生的聚焦效果。我不知道遊戲裡的景深效果一般是怎麼做的,不過猜測應該是使用深度緩衝加模糊特效吧,但是我這裡是不能這麼用的。

於是就打算在瞳孔上取樣若干點,對應這些點產生若干相機,最後將影象疊加的做法。也就是glLookAt裡前三個引數根據瞳孔取樣位置改變;中間三個引數,也就是對焦點不變:

gluLookAt(eye.x, eye.y, eye.z, focus.x, focus.y, focus.z, 0.0, 1.0, 0.0);

這樣子,只要程式中調節focus的位置,就能對焦到不同的位置上。

於是建立一個FBO,用於得到單個取樣點的渲染畫面,渲染到紋理。然後迴圈取樣次數,每次用glBlend來對取樣點得到的畫面進行混合:

glBlendColor(1.0, 1.0, 1.0, 1.0/pupil.samples);
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE);
glEnable(GL_BLEND);

用glBlendColor來計算一個平均的透明度,然後用GL_CONSTANT_ALPHA和GL_ONE的組合。實現每畫一張FBO紋理就將這張紋理按螢幕大小疊到原有畫面上去。

但是問題出現了。當取樣點比較少時可能看不出來,但是取樣點一多,比方說50個,顏色就變得很奇怪,過渡變得不均勻。想了想,應該是這麼算透明度時,由於顏色是256級整數,在計算過程中除不盡的小數都被抹掉了,導致了誤差累積。50次疊加之後,實際上畫面顏色只有256/50=5級了。

一開始不知道怎麼辦,甚至想把所有采樣點都建一個紋理,用多重紋理扔到glsl裡去算。但是想想,要是我想設幾百個取樣點呢?

於是想到了浮點紋理。在建立用於FBO渲染的紋理時,用到的一句glTexImage2D函式,函式的第三個引數internalformat,之前從來都只是設成GL_RGBA,沒細想過其作用。皆因顯示器的32位色正好對應了GL_RGBA,這麼設定本來沒什麼問題。但是對於我現在這種情況,需要對畫素進行細分時,8位的精度根本不夠,因此需要換成浮點數,以得到更準確的結果。

於是可以將這個引數從GL_RGBA換成GL_RGBA16F甚至GL_RGBA32F。這樣紋理在GPU內部就以0~1的16位或者32位浮點數的形式儲存,進行畫素運算時更為準確。