0

我正在尝试在 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;
}

阴影片段着色器

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 x 800。我将帧缓冲区更改回 0 并将视口重置为 800 x 600,然后再进行正常绘图。对此主题的任何帮助将不胜感激。

更新 1

经过一些调整和错误修复,这是我得到的结果。我修复了深度MVP不起作用的错误,我在这里绘制的是存储在立方体贴图中的距离。 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 个单独的矩阵。这是它当前的截图,使用相同的片段着色器(即实际渲染阴影)http://i.imgur.com/HsOSG5v.png 正如您所见,阴影看起来不错,但定位显然是问题。我用来生成它的视图矩阵只是相机位置的逆转换(就像 lookAt() 函数一样)。

更新 3

目前的代码:Shadow Vertex

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 区域没有投影,但这是由于截头锥,我认为很容易修复(比如将相机向后偏移 0.5 到中心)。

4

1 回答 1

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;
}

于 2013-08-12T20:46:41.837 回答