光线追踪器射线方向计算错误问题排查求助
光线追踪射线方向计算错误导致渲染异常问题
问题详情
射线方向计算不正确,场景无法按预期投射射线,无法正常渲染。将射线方向以颜色显示时输出全黑帧,不符合预期效果:
- 预期射线方向显示为红绿色混合(默认z方向为负)
- NDC空间左上角应为纯黑色(对应取值范围-1,-1到0,0)
开发环境:Rust + wgpu + winit,光线追踪逻辑采用WGSL编写。已尝试修改射线生成算法/公式,无任何改善。
相关代码
struct Camera { position: vec3<f32>, forward: vec3<f32>, upward: vec3<f32>, rightward: vec3<f32>, vfov: f32, aspect_ratio: f32, } struct Ray { origin: vec3<f32>, direction: vec3<f32>, } fn camera_generate_ray(camera: Camera, u: f32, v: f32) -> Ray { // e (center) // u, v, w (horizontal/right, up, direction/forward) // r = x * u + aspect ratio * y * v + d * w // d = 1 / tan(alfa / 2) let x = 2.0 * u - 1.0; let y = 2.0 * v - 1.0; let d = 1.0 / tan(camera.vfov / 2.0); let ray_direction = x * camera.rightward + camera.aspect_ratio * y * camera.upward + d * camera.forward; let origin = camera.position; let direction = normalize(ray_direction); return Ray(origin, direction); }
问题分析与修复方案
1. 视场角(vfov)单位错误
WGSL的tan()函数接收弧度值,如果传入的camera.vfov是角度值(比如90度),会导致d值计算完全错误,射线方向趋近于零向量,最终显示全黑。
修复:将vfov转换为弧度:
let vfov_rad = radians(camera.vfov); let d = 1.0 / tan(vfov_rad / 2.0);
2. 屏幕坐标到NDC的y轴翻转
屏幕空间的v坐标通常从上到下递增,但NDC空间的y轴是从下到上递增。原代码中y = 2.0 * v - 1.0会导致左上角的y值为1(而非预期的-1),不符合NDC左上角为纯黑色的要求。
修复:翻转y轴计算:
let y = 1.0 - 2.0 * v;
3. 向量归一化问题
如果camera.rightward、camera.upward未做归一化处理,会导致射线方向的缩放比例异常,进而影响颜色显示结果。
修复:确保Camera的三个方向向量是正交归一化的(互相垂直且长度为1),或在计算时手动归一化:
let right = normalize(camera.rightward); let up = normalize(camera.upward); let ray_direction = x * right + (y * camera.aspect_ratio) * up + d * camera.forward;
4. 颜色映射范围错误
射线方向向量的分量范围是[-1,1],直接作为颜色输出时,负数会被截断为0,导致黑色。需要将向量映射到颜色的[0,1]范围。
修复:在输出颜色时做转换:
let color = (ray.direction + 1.0) / 2.0;
修正后的完整函数
struct Camera { position: vec3<f32>, forward: vec3<f32>, upward: vec3<f32>, rightward: vec3<f32>, vfov: f32, aspect_ratio: f32, } struct Ray { origin: vec3<f32>, direction: vec3<f32>, } fn camera_generate_ray(camera: Camera, u: f32, v: f32) -> Ray { let x = 2.0 * u - 1.0; // 翻转y轴适配NDC let y = 1.0 - 2.0 * v; // 转换vfov为弧度 let vfov_rad = radians(camera.vfov); let d = 1.0 / tan(vfov_rad / 2.0); // 确保方向向量归一化 let right = normalize(camera.rightward); let up = normalize(camera.upward); let ray_direction = x * right + (y * camera.aspect_ratio) * up + d * camera.forward; let origin = camera.position; let direction = normalize(ray_direction); return Ray(origin, direction); }
内容的提问来源于stack exchange,提问作者Zirconium409122
相关产品推荐
相关产品推荐

