2013-12-18 120 views
2

我對OpenGL ES相當陌生,我試圖給當前場景添加一些陰影。我決定在cubemap的幫助下做到這一點。我使用的是OpenGL ES 2.0,因此我無法使用幾何着色器或gl_FragDepth變量。我搜索了一些,所以我對該主題有了一些見解,並且閱讀(http://www.cg.tuwien.ac.at/courses/Realtime/repetitorium/2010/OmnidirShadows.pdf)證明非常有用。基本上我依靠這個鏈接的文檔。Opengl-es 2.0與立方體貼圖的陰影映射

但是我的代碼有些問題,因爲渲染場景中的每個像素都在陰影之下。我認爲這個問題可以在我的着色器中找到,但我在這裏粘貼我所有的相關代碼,以清楚地看到一切。

設置幀緩衝和創建立方體貼圖:

GLuint FBO; 
GLuint cubeTexture; 

glGenFramebuffers(1, &FBO); 
glBindFramebuffer(GL_FRAMEBUFFER, FBO); 

// Depth texture 
glGenTextures(1, &cubeTexture); 
glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture); 

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 
//glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); // no GL_TEXTURE_WRAP_R 

// right, left, top, bottom, front, back 
for (int face = 0; face < 6; ++face) { 
    // create space for the textures, content need not to be specified (last parameter is 0) 
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_DEPTH_COMPONENT16, 1024, 768, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0); 
} 

glBindTexture(GL_TEXTURE_CUBE_MAP, 0); 

glBindFramebuffer(GL_FRAMEBUFFER, 0); 

在顯示功能,我儘量做到6渲染過程,填補了陰影貼圖(立方體的6面)。

渲染:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 
glClearDepthf(1.0f); 

glm::vec3 camPos = ... // position of the camera, it is in world space 
glm::vec4 lightPos = ... // position of the light source, it is in world space 

// 1. render into texture 

float zNear = 0.1f; 
float zFar = 100.0f; 
// or should I use ortho instead of perspective? 
glm::mat4 projMatrix = glm::perspective(90.0f, (float)esContext->width/esContext->height, zNear, zFar); 

// The 6 cameras have to be placed to the light source and they need the proper view matrices 

glm::mat4 cubeMapMatrices[6]; // contains six basic and defined rotation matrices for the six directions 
cubeMapMatrices[0] = glm::make_mat4(rotPositiveX); 
cubeMapMatrices[1] = glm::make_mat4(rotNegativeX); 
cubeMapMatrices[2] = glm::make_mat4(rotPositiveY); 
cubeMapMatrices[3] = glm::make_mat4(rotNegativeY); 
cubeMapMatrices[4] = glm::make_mat4(rotPositiveZ); 
cubeMapMatrices[5] = glm::make_mat4(rotNegativeZ); 

glm::vec4 translation = lightPos; 

glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture); 

glBindFramebuffer(GL_FRAMEBUFFER, FBO); 

for (int face = 0; face < 6; ++face) { 

    glClear(GL_DEPTH_BUFFER_BIT); // do I need this here? 

    cubeMapMatrices[face][3] = translation; // the translation part is the same for all 
    cubeMapMatrices[face] = projMatrix * cubeMapMatrices[face]; // now it's an mvp matrix 

    glUniformMatrix4fv(cubeProjectionMatrixID, 1, GL_FALSE, glm::value_ptr(cubeMapMatrices[face])); 

    // Attach depth cubemap texture to FBO's depth attachment point 
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, cubeTexture, 0); 

    int err = glCheckFramebufferStatus(GL_FRAMEBUFFER); 
    if (err != GL_FRAMEBUFFER_COMPLETE) { 
     std::cout << "Framebuffer error, status: " << err << std::endl; 
    } 

    RenderScene(); // do the drawing 
} 

glBindFramebuffer(GL_FRAMEBUFFER, 0); 

// 2. render into screen 

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

RenderScene(); // do the drawing 

// swap the buffers 

所以來這裏不同的着色器。我有一個頂點着色器和一個片段着色器用於深度計算,一個頂點着色器和一個片段着色器用於屏幕渲染。我的問題是我不確定如何寫入立方體貼圖,如果幀緩衝區正在使用中,那麼gl_Position會確定給定立方體面的座標嗎?用於深度計算

頂點着色器:用於深度計算

in vec3 vPosition; 

uniform mat4 mModel; 
uniform mat4 mCubeProjection; 
uniform vec4 vLight; 

out vec4 vFrag Position; // world space 
out vec4 vLightPosition; // mvp transformed 

void main() 
{ 
    vec4 position = vec4(vPosition, 1.0); 
    vFragPosition = mModel* position; 
    vLightPosition = vLight; 
    gl_Position = mCubeProjection * vFragPosition; 
} 

片段着色器:

in vec4 vFragPosition; // world space 
in vec4 vLightPosition; // world space 

out float depthValue; 

void main() 
{ 
    depthValue = distance(vFragPosition.xyz, vLightPosition.xyz); // need normalization? 
} 

頂點着色器用於渲染到屏幕上:

uniform mat4 mModel; 
uniform mat4 mView; 
uniform mat4 mProjection; 
uniform vec4 vLight; // it is in world space 

out vec3 lw; // light position in world space 
out vec3 pw; // pixel position in world space 

void main() 
{ 
    vec4 position = vec4(vPosition, 1.0); 
    lw = vLight.xyz; 
    pw = (mModel* position).xyz; 
    gl_Position = mProjection* mView * mModel* position; 
} 

顯示到屏幕片段着色器:

in vec3 lw; 
in vec3 pw; 

uniform samplerCube cubeMap; 

out vec4 outputColor; 

void main() 
{ 
    vec3 lookup = pw - lw; 
    float smValue = texture(cubeMap, lookup).r; // retrieves texels from a texture (d, d, d, 1.0) 
    float distance = length(lookup); // dist from the fragment to the light 

    float eps = 0.1; 
    float shadowVal = 1.0; 

    if (smValue + eps < distance) { 
     shadowVal = 0.1; // in shadow 
    } 

    // here comes the lighting stuff 
    // ... 

    outputColor = outputColor * shadowVal; 
} 

如此反覆,問題是,每個像素下的陰影下降。從代碼中我排除了一些統一傳遞給着色器,但它們都可以。你能給我一個建議,我應該在代碼中修復什麼?我的着色器(特別是第一遍)是否正確,我是否正確設置了立方體映射的轉換?謝謝。

P.S:這是我的第一個問題,我希望它足夠清楚,並且滿足正確發佈的問題的要求。

回答

1

基本上,您的問題很簡單:您正在爲立方體貼圖使用深度附件,深度緩衝區存儲透視深度。

您的着色器期望在陰影貼圖中看到的是從光到最近片段的非透視距離。實際上,您已經在片段着色器中計算了這個用於創建陰影貼圖的內容,但是將其輸出到顏色緩衝區(沒有任何附加內容)而不是深度緩衝區。


有這個問題至少有三個可能的解決方案(在性能順序, 最差在前):

  1. 寫入gl_FragDepth而不是depthValue(這實際上是一個顏色緩衝區目標)。

  2. 將您的立方體貼圖附加到GL_COLOR_ATTACHMENT0並使用顏色可呈現格式而不是GL_DEPTH_COMPONENT

  3. 從着色器中刪除depthValue只寫寫透視深度。

選項1絕對是可怕的,即使我看到有人引用建議做這個教程。如果您寫入gl_FragDepth,您將會在很多硬件上禁用硬件深度緩衝區優化,這會使通過生成陰影貼圖的性能變差。

選項2比較好,因爲它是從硬件Z緩衝的優化好處,但它仍然需要大量的內存帶寬,因爲你正在有效地存儲兩個不同的深度值(一個在附色和一個深度附件)。

選項3,而最複雜的,也是通常表現最好的。這是因爲只需存儲硬件深度就可以將計算影像地圖所需的內存帶寬減半。

如果您有興趣瞭解有關選項3的更多信息,我建議您查看this related question。您將實際計算透視深度以用於比較,而不是比較距離。創建陰影貼圖時,在爲片段內存帶寬應用陰影時,您會在片段着色器中交換一些額外的計算。


我們解決您與工作最少的迫在眉睫的問題,您應該追求選項2。將選項3放在桌面上以供將來優化;最好不要優化事情,除非你至少有一些工作。

+0

感謝您的回答。選項3不是一個選項,因爲gl_FragDepth在es 2.0中不可訪問。正如你所建議我去選項2. – Terrordrone

+0

我改變了相關行glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X +面,0,GL_RGB,1024,768,0,GL_RGB,GL_UNSIGNED_SHORT_5_6_5,0);和glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,cubeTexture,0);但我仍然只有0,當我在第二個片段着色器中調用'紋理'功能。對不起,格式不好。 – Terrordrone

+0

我有類似的情況,我發現一些奇怪的東西。如果我只寫gl_FragDepth並且不在shadow fs中輸出任何內容,那麼看起來立方體貼圖根本不會得到任何東西。如果我要寫長度,我可以看到它被正確寫入紅色(我還附加了一個顏色附件)。這是爲什麼發生? – ChaoSXDemon