JavaScript光线追踪器折射功能异常:透明球体显示黑色求助
光线追踪器透明球体显示黑色的问题排查与修复
问题描述
参照教材为JavaScript光线追踪器添加折射功能后,本该透明的球体显示为黑色,多次尝试修复无效。效果如下:
相关代码片段
球体类定义
// 球体类 var Sphere = function(center, radius, color, specular, reflective, refractive, refractive_index) { this.center = center; this.radius = radius; this.color = color; this.specular = specular; this.reflective = reflective; this.refractive = refractive; this.refractive_index = refractive_index; // 新增属性 }
场景球体配置
// 场景中的球体 var spheres = [ new Sphere([0, -1, 3], 1, [255, 0, 0], 500, 0.2, false, 1.0), // 最后两个参数为折射开关和折射率 new Sphere([2, 0, 4], 1, [0, 0, 255], 500, 0.3, false, 1.0), new Sphere([-2, 0, 4], 1, [0, 255, 0], 10, 0.4, true, 1.5), // 设置为透明球体,折射率1.5 new Sphere([0, -5001, 0], 5000, [255, 255, 0], 1000, 0.5, false, 1.0) ];
光线追踪核心函数
// 光线追踪函数 var TraceRay = function(origin, direction, min_t, max_t, depth) { var intersection = ClosestIntersection(origin, direction, min_t, max_t); if (!intersection) { return background_color; } var closest_sphere = intersection[0]; var closest_t = intersection[1]; var point = Add(origin, MultiplySV(closest_t, direction)); var normal = Subtract(point, closest_sphere.center); normal = MultiplySV(1.0 / Length(normal), normal); var view = MultiplySV(-1, direction); var lighting = ComputeLighting(point, normal, view, closest_sphere.specular); var local_color = MultiplySV(lighting, closest_sphere.color); if (depth <= 0) { return local_color; } var reflected_color = [0, 0, 0]; if (closest_sphere.reflective > 0) { var reflected_ray = ReflectRay(view, normal); reflected_color = TraceRay(point, reflected_ray, EPSILON, Infinity, depth - 1); } var refracted_color = [0, 0, 0]; if (closest_sphere.refractive) { var refracted_ray = RefractRay(view, normal, closest_sphere.refractive_index); // 需要实现该函数 refracted_color = TraceRay(point, refracted_ray, EPSILON, Infinity, depth - 1); } return Add(MultiplySV(1 - closest_sphere.reflective - closest_sphere.refractive, local_color), Add(MultiplySV(closest_sphere.reflective, reflected_color), MultiplySV(closest_sphere.refractive, refracted_color))); }
问题排查方向
- 折射函数未实现:代码中标注
RefractRay函数需要实现,若未正确返回折射光线方向,会导致后续追踪无有效交点,返回黑色背景。 - 介质折射率切换错误:光线进入/离开球体时,未在空气(折射率≈1.0)和球体介质(1.5)间切换折射率,导致折射方向计算错误。
- 全反射处理缺失:光线从光密介质射向光疏介质时,可能触发全反射,此时无折射光线,若未 fallback 到反射光线计算,会返回黑色。
- 颜色混合参数溢出:当前透明球体的
reflective(0.4)+refractive(1.0)>1,导致1 - reflective - refractive为负数,local_color被负系数修正后出现异常。 - 内部光线相交误判:折射光线在球体内部时,未排除自身球体的相交检测,可能误判为无有效交点。
修复建议
1. 实现正确的RefractRay函数
function RefractRay(view, normal, refractive_index) { var eta = 1.0 / refractive_index; var cosi = Dot(view, normal); // 判断光线是进入还是离开介质,切换折射率与法线方向 if (cosi < 0) { cosi = -cosi; } else { eta = 1.0 / eta; normal = MultiplySV(-1, normal); } var k = 1 - eta * eta * (1 - cosi * cosi); if (k < 0) { // 触发全反射,返回反射光线 return ReflectRay(view, normal); } // 计算并返回折射光线方向 return Add(MultiplySV(eta, view), MultiplySV(eta * cosi - Math.sqrt(k), normal)); }
2. 调整颜色混合参数
确保reflective与refractive的和不超过1,修改透明球体的配置:
new Sphere([-2, 0, 4], 1, [0, 255, 0], 10, 0.1, 0.8, 1.5)
保留少量漫反射分量,同时让折射、反射效果正常叠加。
3. 修正内部光线相交检测
在追踪折射光线时,修改ClosestIntersection函数,排除当前球体的检测;或调整TraceRay的min_t/max_t范围,避免误判自身相交。
内容的提问来源于stack exchange,提问作者user779537
相关产品推荐
相关产品推荐

