现代的OpenGL阴影cubemapping指针?(Pointers on modern OpenG

2019-07-17 11:52发布

背景

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

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

当前谅解

这是我的想法的一般理解,减去技术性。 请指正。

  • 对于每一个点光源,一个帧缓冲器被设置,如定向阴影贴图
  • 然后单个立方体贴图被生成,并且与结合glBindTexture(GL_TEXTURE_CUBE_MAP, shadowmap)
  • 该立方体贴图设置具有以下属性:

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

(这也类似于定向阴影贴图)

  • 现在glTexImage2D()经六次反复,一次为每个面。 我是这样做的:

      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); 
  • 纹理连接到帧缓存调用

     glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadowmap, 0); 
  • 当场景被呈现,其被呈现在两遍,像定向阴影映射。

  • 首先,影子帧缓冲区结合,视口被(在这种情况下1024 1024)调整到阴影贴图的大小。
  • 剔除被设置为前表面与glCullFace(GL_FRONT)
  • 活性着色器程序被切换到顶点和片段着色器影子,我将提供的源极进一步向下
  • 对于所有六个视图的光视图矩阵计算。 我这样做,通过创建GLM :: MAT4的和向量push_back()矩阵,就像这样:

     // 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); } } 
  • 默认的帧缓冲区是装订成册,现场通常绘制。

问题

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

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

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

影子的顶点着色器:

#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功能代码是从哪里来的,因为我是研究它在我的笔记本电脑,我不能去,现在,但这是那些着色器的结果是:

它是未投影空间中的小正方形通过阴影空间所包围。

我明明做了很多错在这里,很可能集中于我的着色器,由于关于这个问题,因为我觉得很难从任何东西,但教程学习知识的缺乏,我对此非常抱歉。 我很茫然,那将是美好的,如果有人能在这一个明确的解释什么,我做错了阐明,为什么这是错的,我怎么能解决这个问题,甚至一些代码。 我认为这个问题可能是因为我在错误的空间内工作。

Answer 1:

我希望提供一个答案,你的一些问题,但首先需要定义如下:

什么是一个立方体贴图?

它是从一个方向矢量的一对[上面对面,2D坐标],通过在假想的立方体突出方向矢量获得的地图。

什么是一个OpenGL立方体贴图?

这是一组六个“图像”。

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

这是从立方体贴图采样可以做到的原始采样。 这意味着它是使用方向矢量,而不是通常的纹理坐标采样。 然后硬件项目上的假想的立方体方向矢量,并使用所得到的[脸,2D纹理坐标]对以采样右“图像”在合适的2D位置。

什么是GLSL阴影取样?

这是一个采样器基元是有界包含NDC-空间深度值的纹理,并且当使用特定的阴影采样函数取样,返回一个NDC-空间深度之间的“比较”(在阴影图的相同的空间,显然)和NDC空间深度存储界的质感内部。 调用采样函数时进行比较的被指定为在纹理的附加元件的深度坐标。 注意提供了易用性和速度的采样影子,但它始终是可以做到的着色器中比较“手动”。


现在,你的问题:

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

不,OpenGL的渲染在当前帧缓冲区界一组目标。

在立方体贴图的情况下,他们能够使通常的方法是:

  • 创建它们和每个自己的六个“图像”连接到相同的帧缓冲( 在不同的连接点,很明显
  • 使只有目标的一次一个( 所以,你在每个渲染立方体贴图单独面对
  • 呈现在你的脸上立方体贴图想要的东西( 可能使用特有面子“视图”和“投影”矩阵

点光阴影贴图

除了一切谈到立方体贴图,还有一些使用它们来实现点光阴影映射等硬件的深度比较很少使用的问题。

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

  • 的而不是写NDC-空间深度,从点光写径向距离
  • 查询阴影地图时( 见底部示例代码 ):
    • 不使用硬件的深度比较(使用samplerCube而不是samplerCubeShadow)
    • 变换点在“立方体空间”将被测试(不包括投影在所有)
    • 使用“立方体空间”载体作为查找方向采样立方体贴图
    • 比较从所述立方体贴图采样与测试点的径向距离的径向距离

示例代码

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


文章来源: Pointers on modern OpenGL shadow cubemapping?