光线追踪椭球Hit函数故障求助:旋转后光照反射异常
问题排查:椭球光线追踪Hit函数旋转后光照反射异常
从你提供的代码和描述来看,核心问题出在法线向量的计算和变换上,这也是旋转后光照反射异常的关键原因。我们一步步拆解问题并修正:
核心错误分析
你的椭球Hit函数中,法线计算的逻辑存在两个关键问题:
法线变换矩阵使用错误:
椭球可以看作是单位球经过缩放+旋转变换得到的,假设M是将单位球变换为当前椭球的矩阵(缩放矩阵后乘旋转矩阵),那么将椭球逆变换回单位球的矩阵是M.inverse()。但法线向量的变换规则和顶点不同——法线需要用变换矩阵的逆转置矩阵,而不是直接用逆矩阵。你之前尝试过用M.transpose(),但那是错误的,正确的应该是M.inverse().transpose()(逆矩阵的转置)。法线未归一化:
变换后的法线向量长度会因为缩放操作发生变化,如果不进行归一化,光照计算时的方向向量模长不正确,会导致反射、漫反射等光照效果异常,尤其是旋转后缩放的影响会更明显。
另外,代码中(hitRecord.closestIntersection - center) / 1这个除以1的操作完全多余,可以直接去掉。
修正后的Hit函数关键部分
下面是修正后的法线计算代码,以及完整的Hit函数调整:
bool Ellipsoid::Hit(const Ray& ray, const double minHitDistance, const double maxHitDistance, HitRecord& hitRecord, const Vector3d& light) const { if (hitRecord.shapeHit == this) { return false; } // M是将单位球变换为椭球的矩阵(缩放+旋转) const Vector3d direction = M.inverse() * ray.Direction(); const Vector3d oc = M.inverse() * (ray.Origin() - this->center); const double a = direction.dot(direction); const double b = oc.dot(direction); const double c = oc.dot(oc) - 1; const double discriminant = b*b - a*c; if (discriminant > 0) { const double sqrtDiscriminant = sqrt(discriminant); const double aReciprocal = 1.0 / a; double temp = (-b - sqrtDiscriminant) * aReciprocal; if (temp < maxHitDistance && temp > minHitDistance) { hitRecord.distance = temp; hitRecord.closestIntersection = ray.PointAtParameter(hitRecord.distance); // 1. 计算单位球上的法线(即逆变换后的点相对于原点的向量) Vector3d unitSphereNormal = oc + temp * direction; // 2. 用逆转置矩阵将法线变换回世界空间 hitRecord.normal = M.inverse().transpose() * unitSphereNormal; // 3. 归一化法线向量 hitRecord.normal.normalize(); hitRecord.shapeHit = this; return true; } temp = (-b + sqrtDiscriminant) * aReciprocal; if (temp < maxHitDistance && temp > minHitDistance) { hitRecord.distance = temp; hitRecord.closestIntersection = ray.PointAtParameter(hitRecord.distance); Vector3d unitSphereNormal = oc + temp * direction; hitRecord.normal = M.inverse().transpose() * unitSphereNormal; hitRecord.normal.normalize(); hitRecord.shapeHit = this; return true; } } return false; }
额外检查点
如果修正后仍然存在问题,建议排查以下模块:
- 矩阵M的定义是否正确:确认M是将单位球变换为椭球的矩阵(比如先缩放三个轴,再旋转),如果M的作用是反过来的(椭球转单位球),需要调整变换逻辑。
- 光照计算模块:检查光照反射、漫反射的计算是否使用了归一化的法线和光线方向,比如反射向量的计算是否正确调用了法线的归一化版本。
- HitRecord的法线方向:确认法线是否始终指向光线入射的反方向(可以通过点乘光线方向判断,如果点积为正,说明法线指向内部,需要取反)。
内容的提问来源于stack exchange,提问作者Jes
相关产品推荐
相关产品推荐

