光线追踪问题:光源照射球体出现X形光斑而非圆形光斑
光线追踪球体异常光斑与反射全黑问题排查
问题现象
- 基于GLSL实现的球体光线追踪中,光源正对球体时,表面出现3线X形光斑而非预期的圆形光斑;未使用阴影射线,光照来自两个光源。相同逻辑的C++实现无此问题,怀疑是精度或向量计算问题导致。
- 将随机散射逻辑替换为
reflect函数后,场景直接全黑。
效果展示
- 存在X形光斑的光线追踪效果
- 场景法线贴图
- 使用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
相关产品推荐
相关产品推荐

