【问题标题】:Ray Tracing Reflections do not look right光线追踪反射看起来不正确
【发布时间】:2021-12-16 20:00:53
【问题描述】:

我目前正在为 3D 光线追踪引擎编写一个简单的演示。该程序基本上具有以下结构: 一个数组使用三个坐标存储所有平面。对于屏幕上的每个(2D)像素,我投射一条射线并计算每个平面的碰撞点。如果该点位于由其三个点定义的有界平面上(因为数学平面无限大),并且碰撞点离相机最近,则它是选定的碰撞点。基于该信息,我递归地投射更多光线以实现反射效果。结果看起来几乎是正确的,所有平面都出现在正确的位置,但它有(有)一个问题:反射看起来不正确:

我意识到我遇到的一个问题是,如果光线从另一侧击中平面,而不是法线向量指向的平面。如果是这种情况,我通过否定它的分量来反转法线向量。问题是,当我这样做时,完全没有任何变化。结果看起来仍然一样。这有点可疑,但也许那是因为我对数学的确切运作方式有误解。

第二个问题是我需要防止反射光线从同一平面反射。并且如果最近的物体在光线的前面(反射矢量的方向)而不是在它后面。在“修复”这两个问题之后,这是我被困在的结果:

一切都不是它应该的样子,我有点沮丧,因为我一遍又一遍地查看我的代码,却无法弄清楚问题所在。

这是我程序的主要代码:

function update() {
  ctx.clearRect(0, 0, width, height);
  for (let z = 0; z < resolution; z++) {
    for (let x = 0; x < resolution; x++) {

      // Create Ray
      let dx = cameraPosition.x + x - resolution/2;
      let dy = cameraPosition.y + focalLength;
      let dz = cameraPosition.z - z + resolution/2;
      let direction = new Vector(dx, dy, dz);
      let ray = new Ray(cameraPosition, direction);

      let color = castRay(ray, 3);
      ctx.fillStyle = color.toString();
      ctx.fillRect(x, y, 1, 1);
    }
  }
}


function castRay(ray, bounces) {
  let shortestDistance = Infinity;
  let intersectionInfo;
  let planeIndex;
  let intersects = false;

  for (let i = 0; i < planes.length; i++) {
    let plane = planes[i];
    let res = Geometry.intersect(plane, ray);
    if (res == null) continue;
    let isp = res.point;
    let dist = isp.toVector().length();

    let rightDirection = false;
    if (Geometry.dotProduct(res.reflectionVector, isp.toVector().sub(ray.startPoint)) >= 0) {
       if (!isp.equals(ray.startPoint)) {
         rightDirection = true;
       }
    }

    let planeWidth = Math.max(Math.abs(plane.p1.x - plane.p2.x), Math.abs(plane.p1.x - plane.p3.x), Math.abs(plane.p2.x - plane.p3.x));
    let planeHeight = Math.max(Math.abs(plane.p1.z - plane.p2.z), Math.abs(plane.p1.z - plane.p3.z), Math.abs(plane.p2.z - plane.p3.z));
    if (isp.x >= Math.min(plane.p1.x, plane.p2.x, plane.p3.x) && isp.x <= plane.p1.x + planeWidth) {
      if (isp.z >= Math.min(plane.p1.z, plane.p2.z, plane.p3.z) && isp.z <= plane.p1.z + planeHeight) {
        if (dist < shortestDistance && rightDirection) {
          intersects = true;
          shortestDistance = dist;
          planeIndex = i;
          intersectionInfo = res;
        }
      }
    }
  }

  if (!intersects) {
    let noiseValue = Math.floor(Math.random()*25);
    return new Color(noiseValue, noiseValue, noiseValue);
  }

  if (bounces > 0) {
    let color = planes[planeIndex].color;
    let direction = intersectionInfo.reflectionVector;
    let ray = new Ray(intersectionInfo.point, direction);
    return castRay(ray, bounces-1).combine(color, 1-planes[planeIndex].reflectivity);
  }

  return new Color(0, 0, 0);
}

这是主要部分。所有的反射和向量计算都在以下代码中进行:

class Geometry {
  static intersect(plane, ray) {
    let a = plane.p1.toVector();
    let b = plane.p2.toVector();
    let c = plane.p3.toVector();
    let rayStart = ray.startPoint.toVector();
    let rayDirection = ray.direction.normalize();

    // Intersection Point
    let n = Geometry.crossProduct(a.sub(b), a.sub(c));
    let angle = Geometry.angleBetweenVectors(rayDirection, n.negate());
    if (angle >= Math.PI/2) {
       n = n.negate();
       angle -= Math.PI/2;
    }
    let t = -(Geometry.dotProduct(n, rayStart) - Geometry.dotProduct(n, a)) / Geometry.dotProduct(n, rayDirection);
    let intersectionPoint = rayStart.add(rayDirection.scale(t)).toPoint();

    // Reflection Vector
    n = n.normalize();
    n = n.scale(2*Geometry.dotProduct(rayDirection, n));
    let reflectionVector = rayDirection.sub(n);
    return {point: intersectionPoint, angle: angle, reflectionVector: reflectionVector};
  }

  static angleBetweenVectors(a, b) {
    return Math.acos(Geometry.dotProduct(a, b)/(a.length()*b.length()));
  }

  static dotProduct(v1, v2) {
    return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
  }

  static crossProduct(v1, v2) {
    let x = (v1.y*v2.z) - (v1.z*v2.y);
    let y = (v1.z*v2.x) - (v1.x*v2.z);
    let z = (v1.x*v2.y) - (v1.y*v2.x);
    return new Vector(x, y, z);
  }

}

所以这就是我被困的地方。如果有人能指出我正确的方向,我将不胜感激......非常感谢!

【问题讨论】:

    标签: javascript math linear-algebra raytracing


    【解决方案1】:

    我终于弄清楚了错误是什么。事实上,这是两个(容易修复,不是那么容易找到)错误:

    • castRay(...) 函数的第 11 行应该是:

      让 dist = isp.toVector().sub(ray.startPoint).length();

    我计算了相对于原点的距离,而不是相对于光线矢量起点的距离。现在它是固定的。

    • 同一个函数的第14行应该是:

      if (Geometry.dotProduct(ray.direction, isp.toVector().sub(ray.startPoint)) >= 0) { ...

    这是现在的结果:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-03-30
      • 2012-12-11
      • 1970-01-01
      • 2017-03-11
      • 1970-01-01
      • 1970-01-01
      • 2015-11-11
      • 2018-05-31
      相关资源
      最近更新 更多