2011-04-14 78 views

回答

0

好了,就opengl.org一個人曾指出,剪輯空間座標投影產生由clipPos.w劃分來計算標準化設備座標。當將從ndc上的片段反轉爲片段空間座標時,需要重建該w(恰好相應的視圖空間(相機)座標爲-z),並將ndc座標與該值相乘以計算適當的剪輯空間座標(您可以通過將其與逆向投影矩陣相乘來變成視圖空間座標)。

以下代碼假定您正在處理後期處理幀緩衝區。在渲染幾何體時處理它時,可以使用gl_FragCoord.z而不是texture2D(sceneDepth,ndcPos.xy).r。

下面是代碼:

uniform sampler2D sceneDepth; 
uniform mat4 projectionInverse; 
uniform vec2 clipPlanes; // zNear, zFar 
uniform vec2 windowSize; // window width, height 

#define ZNEAR clipPlanes.x 
#define ZFAR clipPlanes.y 

#define A (ZNEAR + ZFAR) 
#define B (ZNEAR - ZFAR) 
#define C (2.0 * ZNEAR * ZFAR) 
#define D (ndcPos.z * B) 
#define ZEYE -(C/(A + D)) 

void main() 
{ 
vec3 ndcPos; 
ndcPos.xy = gl_FragCoord.xy/windowSize; 
ndcPos.z = texture2D (sceneDepth, ndcPos.xy).r; // or gl_FragCoord.z 
ndcPos -= 0.5; 
ndcPos *= 2.0; 
vec4 clipPos; 
clipPos.w = -ZEYE; 
clipPos.xyz = ndcPos * clipPos.w; 
vec4 eyePos = projectionInverse * clipPos; 
} 

基本上這是gluUnproject的GLSL版本。

2

假設您堅持使用固定的管道式模型,視圖和投影,您只需實現gluUnProject man page中給出的公式即可。

在GLSL中沒有內置矩陣反轉,所以理想情況下你應該在CPU上。所以你需要提供一個統一的你構成的modelViewProjection矩陣的反函數。 gl_FragCoord在窗口座標中,所以您還需要提供視圖尺寸。

所以,你可能最終得到類似的信息(編碼即席):

vec4 unProjectedPosition = invertedModelViewProjection * vec4( 
       2.0 * (gl_FragCoord.x - view[0])/view[2] - 1.0, 
       2.0 * (gl_FragCoord.y - view[1])/view[3] - 1.0, 
       2.0 * gl_FragCoord.z - 1.0, 
       1.0); 

如果你實現了自己的老矩陣堆棧的模擬那麼你可能罰款反轉的矩陣。否則,它可能是你預期的more daunting topic,你可能會更好地使用MESA的open source implementation(參見該文件中的第三個函數invert_matrix),僅僅因爲它沒有其他的測試過。

+0

請問什麼是「視圖」? – karx11erx 2011-04-15 17:21:42

+0

這與GL_VIEWPORT的glGetIntegerv返回的值相同。你還應該根據下面的kvark精神修改我的答案:在我看起來比較熟悉的GLSL規範的更新版本中,反轉出現了。不過,你可能不希望每個片段都這樣做,因爲它會非常昂貴。 – Tommy 2011-04-15 21:56:02

+0

謝謝。因此,查看[0 .. 3] =左,右,下,上?我有一個矩陣求逆函數,沒問題。其實我已經在使用它(請參閱我對kvark評論的回覆以獲取詳細信息)。 – karx11erx 2011-04-16 09:29:37

3

看起來是GLSL convert gl_FragCoord.z into eye-space z的重複。

編輯(完整的答案):

// input: x_coord, y_coord, samplerDepth 
vec2 xy = vec2(x_coord,y_coord); //in [0,1] range 
vec4 v_screen = vec4(xy, texture(samplerDepth,xy), 1.0); 
vec4 v_homo = inverse(gl_ProjectionMatrix) * 2.0*(v_screen-vec4(0.5)); 
vec3 v_eye = v_homo.xyz/v_homo.w; //transfer from homogeneous coordinates 
+0

我已閱讀您對該問題的回覆。首先我不想重建z。然後你的回答包含看似完全不相關的東西,即你在一個代碼行中使用了一些變量,這些變量在以後的代碼中永遠不會被引用。當我讀到時,我問自己在回覆時發生了什麼。 (j/k);-) – karx11erx 2011-04-15 17:21:07

+0

@ karx11erx:答案並不像你說的那麼糟糕,但我已經在這裏提供了一個固定版本供你參考。 – kvark 2011-04-15 19:18:04

+0

很酷。謝謝。我認爲它還不完全正確。您從深度緩衝區中獲取v_screen,並在下一行中使用read_depth。我認爲它們是相同的。問題在於,我沒有原始的投影矩陣。我想要做的是在渲染整個幀之後在後期處理中應用陰影貼圖。所以我有一個帶有顏色和深度緩衝區的FBO以及陰影貼圖。儘管如此,它並不完美。看到這裏的詳細信息:http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=295642#Post295642 – karx11erx 2011-04-16 09:21:32

0

我剛剛意識到沒有必要在片段着色器中進行這些計算。你可以通過這樣的CPU上,並與MVP倒數乘以它保存了幾個操作(假設glDepthRange(0, 1),隨意編輯):

glm::vec4 vp(left, right, width, height); 
glm::mat4 viewportMat = glm::translate(
    vec3(-2.0 * vp.x/vp.z - 1.0, -2.0 * vp.y/vp.w - 1.0, -1.0)) 
    * glm::scale(glm::vec3(2.0/vp.z, 2.0/vp.w, 2.0)); 
glm::mat4 mvpInv = inverse(mvp); 
glm::mat4 vmvpInv = mvpInv * viewportMat; 
shader->uniform("vmvpInv", vmvpInv); 

在着色器:

vec4 eyePos = vmvpInv * vec4(gl_FragCoord.xyz, 1); 
vec3 pos = eyePos.xyz/eyePos.w; 
0

我認爲所有可用的答案從某個方面觸及問題,khronos.org有一個Wiki頁面,列出了幾個不同的情況,並用着色器代碼進行了解釋,因此值得在此發佈。 Compute eye space from window space