引言
本文主要讲解光线投射的原理——一种简单的伪3d渲染技术,广泛应用于早期的计算机图形渲染中。
具体来讲,什么是光线投射呢?
简单讲就是将线转化成面的投影技术,如上图所示,从视点发出一簇光线最终投射在几条线上,通过光线投射技术的转化,可以得到右图所示的3D投影。更直观的场景如下图所示。
可以发现,人物在简单的二维平面上下左右移动,可以映射成三维空间的前进、后退、向左向右。
基本原理
光线投射的核心思想是从视点(通常是摄像机的位置)向物体投射光线,判断光线与物体的交点,从而确定该物体的三维特征,比如纹理、颜色和亮度。
更确切地说,我们需要找到光线打在物体上时,此时的光线传播距离,这个距离与三维视图中对应位置的高度有直接关系:光线的传播距离越远,物体在视角中的高度就越小;反之,光线的传播距离越近,物体在视角中的高度就越大。
设光线的传播距离为 ( d ),视角中的墙体高度为 ( h ),那么墙体高度的计算公式通常是:
$$
h = \frac{C}{d}
$$
其中,C 是一个常数,用于调整物体高度的比例。
投射光线
- 建立坐标系$(x,y)$
- 初始化视点位置 $(x_0,y_0)$和光线的方向$\theta_0$
- $\theta_0$的范围为$[-\pi,\pi]$,取逆时针方向为正方向
1 | function castRay(xo,y0,angle) { |
确定交点
如何计算光线和物体的交点,也就是光线打到物体上,传播了多少距离?
1 | function castRay(x0,y0,angle) { |
绘制3D视图
1 | function draw3D(rayAngle) { |
边界处理
1. 光线传播距离的边界判断
离散的,所以会出现超出了墙壁,死循环。
这时候步长stepSize可以设置小点,但是再怎么小无法避免不出现死循环,所以可以设置一个最远传播距离maxDepth。
1 | while (distance < maxDepth) { |
2. 鱼眼效应矫正
光线传播距离与物体高度成反比。
当我们在屏幕上绘制一个矩形(比如墙体)时,由于光线投射的角度不同,如果不进行校正,绘制出的墙体可能会出现两边比中心更高的效果,这就是所谓的鱼眼效应。
这种视觉上的扭曲是不符合现实的。所以要进行校对。
我们通过计算每个光线的实际距离和视线方向的夹角,用余弦函数校正距离:
1 | const distance = ray.distance * Math.cos(rayAngle - player.dir * Math.PI / 2); |
这样我们得到的距离是光线在视线方向上的投影距离,从而消除了由于角度不同带来的距离误差。
注意distance + 0.0001,防止分母为0。
实现
参考
1. Lode’s Computer Graphics Tutorial
2. Ray-Casting Tutorial For Game Development And Other Purposes