jsh*_*hxe 18 c++ opengl 3d shader shadow-mapping
我正在使用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);
Run Code Online (Sandbox Code Playgroud)(这也类似于定向阴影贴图)
现在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);
Run Code Online (Sandbox Code Playgroud)通过调用将纹理附加到帧缓冲区
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadowmap, 0);
Run Code Online (Sandbox Code Playgroud)当要渲染场景时,它将以两个过程渲染,如定向阴影贴图.
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);
}
}
Run Code Online (Sandbox Code Playgroud)绑定默认帧缓冲区,并正常绘制场景.
现在,对着色器.这是我的理解枯竭的地方.我完全不确定我应该做什么,我的研究似乎与彼此冲突,因为它适用于不同的版本.我最后简单地从随机源中复制和粘贴代码,并希望它能实现除黑屏之外的其他功能.我知道这很糟糕,但似乎没有任何明确的定义.我在哪些空间工作?我是否甚至需要一个单独的阴影着色器,就像我在定向点光源中使用的那样?我到底用什么作为阴影立方体贴图的类型?samplerCube?samplerCubeShadow?如何正确地对所述立方体贴图进行采样?我希望有人可以为我清理它并提供一个很好的解释.我目前对着色器部分的理解是: - 当场景被渲染到立方体贴图中时,顶点着色器简单地采用我在C++代码中计算的depthMVP均匀,并通过它们转换输入顶点.- 立方体贴图传递的片段着色器只是将单个输出值分配给gl_FragCoord.z.(这部分与我实现定向阴影贴图时没有变化.我认为它对于立方体贴图是相同的,因为着色器甚至不与立方体贴图交互 - OpenGL只是将它们的输出渲染到立方体贴图,对吧?因为它是一个帧缓冲区?)
这不是一个理解.关于顶点如何变换并用于查找立方体贴图,我一言不发,所以我将粘贴我的着色器的源代码,希望人们可以澄清这一点.请注意,很多代码都是盲目复制和粘贴,我没有改变任何内容以免危害任何理解.
#version 150
in vec3 position;
uniform mat4 depthMVP;
void main()
{
gl_Position = depthMVP * vec4(position, 1);
}
Run Code Online (Sandbox Code Playgroud)
#version 150
out float fragmentDepth;
void main()
{
fragmentDepth = gl_FragCoord.z;
}
Run Code Online (Sandbox Code Playgroud)
#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);
}
Run Code Online (Sandbox Code Playgroud)
#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);
}
Run Code Online (Sandbox Code Playgroud)
我不记得ComputerShadowFactor和VectorToDepthValue函数代码来自哪里,因为我正在我的笔记本电脑上研究它,我现在无法做到,但这是这些着色器的结果:

这是一个被阴影空间包围的小阴影空间.
我显然在这里做了很多错,可能是以我的着色器为中心,由于缺乏关于这个主题的知识,因为我发现除了教程之外我很难学到任何东西,我很抱歉.我不知道如果有人能够清楚地解释我做错了什么,为什么它错了,我怎么能解决它甚至一些代码,这将是一件好事.我认为这个问题可能是因为我在错误的地方工作.
Gig*_*igi 11
我希望为您的一些问题提供答案,但首先需要一些定义:
什么是立方体贴图?
它是从方向矢量到一对[面部,该面上的2d坐标]的映射,通过将方向矢量投影到假设立方体上而获得.
什么是OpenGL立方体贴图纹理?
这是一组六个"图像".
什么是GLSL立方体贴图采样器?
它是一个采样器原语,可以从中进行立方体贴图采样.这意味着它使用方向向量而不是通常的纹理坐标进行采样.然后硬件将方向矢量投影到假想的立方体上,并使用得到的[面部,2d纹理坐标]对在右侧2d位置采样右图像.
什么是GLSL影子采样器?
它是一个采样器原语,它被绑定到包含NDC空间深度值的纹理,并且当使用阴影特定的采样函数进行采样时,返回NDC空间深度之间的"比较"(在阴影贴图的相同空间中,显然)和NDC空间深度存储在有界纹理内.在调用采样函数时,要比较的深度被指定为纹理坐标中的附加元素.请注意,提供阴影采样器是为了便于使用和提高速度,但始终可以在着色器中"手动"进行比较.
现在,针对您的问题:
OpenGL只是渲染到立方体贴图,对吗?
不,OpenGL渲染到当前有界帧缓冲区中的一组目标.
在cubemaps的情况下,渲染它们的常用方法是:
点光阴影贴图
除了关于立方体贴图的所有内容之外,使用它们来实现点光阴影贴图还存在许多问题,因此很少使用硬件深度比较.
相反,常见的实践是:
示例代码
// 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;
Run Code Online (Sandbox Code Playgroud)