背景
我正在使用 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 x 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); } }
绑定了默认的framebuffer,正常绘制场景。
问题
现在,到着色器。这就是我的理解枯竭的地方。我完全不确定我应该做什么,我的研究似乎相互冲突,因为它适用于不同的版本。我最终从随机来源温和地复制和粘贴代码,并希望它能够实现除黑屏之外的其他功能。我知道这很糟糕,但似乎没有关于做什么的明确定义。我在哪些空间工作?我什至需要一个单独的阴影着色器,就像我在定向点照明中使用的那样?我到底用什么作为阴影立方体贴图的类型?采样立方体?采样立方体阴影?如何正确采样所述立方体贴图?我希望有人可以为我清除它并提供一个很好的解释。我目前对着色器部分的理解是: - 当场景被渲染到立方体贴图时,顶点着色器简单地采用我在 C++ 代码中计算的 depthMVP 统一,并通过它们转换输入顶点。- 立方体贴图传递的片段着色器只是将单出值分配给gl_FragCoord.z
. (这部分与我实现定向阴影贴图时没有变化。我认为立方体贴图是一样的,因为着色器甚至不与立方体贴图交互 - 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 函数代码是从哪里来的,因为我在我的笔记本电脑上研究它,我现在无法访问,但这是这些着色器的结果:
它是一个被阴影空间包围的无阴影空间的小正方形。
我显然在这里做错了很多,可能集中在我的着色器上,由于缺乏对该主题的了解,因为我发现除了教程之外很难从任何东西中学习,对此我感到非常抱歉。我很茫然,如果有人可以清楚地解释我做错了什么,为什么它错了,我该如何解决它,甚至可能是一些代码,那就太好了。我认为问题可能是因为我在错误的空间工作。