【发布时间】: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