You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 00:38:10