OpenGL Shadow Map 移动版不工作
OpenGL Shadow Map Mobile Version Doesn't work
总结
我有一个问题 移植 我的基于 OpenGL 3.3 ("desktop") 的游戏引擎到 OpenGL ES 3.2 ("mobile"). 在桌面上一切正常,在移动设备上一切正常但阴影贴图。
我的问题很简单:有人可以在我分享的代码中发现问题,或者指出正确的方向吗?
上下文
- 我所有的引擎代码都是用C
写的
- 除了应用程序层(我在桌面上使用 glfw,在移动设备上使用 glfm)之外,代码库是相同的:为桌面和移动设备编译的代码完全相同。
- 着色器 - 除了像#version 和精度默认值一样注入headers - 在平台之间也是相同的
- 我引擎中的阴影贴图系统基于此处找到的教程:https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows
- 我在桌面上使用 NVIDIA GTX 1080 进行开发,也在其他 NVIDIA PC 上进行了测试
- 我正在使用全新的ASUS ROG II Phone和旧的LG G5进行测试,两者都运行 Android 9,根据[=,两者都支持OpenGL ES 3.2 143=] 应用程序和 都显示完全相同的错误结果。
截图
我的非常简单的灯光测试关卡在桌面上正确呈现。中间的柱子右侧有一个黄色(不可见)光,因此阴影应该投射到左侧,如下面的桌面截图所示:
但是在手机上就大错特错了:
如您所见,手机上的影子是"just wrong"。它应该像在桌面上一样在左边,但它的方向错了,看起来 "chopped off".
我自己的想法和行动
- 我非常小心地确保我的所有调用在 OpenGL ES 3.2 上可用,就像在 OpenGL 3.3 Core 上一样
- 我非常小心地确保 OpenGL 调用没有问题:我正在使用 glDebugMessageCallback 并且没有错误报告任何时候
- 自从它 "just works" 在桌面上,我觉得它背后的数学并没有错
- 我的引擎支持缓存远光的阴影贴图作为性能优化,但禁用它没有任何效果,所以它不会导致问题。
(希望)相关代码
C 中的阴影贴图设置
创建 CUBE MAP 纹理以保持阴影的代码:
static GLuint r_createShadowmapTexture() {
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_CUBE_MAP, tex);
for (unsigned int i = 0; i < 6; ++i)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT32F, r_shadowSize, r_shadowSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
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);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
return tex;
}
为阴影贴图设置帧缓冲区的代码:
glGenFramebuffers(1, &r_shadowDepthMapFBO);
glBindFramebuffer(GL_FRAMEBUFFER, r_shadowDepthMapFBO);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
渲染阴影贴图的代码:
static struct {
vec3 centerOffset, up;
} const g_shadowMapTransforms[6] = {
{ {1.0f, 0.0f, 0.0f}, {0.0f, -1.0f, 0.0f} },
{ {-1.0f, 0.0f, 0.0f}, {0.0f, -1.0f, 0.0f} },
{ {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f} },
{ {0.0f, -1.0f, 0.0f}, {0.0f, 0.0f, -1.0f} },
{ {0.0f, 0.0f, 1.0f}, {0.0f, -1.0f, 0.0f} },
{ {0.0f, 0.0f, -1.0f}, {0.0f, -1.0f, 0.0f} },
};
static void r_shadowMapPass(const point_light_t* light, GLuint cubemapTexture) {
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glDepthMask(GL_TRUE);
glBindFramebuffer(GL_FRAMEBUFFER, r_shadowDepthMapFBO);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cubemapTexture, 0);
const float farPlane = min(r_far, light->radius);
mat4x4 shadowProj;
mat4x4_perspective(shadowProj, (float)M_PI_2, 1.0f, r_near, farPlane);
mat4x4 shadowVP[6];
for (int i = 0; i < 6; ++i) {
vec3 center;
vec3_add(center, light->origin, g_shadowMapTransforms[i].centerOffset);
mat4x4 view;
mat4x4_look_at(view, (float*)light->origin, center, (float*)g_shadowMapTransforms[i].up);
mat4x4_mul(shadowVP[i], shadowProj, view);
}
rtech_shadowmap_enable();
rtech_shadowmap_setLightPos((float*)light->origin);
rtech_shadowmap_setFarPlane(farPlane);
rtech_shadowmap_setShadowMatrices(shadowVP);
render_entity_t* re = r_entities;
glClear(GL_DEPTH_BUFFER_BIT);
for (uint32_t i = 0; i < r_numEntities; ++i, ++re) {
if (!re->castsShadow)
continue;
rtech_shadowmap_setModelMatrix(re->m);
if (re->hasSolid) {
const render_solids_t* const rs = r_solids + re->index;
for (uint32_t j = 0; j < rs->count; ++j) {
const uint32_t ao = rs->offset + j;
r_renderBlock(ao, rs, j);
}
} else {
render_model_t* const rm = &r_models[re->index];
for (uint32_t j = 0; j < rm->count; ++j)
r_renderArrayObject(rm->arrayObjects[j], rm->numIndices[j]);
}
}
glDepthMask(GL_FALSE);
}
在桌面上,我为所有着色器(顶点、几何体和片段)添加以下前缀:
#version 330 core
在移动设备上,我为所有着色器添加以下前缀:
#version 320 es
precision highp float;
precision highp int;
precision highp sampler2D;
precision highp samplerCubeShadow;
阴影贴图顶点着色器:
layout (location = 0) in vec3 aPos;
uniform mat4 gModel;
void main()
{
gl_Position = gModel * vec4(aPos, 1.0);
}
阴影贴图几何着色器:
layout (triangles) in;
layout (triangle_strip, max_vertices=18) out;
uniform mat4 gShadowMatrices[6];
out vec4 FragPos; // FragPos from GS (output per emitvertex)
void main()
{
for(int face = 0; face < 6; ++face)
{
gl_Layer = face; // built-in variable that specifies to which face we render.
for(int i = 0; i < 3; ++i) // for each triangle's vertices
{
FragPos = gl_in[i].gl_Position;
gl_Position = gShadowMatrices[face] * FragPos;
EmitVertex();
}
EndPrimitive();
}
}
阴影贴图片段着色器:
in vec4 FragPos;
uniform vec3 gLightPos;
uniform float gFarPlane;
void main()
{
// get distance between fragment and light source
float lightDistance = length(FragPos.xyz - gLightPos);
// map to [0;1] range by dividing by gFarPlane
lightDistance = lightDistance / gFarPlane;
// write this as modified depth
gl_FragDepth = lightDistance;
}
实际使用阴影贴图的点光通道的摘录:
...
uniform samplerCubeShadow gShadowCubeMap;
uniform float gFarPlane;
...
float ShadowCalcSinglePass(vec3 fragPos, vec3 lightPos) {
vec3 fragToLight = fragPos - lightPos;
float currentDepth = (length(fragToLight) - (0.005 * gFarPlane)) / gFarPlane;
return texture(gShadowCubeMap, vec4(fragToLight, currentDepth));
}
我知道这是一个非常开放的问题,涉及到很多代码。我正在尝试提供必要的相关信息,如果需要或要求,我可以分享更多!
更新 1
根据@MichaelKenzel 的建议,我尝试将阴影贴图渲染到屏幕上。这非常适合桌面,如下所示。然而,在移动设备上,这显示了一个 "all red" 阴影贴图,其中(因为我的着色器执行 1.0 - 样本)texture() 函数为每个像素返回 0。但是,如上图 "wrong" 所示,某处有阴影,因此以这种方式从深度缓冲区读取时似乎存在问题。
注意:下图来自与上图不同的测试级别,具有更复杂的阴影
in vec2 TexCoord0;
uniform samplerCube gTexture;
uniform int gSide;
out vec4 FragColor;
void main()
{
vec3 vec;
switch (gSide) {
case 0: vec = vec3(1.0, TexCoord0.xy); break;
case 1: vec = vec3(-1.0, TexCoord0.xy); break;
case 2: vec = vec3(TexCoord0.x, 1.0, TexCoord0.y); break;
case 3: vec = vec3(TexCoord0.x, -1.0, TexCoord0.y); break;
case 4: vec = vec3(TexCoord0.xy, 1.0); break;
case 5: vec = vec3(TexCoord0.xy, -1.0); break;
default: vec = vec3(1.0, 0.0, 0.0); break;
}
FragColor = vec4(vec3(1.0, 0.0, 0.0) * (1.0 - texture(gTexture, vec).r), /*alpha*/ 1.0);
}
渲染阴影贴图调试四边形的代码:
void r_debugPassShadowMap2() {
if (!r_shadowmapDebug.initialized) {
r_shadowmapDebug.initialized = true;
const float margin = 16;
const float mapWidth = (r_windowWidth - (margin * 7.f)) / 6.f;
mat4x4 t, s;
mat4x4_translate(t, 1, 1, 0); // quad verts range from -1x-1x0 to 1x1x0
mat4x4_identity(s);
s[0][0] = s[1][1] = s[2][2] = mapWidth / 2;
mat4x4 m;
mat4x4_mul(m, s, t);
mat4x4 p;
mat4x4_ortho(p, 0, (float)r_windowWidth, (float)r_windowHeight, 0, -1, 1);
for (uint32_t i = 0; i < 6; ++i) {
mat4x4 v;
mat4x4_translate(v, margin + (margin + mapWidth) * i, margin, 0);
mat4x4 mv;
mat4x4_mul(mv, v, m);
mat4x4_mul(r_shadowmapDebug.sideWVP[i], p, mv);
}
}
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_BLEND);
rtech_shadowmapdebug_enable();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, r_shadowmapDebug.texture);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_NONE);
for (uint32_t i = 0; i < 6; ++i) {
rtech_shadowmapdebug_setWVP(r_shadowmapDebug.sideWVP[i]);
rtech_shadowmapdebug_setSide(i);
glBindVertexArray(r_quadAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
}
更新 2 - 第一次部分修复
感谢@MorrisonChang 的link,我尝试了一些不同的东西并且它产生了巨大的变化:我将阴影贴图的GL_TEXTURE_MAG_FILTER和GL_TEXTURE_MIN_FILTER设置为GL_LINEAR质地。当我将其更改为 GL_NEAREST 时,我突然在移动设备上获得了更多结果!然而,如下图清楚地显示,出于某种原因,它只渲染了一张脸,而不是全部 6 张脸!但至少我更进了一步!
我找到了答案,(对我来说)它令人费解,但我发现它符合规范。在我改变我的几何着色器后,我让它立即工作:
void main()
{
for(int face = 0; face < 6; ++face)
{
gl_Layer = face; // built-in variable that specifies to which face we render.
for(int i = 0; i < 3; ++i) // for each triangle's vertices
{
FragPos = gl_in[i].gl_Position;
gl_Position = gShadowMatrices[face] * FragPos;
EmitVertex();
}
EndPrimitive();
}
}
对此:
void emitFace(mat4 m) {
for(int i = 0; i < 3; ++i)
{
FragPos = gl_in[i].gl_Position;
gl_Position = m * FragPos;
EmitVertex();
}
EndPrimitive();
}
void main()
{
gl_Layer = 0;
emitFace(gShadowMatrices[0]);
gl_Layer = 1;
emitFace(gShadowMatrices[1]);
gl_Layer = 2;
emitFace(gShadowMatrices[2]);
gl_Layer = 3;
emitFace(gShadowMatrices[3]);
gl_Layer = 4;
emitFace(gShadowMatrices[4]);
gl_Layer = 5;
emitFace(gShadowMatrices[5]);
}
显然,通过 for 循环变量分配给 gl_Layer 在 OpenGL ES 中是行不通的!
在这个页面 https://www.khronos.org/registry/OpenGL-Refpages/es3/html/gl_Layer.xhtml 上写着
If a shader statically assigns a value to gl_Layer, layered rendering mode is enabled.
它还说:
If the geometry stage makes no static assignment to gl_Layer, the input gl_Layer in the fragment stage will be zero.
但是它没有说"you are not supposed to assign a non static value to gl_Layer ever",编译器也根本没有警告!
在 OpenGL Core 中,规范实际上说的是完全相同的事情:https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/gl_Layer.xhtml
然而,无论如何,我的 NVIDIA 驱动程序支持对 gl_Layer 的动态分配,并且因为它这样做,我从未意识到这是错误的...
总结
我有一个问题 移植 我的基于 OpenGL 3.3 ("desktop") 的游戏引擎到 OpenGL ES 3.2 ("mobile"). 在桌面上一切正常,在移动设备上一切正常但阴影贴图。
我的问题很简单:有人可以在我分享的代码中发现问题,或者指出正确的方向吗?
上下文
- 我所有的引擎代码都是用C 写的
- 除了应用程序层(我在桌面上使用 glfw,在移动设备上使用 glfm)之外,代码库是相同的:为桌面和移动设备编译的代码完全相同。
- 着色器 - 除了像#version 和精度默认值一样注入headers - 在平台之间也是相同的
- 我引擎中的阴影贴图系统基于此处找到的教程:https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows
- 我在桌面上使用 NVIDIA GTX 1080 进行开发,也在其他 NVIDIA PC 上进行了测试
- 我正在使用全新的ASUS ROG II Phone和旧的LG G5进行测试,两者都运行 Android 9,根据[=,两者都支持OpenGL ES 3.2 143=] 应用程序和 都显示完全相同的错误结果。
截图
我的非常简单的灯光测试关卡在桌面上正确呈现。中间的柱子右侧有一个黄色(不可见)光,因此阴影应该投射到左侧,如下面的桌面截图所示:
但是在手机上就大错特错了:
如您所见,手机上的影子是"just wrong"。它应该像在桌面上一样在左边,但它的方向错了,看起来 "chopped off".
我自己的想法和行动
- 我非常小心地确保我的所有调用在 OpenGL ES 3.2 上可用,就像在 OpenGL 3.3 Core 上一样
- 我非常小心地确保 OpenGL 调用没有问题:我正在使用 glDebugMessageCallback 并且没有错误报告任何时候
- 自从它 "just works" 在桌面上,我觉得它背后的数学并没有错
- 我的引擎支持缓存远光的阴影贴图作为性能优化,但禁用它没有任何效果,所以它不会导致问题。
(希望)相关代码
C 中的阴影贴图设置
创建 CUBE MAP 纹理以保持阴影的代码:
static GLuint r_createShadowmapTexture() {
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_CUBE_MAP, tex);
for (unsigned int i = 0; i < 6; ++i)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT32F, r_shadowSize, r_shadowSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
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);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_GREATER);
return tex;
}
为阴影贴图设置帧缓冲区的代码:
glGenFramebuffers(1, &r_shadowDepthMapFBO);
glBindFramebuffer(GL_FRAMEBUFFER, r_shadowDepthMapFBO);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
渲染阴影贴图的代码:
static struct {
vec3 centerOffset, up;
} const g_shadowMapTransforms[6] = {
{ {1.0f, 0.0f, 0.0f}, {0.0f, -1.0f, 0.0f} },
{ {-1.0f, 0.0f, 0.0f}, {0.0f, -1.0f, 0.0f} },
{ {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f} },
{ {0.0f, -1.0f, 0.0f}, {0.0f, 0.0f, -1.0f} },
{ {0.0f, 0.0f, 1.0f}, {0.0f, -1.0f, 0.0f} },
{ {0.0f, 0.0f, -1.0f}, {0.0f, -1.0f, 0.0f} },
};
static void r_shadowMapPass(const point_light_t* light, GLuint cubemapTexture) {
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glDepthMask(GL_TRUE);
glBindFramebuffer(GL_FRAMEBUFFER, r_shadowDepthMapFBO);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cubemapTexture, 0);
const float farPlane = min(r_far, light->radius);
mat4x4 shadowProj;
mat4x4_perspective(shadowProj, (float)M_PI_2, 1.0f, r_near, farPlane);
mat4x4 shadowVP[6];
for (int i = 0; i < 6; ++i) {
vec3 center;
vec3_add(center, light->origin, g_shadowMapTransforms[i].centerOffset);
mat4x4 view;
mat4x4_look_at(view, (float*)light->origin, center, (float*)g_shadowMapTransforms[i].up);
mat4x4_mul(shadowVP[i], shadowProj, view);
}
rtech_shadowmap_enable();
rtech_shadowmap_setLightPos((float*)light->origin);
rtech_shadowmap_setFarPlane(farPlane);
rtech_shadowmap_setShadowMatrices(shadowVP);
render_entity_t* re = r_entities;
glClear(GL_DEPTH_BUFFER_BIT);
for (uint32_t i = 0; i < r_numEntities; ++i, ++re) {
if (!re->castsShadow)
continue;
rtech_shadowmap_setModelMatrix(re->m);
if (re->hasSolid) {
const render_solids_t* const rs = r_solids + re->index;
for (uint32_t j = 0; j < rs->count; ++j) {
const uint32_t ao = rs->offset + j;
r_renderBlock(ao, rs, j);
}
} else {
render_model_t* const rm = &r_models[re->index];
for (uint32_t j = 0; j < rm->count; ++j)
r_renderArrayObject(rm->arrayObjects[j], rm->numIndices[j]);
}
}
glDepthMask(GL_FALSE);
}
在桌面上,我为所有着色器(顶点、几何体和片段)添加以下前缀:
#version 330 core
在移动设备上,我为所有着色器添加以下前缀:
#version 320 es
precision highp float;
precision highp int;
precision highp sampler2D;
precision highp samplerCubeShadow;
阴影贴图顶点着色器:
layout (location = 0) in vec3 aPos;
uniform mat4 gModel;
void main()
{
gl_Position = gModel * vec4(aPos, 1.0);
}
阴影贴图几何着色器:
layout (triangles) in;
layout (triangle_strip, max_vertices=18) out;
uniform mat4 gShadowMatrices[6];
out vec4 FragPos; // FragPos from GS (output per emitvertex)
void main()
{
for(int face = 0; face < 6; ++face)
{
gl_Layer = face; // built-in variable that specifies to which face we render.
for(int i = 0; i < 3; ++i) // for each triangle's vertices
{
FragPos = gl_in[i].gl_Position;
gl_Position = gShadowMatrices[face] * FragPos;
EmitVertex();
}
EndPrimitive();
}
}
阴影贴图片段着色器:
in vec4 FragPos;
uniform vec3 gLightPos;
uniform float gFarPlane;
void main()
{
// get distance between fragment and light source
float lightDistance = length(FragPos.xyz - gLightPos);
// map to [0;1] range by dividing by gFarPlane
lightDistance = lightDistance / gFarPlane;
// write this as modified depth
gl_FragDepth = lightDistance;
}
实际使用阴影贴图的点光通道的摘录:
...
uniform samplerCubeShadow gShadowCubeMap;
uniform float gFarPlane;
...
float ShadowCalcSinglePass(vec3 fragPos, vec3 lightPos) {
vec3 fragToLight = fragPos - lightPos;
float currentDepth = (length(fragToLight) - (0.005 * gFarPlane)) / gFarPlane;
return texture(gShadowCubeMap, vec4(fragToLight, currentDepth));
}
我知道这是一个非常开放的问题,涉及到很多代码。我正在尝试提供必要的相关信息,如果需要或要求,我可以分享更多!
更新 1
根据@MichaelKenzel 的建议,我尝试将阴影贴图渲染到屏幕上。这非常适合桌面,如下所示。然而,在移动设备上,这显示了一个 "all red" 阴影贴图,其中(因为我的着色器执行 1.0 - 样本)texture() 函数为每个像素返回 0。但是,如上图 "wrong" 所示,某处有阴影,因此以这种方式从深度缓冲区读取时似乎存在问题。
注意:下图来自与上图不同的测试级别,具有更复杂的阴影
in vec2 TexCoord0;
uniform samplerCube gTexture;
uniform int gSide;
out vec4 FragColor;
void main()
{
vec3 vec;
switch (gSide) {
case 0: vec = vec3(1.0, TexCoord0.xy); break;
case 1: vec = vec3(-1.0, TexCoord0.xy); break;
case 2: vec = vec3(TexCoord0.x, 1.0, TexCoord0.y); break;
case 3: vec = vec3(TexCoord0.x, -1.0, TexCoord0.y); break;
case 4: vec = vec3(TexCoord0.xy, 1.0); break;
case 5: vec = vec3(TexCoord0.xy, -1.0); break;
default: vec = vec3(1.0, 0.0, 0.0); break;
}
FragColor = vec4(vec3(1.0, 0.0, 0.0) * (1.0 - texture(gTexture, vec).r), /*alpha*/ 1.0);
}
渲染阴影贴图调试四边形的代码:
void r_debugPassShadowMap2() {
if (!r_shadowmapDebug.initialized) {
r_shadowmapDebug.initialized = true;
const float margin = 16;
const float mapWidth = (r_windowWidth - (margin * 7.f)) / 6.f;
mat4x4 t, s;
mat4x4_translate(t, 1, 1, 0); // quad verts range from -1x-1x0 to 1x1x0
mat4x4_identity(s);
s[0][0] = s[1][1] = s[2][2] = mapWidth / 2;
mat4x4 m;
mat4x4_mul(m, s, t);
mat4x4 p;
mat4x4_ortho(p, 0, (float)r_windowWidth, (float)r_windowHeight, 0, -1, 1);
for (uint32_t i = 0; i < 6; ++i) {
mat4x4 v;
mat4x4_translate(v, margin + (margin + mapWidth) * i, margin, 0);
mat4x4 mv;
mat4x4_mul(mv, v, m);
mat4x4_mul(r_shadowmapDebug.sideWVP[i], p, mv);
}
}
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_BLEND);
rtech_shadowmapdebug_enable();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, r_shadowmapDebug.texture);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_NONE);
for (uint32_t i = 0; i < 6; ++i) {
rtech_shadowmapdebug_setWVP(r_shadowmapDebug.sideWVP[i]);
rtech_shadowmapdebug_setSide(i);
glBindVertexArray(r_quadAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
}
更新 2 - 第一次部分修复
感谢@MorrisonChang 的link,我尝试了一些不同的东西并且它产生了巨大的变化:我将阴影贴图的GL_TEXTURE_MAG_FILTER和GL_TEXTURE_MIN_FILTER设置为GL_LINEAR质地。当我将其更改为 GL_NEAREST 时,我突然在移动设备上获得了更多结果!然而,如下图清楚地显示,出于某种原因,它只渲染了一张脸,而不是全部 6 张脸!但至少我更进了一步!
我找到了答案,(对我来说)它令人费解,但我发现它符合规范。在我改变我的几何着色器后,我让它立即工作:
void main()
{
for(int face = 0; face < 6; ++face)
{
gl_Layer = face; // built-in variable that specifies to which face we render.
for(int i = 0; i < 3; ++i) // for each triangle's vertices
{
FragPos = gl_in[i].gl_Position;
gl_Position = gShadowMatrices[face] * FragPos;
EmitVertex();
}
EndPrimitive();
}
}
对此:
void emitFace(mat4 m) {
for(int i = 0; i < 3; ++i)
{
FragPos = gl_in[i].gl_Position;
gl_Position = m * FragPos;
EmitVertex();
}
EndPrimitive();
}
void main()
{
gl_Layer = 0;
emitFace(gShadowMatrices[0]);
gl_Layer = 1;
emitFace(gShadowMatrices[1]);
gl_Layer = 2;
emitFace(gShadowMatrices[2]);
gl_Layer = 3;
emitFace(gShadowMatrices[3]);
gl_Layer = 4;
emitFace(gShadowMatrices[4]);
gl_Layer = 5;
emitFace(gShadowMatrices[5]);
}
显然,通过 for 循环变量分配给 gl_Layer 在 OpenGL ES 中是行不通的!
在这个页面 https://www.khronos.org/registry/OpenGL-Refpages/es3/html/gl_Layer.xhtml 上写着
If a shader statically assigns a value to gl_Layer, layered rendering mode is enabled.
它还说:
If the geometry stage makes no static assignment to gl_Layer, the input gl_Layer in the fragment stage will be zero.
但是它没有说"you are not supposed to assign a non static value to gl_Layer ever",编译器也根本没有警告!
在 OpenGL Core 中,规范实际上说的是完全相同的事情:https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/gl_Layer.xhtml
然而,无论如何,我的 NVIDIA 驱动程序支持对 gl_Layer 的动态分配,并且因为它这样做,我从未意识到这是错误的...