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

光线追踪椭球Hit函数故障求助:旋转后光照反射异常

问题排查:椭球光线追踪Hit函数旋转后光照反射异常

从你提供的代码和描述来看,核心问题出在法线向量的计算和变换上,这也是旋转后光照反射异常的关键原因。我们一步步拆解问题并修正:

核心错误分析

你的椭球Hit函数中,法线计算的逻辑存在两个关键问题:

  1. 法线变换矩阵使用错误:
    椭球可以看作是单位球经过缩放+旋转变换得到的,假设M是将单位球变换为当前椭球的矩阵(缩放矩阵后乘旋转矩阵),那么将椭球逆变换回单位球的矩阵是M.inverse()。但法线向量的变换规则和顶点不同——法线需要用变换矩阵的逆转置矩阵,而不是直接用逆矩阵。你之前尝试过用M.transpose(),但那是错误的,正确的应该是M.inverse().transpose()(逆矩阵的转置)。

  2. 法线未归一化:
    变换后的法线向量长度会因为缩放操作发生变化,如果不进行归一化,光照计算时的方向向量模长不正确,会导致反射、漫反射等光照效果异常,尤其是旋转后缩放的影响会更明显。

另外,代码中(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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 16:12:42