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

JavaScript体素光线追踪:如何反转速度向量实现完美反弹?

体素光线追踪系统中的光线反弹实现方案

问题描述

我正在开发一个基于JavaScript的体素光线追踪系统,简单版本的实现是可行的,但卡在了虚拟光线的反弹效果算法逻辑上。

光线的核心逻辑是:每条光线是包含位置和速度的虚拟点,二者都是以{x, y, z}形式存储的向量。每帧将速度向量叠加到位置向量上,并检测新位置的体素,核心代码如下:

for(let step = 0; step < dist_max; step++) {
    pos.x += vel.x;
    pos.y += vel.y;
    pos.z += vel.z;
    if(solid_at(pos)) {
        // A voxel was hit
    }
}

需求是:当检测到体素碰撞时,让光线像橡胶球一样反弹(只改方向不改速度大小),碰撞表面视为正对光线的平面;同时需要用0-1之间的数值控制反弹强度,适配半透明体素的反射/折射需求。

目前的困惑是:直接把速度整体乘-1会让光线原路返回,应该只反转速度的一个或多个轴,但光线朝向任意方向,X/Y/Z轴速度可正可负,不知道该判断翻转哪些轴?


解决方案

核心思路:利用轴对齐法向量计算反射方向

体素是轴对齐的立方体,所以碰撞时的法向量只会是(±1,0,0)、(0,±1,0)或(0,0,±1)这六种情况。关键是先确定碰撞的是体素的哪个面,再用反射公式计算新速度。

步骤1:精准判断碰撞面(修正碰撞检测逻辑)

原始代码直接移动后检测位置,无法精准定位碰撞面。需要先记录移动前的位置,碰撞时回溯,再对比位置变化确定法向量:

for(let step = 0; step < dist_max; step++) {
    // 记录移动前的位置
    const prev_pos = {x: pos.x, y: pos.y, z: pos.z};
    // 移动光线
    pos.x += vel.x;
    pos.y += vel.y;
    pos.z += vel.z;
    
    if(solid_at(pos)) {
        // 回溯到碰撞前的位置,避免光线陷入体素内部
        pos.x = prev_pos.x;
        pos.y = prev_pos.y;
        pos.z = prev_pos.z;
        
        // 计算当前所在体素的坐标(假设体素为1x1x1的立方体)
        const voxel = {
            x: Math.floor(pos.x),
            y: Math.floor(pos.y),
            z: Math.floor(pos.z)
        };
        let normal = {x: 0, y: 0, z: 0};
        
        // 判断X轴方向的碰撞:撞到体素的右侧(x+1)或左侧(x)面
        if(pos.x + vel.x >= voxel.x + 1) {
            normal.x = 1;
        } else if(pos.x + vel.x < voxel.x) {
            normal.x = -1;
        }
        // 判断Y轴方向的碰撞
        else if(pos.y + vel.y >= voxel.y + 1) {
            normal.y = 1;
        } else if(pos.y + vel.y < voxel.y) {
            normal.y = -1;
        }
        // 判断Z轴方向的碰撞
        else if(pos.z + vel.z >= voxel.z + 1) {
            normal.z = 1;
        } else if(pos.z + vel.z < voxel.z) {
            normal.z = -1;
        }
        
        // 计算反弹后的速度,1.0为完全反弹强度
        bounce(vel, normal, 1.0);
        break; // 单次碰撞后跳出,如需多次反弹可移除break继续循环
    }
}

步骤2:实现带强度控制的反弹函数

用标准的反射向量公式计算新速度,再乘以0-1之间的强度系数:

function bounce(vel, normal, strength) {
    // 计算入射速度与法向量的点积
    const dot = vel.x * normal.x + vel.y * normal.y + vel.z * normal.z;
    // 应用反射公式
    vel.x = vel.x - 2 * dot * normal.x;
    vel.y = vel.y - 2 * dot * normal.y;
    vel.z = vel.z - 2 * dot * normal.z;
    // 乘以反弹强度:0=完全吸收,1=完全反弹,0.5=半强度反弹
    vel.x *= strength;
    vel.y *= strength;
    vel.z *= strength;
}

关键原理说明

  • 因为体素是轴对齐的,法向量只会在单个轴上有±1的值,所以反射公式会自动反转对应轴的速度分量,其他分量保持不变,完全符合橡胶球的反弹逻辑。
  • 强度系数直接控制反弹后的速度大小,适配半透明体素的部分反射需求(比如强度0.7表示70%的速度保留,30%被吸收)。

适配非1x1体素的修改

如果你的体素不是1x1x1的大小,只需把边界判断的voxel.x + 1改成voxel.x * size + size,voxel.x改成voxel.x * size即可(size为单个体素的边长)。


内容的提问来源于stack exchange,提问作者MirceaKitsune

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 21:34:52