立方体贴图阴影贴图不起作用 [英] Cubemap shadow mapping not working

查看:137
本文介绍了立方体贴图阴影贴图不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在openGL版本3.3中创建全向/点光源.我已经在互联网和该站点上进行了搜索,但是到目前为止,我还无法完成此任务.据我了解,我应该

I'm attempting to create omnidirectional/point lighting in openGL version 3.3. I've searched around on the internet and this site, but so far I have not been able to accomplish this. From my understanding, I am supposed to

使用深度分量生成帧缓冲区

Generate a framebuffer using depth component

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

Generate a cubemap and bind it to said framebuffer

绘制枚举GL_TEXTURE_CUBE_MAP_ *所引用的立方体贴图的各个部分

Draw to the individual parts of the cubemap as refrenced by the enums GL_TEXTURE_CUBE_MAP_*

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

Draw the scene normally, and compare the depth value of the fragments against those in the cubemap

现在,我已经读到最好使用光线到碎片的距离,而不是存储碎片深度,因为这样可以更轻松地查找立方体贴图(不需要检查每个单独的纹理吗? )

Now, I've read that it is better to use distances from the light to the fragment, rather than to store the fragment depth, as it allows for easier cubemap look up (something about not needing to check each individual texture?)

我当前的问题是发出的光实际上在一个球体中,并且不会产生阴影.另一个问题是,尽管我感觉帧缓冲器如果渲染为纹理,则不需要渲染缓冲器,但它却抱怨帧缓冲器不完整.

My current issue is that the light that comes out is actually in a sphere, and does not generate shadows. Another issue is that the framebuffer complains of not being complete, although I was under the impression that a framebuffer does not need a renderbuffer if it renders to a texture.

这是我的帧缓冲区和多维数据集映射初始化:

Here is my framebuffer and cube map initialization:

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]范围的正交投影 好吧,它们的定义如下:

Now as you are probably thinking, what is depthMVP? Depth projection matrix is currently an orthogonal projection with the ranges [-10, 10] in each direction Well they are defined like so:

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

这里的问题是我不知道是什么???价值应该是.它曾经是相机矩阵,但是我不确定这是否应该是它. 然后像下面这样对立方体贴图的两边完成绘制代码:

The issue here is that I don't know what the ??? value is supposed to be. It used to be the camera matrix, however I am unsure if that is what it is supposed to be. Then the draw code is done for the sides of the cubemap like so:

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 x800.在进行普通绘制之前,将帧缓冲区更改回0并将视口重置为800 x 600.在这个问题上的任何帮助将不胜感激.

Finally the scene gets drawn normally (I'll spare you the details). Before the calls to draw onto the cubemap I set the framebuffer to the one that I generated earlier, and change the viewport to 800 by 800. I change the framebuffer back to 0 and reset the viewport to 800 by 600 before I do normal drawing. Any help on this subject will be greatly appreciated.

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

After some tweaking and bug fixing, this is the result I get. I fixed an error with the depthMVP not working, what I am drawing here is the distance that is stored in the cubemap. http://imgur.com/JekOMvf

基本上发生的是,它在每一侧都绘制相同的一侧投影.这是有道理的,因为我们在每一侧都使用相同的视图矩阵,但是我不确定应该使用哪种视图矩阵.我认为它们应该是位于中间的lookAt()矩阵,并在立方体贴图侧面的方向上向外看.但是,出现的问题是我应该如何在主抽奖电话中使用这些多个投影.

Basically what happens is it draws the same one sided projection on each side. This makes sense since we use the same view matrix for each side, however I am not sure what sort of view matrix I am supposed to use. I think they are supposed to be lookAt() matrices that are positioned at the center, and look out in the cube map side's direction. However, the question that arises is how I am supposed to use these multiple projections in my main draw call.

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

I've gone ahead and created these matrixes, however I am unsure of how valid they are (they were ripped from a website for DX cubemaps, so I inverted the Z coord).

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()函数那样).

The question still stands, what I am supposed to translate the depthMVP view portion by, as these are 6 individual matrices. Here is a screenshot of what it currently looks like, with the same frag shader (i.e. actually rendering shadows) http://i.imgur.com/HsOSG5v.png As you can see the shadows seem fine, however the positioning is obviously an issue. The view matrix that I used to generate this was just an inverse translation of the position of the camera (as the lookAt() function would do).

代码,目前的样子: 阴影顶点

Code, as it currently stands: 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区域,但这是由于平截头体造成的,我认为这很容易修复(例如将相机向后偏移.5到中心).

Everything is currently working, sort of. The data that the texture stores (i.e. the distance) seems to be stored in a weird manner. It seems like it is normalized, as all values are between 0 and 1. Also, there is a 1x1x1 area around the viewer that does not have a projection, but this is due to the frustum and I think will be easy to fix (like offsetting the cameras back .5 into the center).

推荐答案

如果将片段深度留给OpenGL来确定,则可以利用硬件分层Z优化.基本上,如果您曾经在片段着色器中写入gl_FragDepth(不使用新的保守深度GLSL扩展名),则会阻止称为分层Z的硬件优化.Hi-Z简而言之,是一种可以跳过某些基元的光栅化的技术.基于整个图元的深度值位于深度缓冲区中已经存在的值的后面.但是,只有在您的着色器从不向gl_FragDepth写入任意值的情况下,它才起作用.

If you leave the fragment depth to OpenGL to determine you can take advantage of hardware hierarchical Z optimizations. Basically, if you ever write to gl_FragDepth in a fragment shader (without using the newfangled conservative depth GLSL extension) it prevents hardware optimizations called hierarchical Z. Hi-Z, for short, is a technique where rasterization for some primitives can be skipped on the basis that the depth values for the entire primitive lies behind values already in the depth buffer. But it only works if your shader never writes an arbitrary value to gl_FragDepth.

如果您不使用传统的深度来代替从光线到立方体贴图的距离,而是在理论上应该在编写阴影贴图时获得更高的吞吐量(因为可以跳过被遮挡的基元).

If instead of writing a fragment's distance from the light to your cube map, you stick with traditional depth you should theoretically get higher throughput (as occluded primitives can be skipped) when writing your shadow maps.

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

Then, in your fragment shader where you sample your depth cube map, you would convert the distance values into depth values by using a snippet of code like this (where f and n are the far and near plane distances you used when creating your depth cube map):



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问题中借用的代码:具有深度立方体贴图的全向阴影贴图

Code borrowed from SO question: Omnidirectional shadow mapping with depth cubemap

因此,将额外的代码应用于着色器,它将可以解决如下问题:

So applying that extra bit of code to your shader, it would work out to something like this:


void main(){
    浮动shadowDepth = texture(shadowMap,ShadowCoord.xyz/ShadowCoord.w).x;
    float testDepth = VectorToDepthValue(lightPos-PositionWorldSpace);
    如果(shadowDepth< testDepth)
        可见度= 0.1;
}


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

这篇关于立方体贴图阴影贴图不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆