GLSL实现带阴影盒子时角落出现亮线的原因排查
GLSL盒子阴影效果右上角亮线问题原因分析
问题背景
你用以下GLSL代码实现带阴影的盒子效果,在右上角的渐变交汇处出现了明显亮线:
void mainImage(out vec4 fragColor, in vec2 fragCoord) { vec2 uv = fragCoord / iResolution.xy; vec2 windowSize = iResolution.xy; float border = 50.0; vec2 pixelPos = uv * windowSize; vec2 v = windowSize - pixelPos; vec2 n = normalize(vec2(1.0, -1.0)); float col = 1.0; if (dot(v, n) < 0.0) { col = clamp(windowSize.x - pixelPos.x, 0.0, border) / border; } else { col = clamp(windowSize.y - pixelPos.y, 0.0, border) / border; } fragColor = vec4(vec3(col), 1.0); }
你用Python代码验证时,对角线及附近像素的计算值均为0.2,理论上不应有明暗差异,但OpenGL渲染时仍出现亮线。
问题原因
1. 单精度浮点精度误差
GPU使用单精度浮点数计算,而Python的numpy默认是双精度。normalize(vec2(1.0, -1.0))得到的是(√2/2, -√2/2),这个值无法用二进制浮点数精确表示,导致dot(v, n)的计算在GPU上存在微小误差。理论上对角线上的点dot(v,n)应为0,但实际计算可能略正或略负;相邻像素的计算结果可能因精度问题跨了0阈值,一部分用x方向渐变、一部分用y方向渐变。虽然两种方式计算出的col理论值相同,但GPU的浮点误差会让两者产生细微差异,再加上颜色量化(比如8位RGB输出),就会显现出亮线。
2. 像素采样位置差异
GLSL中fragCoord对应的是像素中心坐标(比如ShaderToy环境),而你的Python代码直接使用整数像素坐标计算,两者存在0.5像素的偏移。这会导致GPU中v = windowSize - pixelPos的计算值和Python中的值有细微不同,进而影响点积结果,使得原本应在同一侧的点被分到阈值的另一边。
3. 光栅化硬件实现差异
GPU的光栅化单元在处理像素时,有硬件层面的优化或精度限制,在dot(v,n) < 0这种临界点判断上,容易出现和CPU精确计算不一致的情况,导致边缘像素的计算逻辑出现偏差。
解决方法
去掉硬阈值判断,直接取两个方向渐变的最小值,逻辑和原代码一致,但避免了临界点的精度问题:
void mainImage(out vec4 fragColor, in vec2 fragCoord) { vec2 windowSize = iResolution.xy; float border = 50.0; float distX = clamp(windowSize.x - fragCoord.x, 0.0, border) / border; float distY = clamp(windowSize.y - fragCoord.y, 0.0, border) / border; // 取两个渐变值的最小值,替代硬判断 float col = min(distX, distY); fragColor = vec4(vec3(col), 1.0); }
内容的提问来源于stack exchange,提问作者Roy Varon
相关产品推荐
相关产品推荐

