指针现代OpenGL的阴影cubemapping? [英] Pointers on modern OpenGL shadow cubemapping?

查看:244
本文介绍了指针现代OpenGL的阴影cubemapping?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用C ++和现代的OpenGL(3.3)在3D游戏的工作。我现在工作的灯光和阴影渲染,我已经成功地实施定向阴影映射。看完以上的游戏我已经决定,我会需要点光源阴影映射的要求。之后做一些研究,我发现,做全方位的阴影映射,我会做同样的定向阴影映射的东西,但有一个立方体来代替。

I am working on a 3D game using C++ and modern OpenGL (3.3). I am now working on the lighting and shadow rendering, and I've successfully implemented directional shadow mapping. After reading over the requirements for the game I have decided that I'd be needing point light shadow mapping. After doing some research, I discovered that to do omnidirectional shadow mapping I will do something similar to directional shadow mapping, but with a cubemap instead.

我立方贴图没有previous知识,但我对他们的理解是,一个立方体贴图为六纹理,无缝连接。 我做了一些环顾四周,但不幸的是我在努力寻找一个明确的教程对现代OpenGL的主题。我找教程:第一,从开始到结束,解释它,因为我认真地挣扎着从源头code片段或只是概念的学习,但我尽力了。

I have no previous knowledge of cubemaps but my understanding of them is that a cubemap is six textures, seamlessly attached. I did some looking around but unfortunately I struggled to find a definitive "tutorial" on the subject for modern OpenGL. I look for tutorials first that explain it from start to finish because I seriously struggled to learn from snippets of source code or just concepts, but I tried.

下面是我的一般理解的想法,减去的技术问题。请大家指正。

Here is my general understanding of the idea, minus the technicalities. Please correct me.

  • 对于每一个光点,一帧缓冲区设置,如定向shadowmapping
  • 在一个单独的立方体贴图,然后生成,并绑定与 glBindTexture(GL_TEXTURE_CUBE_MAP,阴影贴图)
  • 该立方体设置具有以下属性:

  • For each point light, a framebuffer is set up, like directional shadowmapping
  • A single cubemap texture is then generated, and bound with glBindTexture(GL_TEXTURE_CUBE_MAP, shadowmap).
  • The cubemap is set up with the following attributes:

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

(这也是类似于定向shadowmapping)

(this is also similar to directional shadowmapping)

  • 现在 glTexImage2D()通过六次面对每一个迭代一次。我是这样做的:

  • Now glTexImage2D() is iterated through six times, once for each face. I do that like this:

 for (int face = 0; face < 6; face++) // Fill each face of the shadow cubemap
     glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_DEPTH_COMPONENT32F , 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);

  • 的纹理被附加到帧缓冲区通过调用

  • The texture is attached to the framebuffer with a call to

    glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadowmap, 0);
    

  • 在现场被渲染,它呈现在两次传球,像定向阴影映射。

  • When the scene is to be rendered, it is rendered in two passes, like directional shadow mapping.

    光视图矩阵的所有六个视图计算。我这样做是通过创建GLM :: mat4的载体和的push_back()矩阵,像这样的:

    The light view matrices for all six views are calculated. I do it by creating a vector of glm::mat4's and push_back() the matrices, like this:

    // Create the six view matrices for all six sides
    for (int i = 0; i < renderedObjects.size(); i++) // Iterate through all rendered objects
    {
        renderedObjects[i]->bindBuffers(); // Bind buffers for rendering with it
    
        glm::mat4 depthModelMatrix = renderedObjects[i]->getModelMatrix(); // Set up model matrix
    
        for (int i = 0; i < 6; i++) // Draw for each side of the light
        {
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, shadowmap, 0);
            glClear(GL_DEPTH_BUFFER_BIT); // Clear depth buffer
    
            // Send MVP for shadow map
            glm::mat4 depthMVP = depthProjectionMatrix * depthViewMatrices[i] * depthModelMatrix;
            glUniformMatrix4fv(glGetUniformLocation(shadowMappingProgram, "depthMVP"), 1, GL_FALSE, glm::value_ptr(depthMVP));
    
            glUniformMatrix4fv(glGetUniformLocation(shadowMappingProgram, "lightViewMatrix"), 1, GL_FALSE, glm::value_ptr(depthViewMatrices[i]));
            glUniformMatrix4fv(glGetUniformLocation(shadowMappingProgram, "lightProjectionMatrix"), 1, GL_FALSE, glm::value_ptr(depthProjectionMatrix));
            glDrawElements(renderedObjects[i]->getDrawType(), renderedObjects[i]->getElementSize(), GL_UNSIGNED_INT, 0);
        }
    }
    

  • 默认帧缓冲区绑定,场面通常绘制。

  • The default framebuffer is bound, and the scene is drawn normally.

    现在,到着色器。这是我的理解枯竭。我对我应该做的完全不确定,我的研究似乎与海誓山盟冲突,因为它是针对不同的版本。最后我只是淡淡地复制和随机来源粘贴code,并希望它会取得比黑屏其他的东西。我知道这是可怕的,但似乎并没有要什么做任何明确的定义。我的工作是什么空间?难道我甚至需要一个单独的阴影着色器,就像我以前在定向点照明?我作为一个影子立方体贴图类型使用什么玩意? samplerCube? samplerCubeShadow?我怎么样说,立方体贴图正确?我希望有人可以清除它为我提供一个很好的解释。 我现在的着色器部分的理解是:   - 当场景被渲染到立方体贴图,顶点着色器只是需要我计算我的C ++ code中的depthMVP统一,他们将输入的顶点。   - 在立方体通的片段着色器简单地分配挑出值到 gl_FragCoord.z​​ 。 (这部分是从什么时候我实现了定向阴影映射不变,我认为,因为着色器甚至不与立方体贴图互动,将是相同的cubemapping - OpenGL的只是呈现向他们输出到立方体贴图,正确的,因为它是一个?帧缓冲区?)

    Now, to the shaders. This is where my understanding runs dry. I am completely unsure on what I should do, my research seems to conflict with eachother, because it's for different versions. I ended up blandly copying and pasting code from random sources, and hoping it'd achieve something other than a black screen. I know this is terrible, but there doesn't seem to be any clear definitions on what to do. What spaces do I work in? Do I even need a separate shadow shader, like I used in directional point lighting? What the hell do I use as the type for a shadow cubemap? samplerCube? samplerCubeShadow? How do I sample said cubemap properly? I hope that someone can clear it up for me and provide a nice explanation. My current understanding of the shader part is: - When the scene is being rendered into the cubemap, the vertex shader simply takes the depthMVP uniform I calculated in my C++ code and transforms the input vertices by them. - The fragment shader of the cubemap pass simply assigns the single out value to the gl_FragCoord.z. (This part is unchanged from when I implemented directional shadow mapping. I assumed it would be the same for cubemapping because the shaders don't even interact with the cubemap - OpenGL simply renders the output from them to the cubemap, right? Because it's a framebuffer?)

    • 在顶点着色器的正常渲染是不变的。
    • 在片段着色器的正常渲染,顶点位置转化为光的空间与光的投影和视图矩阵。
    • 这是莫名其妙地用在立方体贴图查找。 ???
    • 一旦深度一直使用神奇手段检索到,它是相对于光到顶点的距离,很象定向shadowmapping。如果是少,这一点必须被隐藏,反之亦然。

    这不是多大的理解。我去的空白,如何顶点变换和用于查找的立方体贴图,所以我要粘贴的来源为我着色器,在希望人们能够澄清这一点。请注意,很多这code是盲目的复制和粘贴,我没有改变任何东西,以不损害任何的了解。

    It's not much of an understanding. I go blank as to how the vertices are transformed and used to lookup the cubemap, so I'm going to paste the source for my shaders, in hope that people can clarify this. Please note that a lot of this code is blind copying and pasting, I haven't altered anything as to not jeopardise any understanding.

    #version 150
    
    in vec3 position;
    
    uniform mat4 depthMVP;
    
    void main()
    {
        gl_Position = depthMVP * vec4(position, 1);
    }
    

    阴影片段着色器:

    #version 150
    
    out float fragmentDepth;
    
    void main()
    {
        fragmentDepth = gl_FragCoord.z;
    }
    

    标准顶点着色器:

    #version 150
    
    in vec3 position;
    in vec3 normal;
    in vec2 texcoord;
    
    uniform mat3 modelInverseTranspose;
    uniform mat4 modelMatrix;
    uniform mat4 viewMatrix;
    uniform mat4 projectionMatrix;
    
    out vec3 fragnormal;
    out vec3 fragnormaldirection;
    out vec2 fragtexcoord;
    out vec4 fragposition;
    out vec4 fragshadowcoord;
    
    void main()
    {
        fragposition = modelMatrix * vec4(position, 1.0);
        fragtexcoord = texcoord;
        fragnormaldirection = normalize(modelInverseTranspose * normal);
        fragnormal = normalize(normal);
        fragshadowcoord = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
    
    
        gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
    }
    

    标准片段着色器:

    #version 150
    
    out vec4 outColour;
    
    in vec3 fragnormaldirection;
    in vec2 fragtexcoord;
    in vec3 fragnormal;
    in vec4 fragposition;
    in vec4 fragshadowcoord;
    
    uniform mat4 modelMatrix;
    uniform mat4 viewMatrix;
    uniform mat4 projectionMatrix;
    uniform mat4 viewMatrixInversed;
    
    uniform mat4 lightViewMatrix;
    uniform mat4 lightProjectionMatrix;
    
    uniform sampler2D tex;
    uniform samplerCubeShadow shadowmap;
    
    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;
    }
    
    float ComputeShadowFactor(samplerCubeShadow ShadowCubeMap, vec3 VertToLightWS)
    {   
        float ShadowVec = texture(ShadowCubeMap, vec4(VertToLightWS, 1.0));
        if (ShadowVec + 0.0001 > VectorToDepthValue(VertToLightWS)) // To avoid self shadowing, I guess
            return 1.0;
    
        return 0.7;
    }
    
    void main()
    {
        vec3 light_position = vec3(0.0, 0.0, 0.0);
        vec3 VertToLightWS = light_position - fragposition.xyz;
        outColour = texture(tex, fragtexcoord) * ComputeShadowFactor(shadowmap, VertToLightWS);
    }
    

    我不记得在那里的ComputerShadowFactor和VectorToDepthValue功能code来的,因为我是在我的笔记本电脑,我不能去,现在研究它,但是这是这些着色器的结果:

    I can't remember where the ComputerShadowFactor and VectorToDepthValue function code came from, because I was researching it on my laptop which I can't get to right now, but this is the result of those shaders:

    这是一个小广场无暗影空间阴影的空间包围。

    It is a small square of unshadowed space surrounded by shadowed space.

    我明明做了很多错在这里,可能是集中在我的着色器,因为关于这个问题,因为我觉得很难从什么,但辅导学习它缺乏知识,我非常后悔的。我很茫然,那将是美好的,如果有人能阐明这光解释清楚什么我做错了,为什么这是错的,我怎么能解决这个问题,甚至一些code。我认为这个问题可能是因为我在错误的空间工作。

    I am obviously doing a lot wrong here, probably centered on my shaders, due to a lack of knowledge on the subject because I find it difficult to learn from anything but tutorials, and I am very sorry for that. I am at a loss it it would be wonderful if someone can shed light on this with a clear explanation on what I am doing wrong, why it's wrong, how I can fix it and maybe even some code. I think the issue may be because I am working in the wrong spaces.

    推荐答案

    我希望能解答你的一些问题,但首先一些定义是必需的:

    I hope to provide an answer to some of your questions, but first some definitions are required:

    什么是立方体?

    有从一个方向矢量的一对的地图〔面,在该面的二维坐标],通过把方向矢量上的假想立方体获得

    It is a map from a direction vector to a pair of [face, 2d coordinates on that face], obtained by projecting the direction vector on an hypothetical cube.

    什么是OpenGL的立方体贴图?

    这是一套六形象。

    什么是GLSL立方体贴图取样器?

    这是一个采样器从立方体采样可以做到的原始。这意味着,它被使用的方向矢量,而不是通常的纹理坐标采样。硬件然后投射在一个假想的立方体的方向向量,然后用这个[脸,2D纹理坐标]对来样的权利形象在合适的二维位置。

    It is a sampler primitive from which cubemap sampling can be done. This mean that it is sampled using a direction vector instead of the usual texture coordinates. The hardware then project the direction vector on an hypothetical cube and use the resulting [face, 2d texture coordinate] pair to sample the right "image" at the right 2d position.

    什么是GLSL的阴影采样?

    有一个采样器原语,为界包含NDC-空间深度值的纹理,并且当使用影子特定的抽样函数取样,返回NDC-空间深度之间的比较(在的同一空间阴影贴图,很明显),并存储在有限的纹理内的NDC空间的深度。调用采样函数时进行比较的被指定为在纹理的附加元件的深度坐标。注意提供了易用性和速度的采样的影子,但它总是可以在着色器手动做比较。

    It is a sampler primitive that is bounded to a texture containing NDC-space depth values and, when sampled using the shadow-specific sampling functions, return a "comparison" between a NDC-space depth (in the same space of the shadow map, obviously) and the NDC-space depth stored inside the bounded texture. The depth to compare against is specified as an additional element in the texture coordinates when calling the sampling function. Note that shadow samplers are provided for ease of use and speed, but it is always possible to do the comparison "manually" in the shader.

    现在,你的问题:

    的OpenGL只是呈现[...]的立方体贴图,对吧?

    没有时,OpenGL渲染到在当前界帧缓冲区一组目标。

    No, OpenGL render to a set of targets in the currently bounded framebuffer.

    在立方贴图的情况下,通常的方式在它们来渲染是:

    In the case of cubemaps, the usual way to render in them is:

    • 来创建它们和他们的每一个六人形象连接到同一个 帧缓冲(在不同的附着点,显然的)
    • 要在同一时间只允许目标之一(这样,你在每个渲染立方体贴图面的<​​em>单独的)
    • 来呈现你想要的立方体贴图面(可能使用脸部专用的视图和投影矩阵的)
    • to create them and attach each of their six "images" to the same framebuffer (at different attachment points, obviously)
    • to enable only one of the target at a time (so, you render in each cubemap face individually)
    • to render what you want in the cubemap face (possibly using face-specific "view" and "projection" matrices)

    点光阴影贴图

    在除了所说的一切关于立方贴图,也有使用它们来实现点光阴影映射了一些问题,因此在硬件深度比较很少使用。

    In addition to everything said about cubemaps, there are a number of problems in using them to implement point-light shadow mapping and so the hardware depth comparison is rarely used.

    相反,什么是共同的初步实践如下:

    Instead, what is common pratice is the following:

    • ,而不是写NDC空间的深度,写的径向距离 点光源
    • 查询阴影图时(看样code底部的):
      • 在不使用硬件的深度比较(使用samplerCube代替samplerCubeShadow)
      • 变换点中的立方体空间进行试验(不包括投影在所有)
      • 使用魔方空间向量作为查找方向采样立方体贴图
      • 比较从立方体抽样与检测点的径向距离的径向距离
      • instead of writing NDC-space depth, write radial distance from the point light
      • when querying the shadow map (see sample code at bottom):
        • do not use hardware depth comparisons (use samplerCube instead of samplerCubeShadow)
        • transform the point to be tested in the "cube space" (that do not include projection at all)
        • use the "cube-space" vector as the lookup direction to sample the cubemap
        • compare the radial distance sampled from the cubemap with the radial distance of the tested point

        样品code

        // sample radial distance from the cubemap
        float radial_dist = texture(my_cubemap, cube_space_vector).x;
        
        // compare against test point radial distance
        bool shadowed = length(cube_space_vector) > radial_dist;
        

        这篇关于指针现代OpenGL的阴影cubemapping?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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