Cubemap阴影贴图不起作用

时间:2013-08-11 20:19:27

标签: opengl glsl shadow

我正在尝试在openGL 3.3版中创建全向/点光源。我在互联网和这个网站上搜索过,但到目前为止我还没有完成这个。根据我的理解,我应该

  

使用深度分量

生成帧缓冲区      

生成立方体贴图并将其绑定到所述帧缓冲区

     

绘制到立方体贴图的各个部分,如枚举GL_TEXTURE_CUBE_MAP _ *

所示。      

正常绘制场景,并将碎片的深度值与立方体贴图中的深度值进行比较

现在,我已经读过,最好使用从光到片段的距离,而不是存储片段深度,因为它可以更容易地查找立方体贴图(不需要检查每个单独的纹理? )

我目前的问题是,出来的光实际上是在球体中,并且不会产生阴影。另一个问题是帧缓冲区抱怨不完整,虽然我认为帧缓冲区在呈现纹理时不需要渲染缓冲区。

这是我的帧缓冲和立方体贴图初始化:

framebuffer = 0;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

glGenTextures(1, &shadowTexture);
glBindTexture(GL_TEXTURE_CUBE_MAP, shadowTexture);
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);GL_COMPARE_R_TO_TEXTURE);
for(int i = 0; i < 6; i++){
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i , 0,GL_DEPTH_COMPONENT16, 800, 800, 0,GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
}

glDrawBuffer(GL_NONE);

阴影顶点着色器

void main(){
    gl_Position = depthMVP * M* vec4(position,1);
    pos =(M * vec4(position,1)).xyz;
}

Shadow Fragment Shader

void main(){
    fragmentDepth = distance(lightPos, pos);
}

顶点着色器(无关位切出)

uniform mat4 depthMVP;
void main() {
    PositionWorldSpace = (M * vec4(position,1.0)).xyz;
    gl_Position = MVP * vec4(position, 1.0 );

    ShadowCoord = depthMVP * M* vec4(position, 1.0);
}

片段着色器(不相关的代码剪切)

uniform samplerCube shadowMap;
void main(){
    float bias = 0.005;
    float visibility = 1;
    if(texture(shadowMap, ShadowCoord.xyz).x < distance(lightPos, PositionWorldSpace)-bias)
        visibility = 0.1
}

现在你可能在想,什么是depthMVP?深度投影矩阵当前是在每个方向上具有范围[-10,10]的正交投影 好吧,他们的定义如下:

glm::mat4 depthMVP = depthProjectionMatrix* ??? *i->getModelMatrix();

这里的问题是我不知道???价值应该是。它曾经是相机矩阵,但我不确定它是否应该是它应该是什么。 然后对立方体贴图的边进行绘制代码,如下所示:

for(int loop = 0; loop < 6; loop++){
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X+loop, shadowTexture,0);
    glClear( GL_DEPTH_BUFFER_BIT);
    for(auto i: models){
        glUniformMatrix4fv(modelPos, 1, GL_FALSE, glm::value_ptr(i->getModelMatrix()));
        glm::mat4 depthMVP = depthProjectionMatrix*???*i->getModelMatrix();
        glUniformMatrix4fv(glGetUniformLocation(shadowProgram, "depthMVP"),1, GL_FALSE, glm::value_ptr(depthMVP));
        glBindVertexArray(i->vao);
        glDrawElements(GL_TRIANGLES, i->triangles, GL_UNSIGNED_INT,0);
    }
}

最后,场景得到了正常的绘制(我将为您提供详细信息)。在调用绘制立方体贴图之前,我将帧缓冲区设置为我之前生成的帧缓冲区,并将视口更改为800乘800.我将帧缓冲区更改回0并将视口重置为800 x 600,然后才进行常规绘制。任何有关此主题的帮助将不胜感激。

更新1

经过一些调整和错误修复后,这就是我得到的结果。我修正了一个错误,深度模式不能正常工作,我在这里绘制的是存储在立方体贴图中的距离。 http://imgur.com/JekOMvf

基本上发生的是它在每一侧绘制相同的单侧投影。这是有道理的,因为我们为每一侧使用相同的视图矩阵,但是我不确定我应该使用什么类型的视图矩阵。我认为它们应该是位于中心的lookAt()矩阵,并在立方体贴图方向看。然而,出现的问题是我应该如何在我的主要绘图调用中使用这些多个投影。

更新2

我已经开始创建这些矩阵,但我不确定它们的有效性(它们是从DX立方体贴图的网站上撕下来的,所以我颠倒了Z坐标)。

case 1://Negative X
    sideViews[i] = glm::lookAt(glm::vec3(0), glm::vec3(-1,0,0),glm::vec3(0,-1,0));
    break;
case 3://Negative Y
    sideViews[i] = glm::lookAt(glm::vec3(0), glm::vec3(0,-1,0),glm::vec3(0,0,-1));
    break;
case 5://Negative Z
    sideViews[i] = glm::lookAt(glm::vec3(0), glm::vec3(0,0,-1),glm::vec3(0,-1,0));
    break;
case 0://Positive X
    sideViews[i] = glm::lookAt(glm::vec3(0), glm::vec3(1,0,0),glm::vec3(0,-1,0));
    break;
case 2://Positive Y
    sideViews[i] = glm::lookAt(glm::vec3(0), glm::vec3(0,1,0),glm::vec3(0,0,1));
    break;
case 4://Positive Z
    sideViews[i] = glm::lookAt(glm::vec3(0), glm::vec3(0,0,1),glm::vec3(0,-1,0));
    break;

问题仍然存在,我应该将depthMVP视图部分转换为,因为这些是6个单独的矩阵。下面是目前的截图,使用相同的frag着色器(即实际渲染阴影)http://i.imgur.com/HsOSG5v.png 你可以看到阴影似乎很好,但定位显然是一个问题。我用来生成它的视图矩阵只是相机位置的反向平移(就像lookAt()函数那样)。

更新3

代码,目前的情况如下: 阴影顶点

void main(){
    gl_Position = depthMVP * vec4(position,1);
    pos =(M * vec4(position,1)).xyz;
}

暗影碎片

void main(){
    fragmentDepth = distance(lightPos, pos);
}

主要顶点

void main(){
    PositionWorldSpace = (M*vec4(position, 1)).xyz;
    ShadowCoord = vec4(PositionWorldSpace - lightPos, 1);
}

主要碎片

void main(){
    float texDist = texture(shadowMap, ShadowCoord.xyz/ShadowCoord.w).x;
    float dist = distance(lightPos, PositionWorldSpace);
    if(texDist < distance(lightPos, PositionWorldSpace)
        visibility = 0.1;
    outColor = vec3(texDist);//This is to visualize the depth maps
}

正在使用的透视矩阵

glm::mat4 depthProjectionMatrix = glm::perspective(90.f, 1.f, 1.f, 50.f);

目前一切都在运作,有点像。纹理存储的数据(即距离)似乎以奇怪的方式存储。它似乎是标准化的,因为所有值都在0和1之间。此外,在观察者周围有一个没有投影的1x1x1区域,但这是由于截锥体而且我认为很容易修复(如将相机偏移回到中心位置.5。

1 个答案:

答案 0 :(得分:3)

如果将片段深度保留为OpenGL,以确定您可以利用硬件分层Z优化。基本上,如果您在片段着色器中写入gl_FragDepth(不使用新的保守深度GLSL扩展),它会阻止称为分层Z的硬件优化。简称Hi-Z是一种技术,其中某些基元的光栅化可以在整个基元的深度值位于深度缓冲区中已有的值之后的基础上跳过。但它只有在着色器永远不会将任意值写入gl_FragDepth时才有效。

如果不是将片段与光线的距离写入立方体贴图,那么在编写阴影贴图时,您应该坚持传统的深度理论上应该获得更高的吞吐量(因为可以跳过遮挡的图元)。

然后,在您对深度立方体贴图进行采样的片段着色器中,您可以使用这样的代码片段将距离值转换为深度值(其中f和n是远和近平面距离你在创建深度立方体贴图时使用):


float VectorToDepthValue(vec3 Vec)
{
    vec3 AbsVec = abs(Vec);
    float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z));

    const float f = 2048.0;
    const float n = 1.0;
    float NormZComp = (f+n) / (f-n) - (2*f*n)/(f-n)/LocalZcomp;
    return (NormZComp + 1.0) * 0.5;
}

从SO问题借来的代码:Omnidirectional shadow mapping with depth cubemap

因此,将这些额外的代码应用到着色器中,可能会出现类似这样的情况:


void main () {
    float shadowDepth = texture(shadowMap, ShadowCoord.xyz/ShadowCoord.w).x;
    float testDepth   = VectorToDepthValue(lightPos - PositionWorldSpace);
    if (shadowDepth < testDepth)
        visibility = 0.1;
}