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

光线追踪问题:光源照射球体出现X形光斑而非圆形光斑

光线追踪球体异常光斑与反射全黑问题排查

问题现象

  • 基于GLSL实现的球体光线追踪中,光源正对球体时,表面出现3线X形光斑而非预期的圆形光斑;未使用阴影射线,光照来自两个光源。相同逻辑的C++实现无此问题,怀疑是精度或向量计算问题导致。
  • 将随机散射逻辑替换为reflect函数后,场景直接全黑。

效果展示

  1. 光线追踪效果 - 存在X形光斑的光线追踪效果
  2. 场景法线贴图 - 场景法线贴图
  3. reflect函数效果 - 使用reflect函数替代随机散射后的效果

GLSL着色器代码

#version 410 core

in vec2 v_texCoord;
in vec2 v_rayCoord;

uniform sampler2D u_randTexture;
uniform vec2 u_randSampler;

out vec4 FragColor;

void main()
{
    float window_width = 800.f;
    float window_height = 600.f;
    float focal_distance = -400.f;

    const int sphere_count = 4;
    const int ray_bounce = 3;

    // might be a better way to randomize
    vec2 tex_coord = v_texCoord + u_randSampler;
    vec4 rand_vec = texture(u_randTexture, tex_coord);
    rand_vec = (rand_vec - vec4(0.5f)) * vec4(2.f);

    float radius[sphere_count] = float[](0.5f, 100.f, 80.f, 0.1f);
    vec3 positions[sphere_count] = vec3[](vec3(0.f, -2.7f, -5.f), vec3(0.f, -103.f, -5.f), vec3(100.f, 80.f, -100.f), vec3(-0.8f, -1.7f, -4.f));
    vec3 color[sphere_count] = vec3[](vec3(1.f, 0.5f, 0.5f), vec3(0.5f, 0.5f, 1.f), vec3(0.9f, 0.9f, 0.3f), vec3(0.48f, 0.38f, 0.57f));
    float brightness[sphere_count] = float[](0.f, 0.f, 7.f, 500.f);

    vec3 ray_origin = vec3(0.f, 0.f, 0.f);
    vec3 ray_direction = vec3(v_rayCoord.x, v_rayCoord.y, focal_distance);

    vec3 absorb_col = vec3(1.f);
    vec3 light_col = vec3(0.f);

    for(int i=0;i<ray_bounce;++i)
    {
        float closest_hit = 10000.f; // arbitrary large number
        int idx = -1;
        // loop through all the different objects
        for(int j=0;j<sphere_count;++j)
        {
            vec3 origin_to_center = ray_origin - positions[j];
            float a = dot(ray_direction, ray_direction);
            float b = 2.f * dot(origin_to_center, ray_direction);
            float c = dot(origin_to_center, origin_to_center) - radius[j] * radius[j];
            float discriminant = b * b - 4.f * a * c;
            if(discriminant < 0.f)
                continue;

            float t = (-b - sqrt(discriminant)) / (2.f * a);
            if(t > 0.f && t < closest_hit)
            {
                closest_hit = t;
                idx = j;
            }
        }

        if(idx < 0)
        {
            //light_col += vec3(0.627f, 1.f, 1.f) * vec3(0.3) * absorb_col;
            light_col += vec3(0.f);
            break;
        }

        vec3 surface = ray_origin + ray_direction * closest_hit;
        vec3 normal = normalize(surface - positions[idx]);
        vec3 incident_vector = normalize(-ray_direction);

        ray_origin = surface + normal * 0.00000000001f;
        ray_direction = normal + rand_vec.xyz * 0.99f;

        if(brightness[idx] > 0.f)
        {
            light_col += color[idx] * absorb_col * brightness[idx];
            break;
        } else
        {
            absorb_col *= color[idx];
        }
    }

    FragColor = vec4(light_col, 1.0);
}

CPU生成随机纹理代码

// dis(gen) should generate a float from 0.f to 1.f
void randVec3(Renderer::Vec3<float>& _vec3)
{

    _vec3.x = dis(gen) * 2.f - 1.f;
    _vec3.y = dis(gen) * 2.f - 1.f;
    _vec3.z = dis(gen) * 2.f - 1.f;

    _vec3.normalize();
}
int main()
{
        ...
    unsigned char* rand_vec_data = new unsigned char[WINDOW_WIDTH * WINDOW_HEIGHT * 3];
    for(unsigned int i=0;i<WINDOW_WIDTH * WINDOW_HEIGHT;++i)
    {
        Renderer::Vec3<float> rand_vector;
        randVec3(rand_vector);
        rand_vector = (rand_vector + Renderer::Vec3<float>(1.f)) * Renderer::Vec3<float>(0.5f);

        rand_vec_data[i * 3] = static_cast<unsigned char>(rand_vector.x * 255);
        rand_vec_data[i * 3 + 1] = static_cast<unsigned char>(rand_vector.y * 255);
        rand_vec_data[i * 3 + 2] = static_cast<unsigned char>(rand_vector.z * 255);
    }
        ...
}

问题分析与修复方案

1. X形光斑问题

核心原因

  • 散射方向未归一化:当前ray_direction = normal + rand_vec.xyz * 0.99f的计算结果未做归一化,导致方向向量分布异常。
  • 随机纹理精度不足:用8位unsigned char存储随机向量,量化误差会让随机方向集中在几个轴向上,形成规则光斑。
  • 表面偏移量过小:0.00000000001f远低于GLSL浮点数有效精度,无法避免光线自相交,干扰光照计算。

修复步骤

  • 对散射方向做归一化:ray_direction = normalize(normal + rand_vec.xyz * 0.99f);
  • 增大表面偏移量:改为1e-5f,确保光线远离当前表面,避免自相交:ray_origin = surface + normal * 1e-5f;
  • 提升随机纹理精度:改用GL_RGB16F或GL_RGB32F浮点纹理存储随机向量,消除量化误差。

2. reflect函数导致场景全黑问题

核心原因

  • 表面偏移量过小:反射光线立即与原表面相交,被判定为无效射线,直接跳出循环无法命中光源。
  • 反射光线未命中光源:当前光源球体尺寸小(半径0.1),反射光线方向未对准光源,或碰撞检测逻辑未正确处理反射后的射线方向。

修复步骤

  • 先修复表面偏移量为1e-5f,避免自相交。
  • 验证反射方向正确性:临时将FragColor改为反射方向的颜色值,确认反射向量计算无误。
  • 调整碰撞检测逻辑:确保光线碰撞时正确筛选正的t值,必要时同时检查二次方程的两个根,避免遗漏远端交点。
  • 增加光线反弹次数或调整光源位置,确保反射光线能命中光源球体。

内容的提问来源于stack exchange,提问作者Jeffrey Chen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 10:52:12