如何在 HTML-Canvas 上渲染光线投射墙纹理
How to render raycasted wall textures on HTML-Canvas
我正在尝试构建光线投射引擎。我已经成功地使用 ctx.fillRect()
逐列渲染了一个场景,如下所示。
canvas-raycasting.png
我为上面的渲染写的代码:
var scene = [];// this contains distance of wall from player for an perticular ray
var points = [];// this contains point at which ray hits the wall
/*
i have a Raycaster class which does all math required for ray casting and returns
an object which contains two arrays
1 > scene : array of numbers representing distance of wall from player.
2 > points : contains objects of type { x , y } representing point where ray hits the wall.
*/
var data = raycaster.cast(wall);
/*
raycaster : instance of Raycaster class ,
walls : array of boundries constains object of type { x1 , y1 , x2 , y2 } where
(x1,y1) represent start point ,
(x2,y2) represent end point.
*/
scene = data.scene;
var scene_width = 800;
var scene_height = 400;
var w = scene_width / scene.length;
for(var i=0;i<scene.length;++i){
var c = scene[i] == Infinity ? 500 : scene[i] ;
var s = map(c,0,500,255,0);// how dark/bright the wall should be
var h = map(c,0,500,scene_height,10); // relative height of wall (farther the smaller)
ctx.beginPath();
ctx.fillStyle = 'rgb('+s+','+s+','+s+')';
ctx.fillRect(i*w,200-h*0.5,w+1,h);
ctx.closePath();
}
现在我正在尝试构建一个基于网络的 FPS(第一人称射击游戏)并坚持在 canvas 上渲染墙壁纹理。
ctx.drawImage()
方法接受参数如下
void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
ctx.drawImage_arguments
但是 ctx.drawImage()
方法将图像绘制为矩形,没有像 Wolfenstein 3D
那样的 3D 效果
我不知道该怎么做。
我应该使用ctx.tranform()
吗?如果是,如何?如果不是,我该怎么办?
我正在寻找用于使用 2D 光线投射产生伪 3d 效果的数学。
一些伪3d游戏是Wolfenstein 3D Doom
我正在尝试构建类似 this
的东西
谢谢你:)
您映射(或不映射,视情况而定)纹理坐标的方式未按预期工作。
I am looking for the Maths used to produce pseudo 3d effect using 2D raycasting
Wikipedia Entry for Texture Mapping 有一个很好的部分,其中包含有关 Doom 如何执行纹理贴图的特定数学运算。来自条目:
The Doom engine restricted the world to vertical walls and horizontal floors/ceilings, with a camera that could only rotate about the vertical axis. This meant the walls would be a constant depth coordinate along a vertical line and the floors/ceilings would have a constant depth along a horizontal line. A fast affine mapping could be used along those lines because it would be correct.
A "fast affine mapping" 只是纹理坐标的简单 2D 插值,对于您正在尝试的操作来说是一个合适的操作。 Doom 引擎的限制还在于
Doom renders vertical and horizontal spans with affine texture mapping, and is therefore unable to draw ramped floors or slanted walls.
您的逻辑似乎不包含任何用于在各种坐标空间之间转换坐标的代码。您至少需要在给定的光线追踪坐标和纹理坐标空间之间应用变换。这通常涉及矩阵数学并且非常常见,也可以称为 Projection,就像将点从一个 space/surface 投影到另一个。通过仿射变换,您可以避免使用矩阵来支持线性插值。
适用于您的变量(见上文)的坐标方程可能如下所示:
u = (1 - a) * wallStart + a * wallEnd
where 0 <= *a* <= 1
或者,您可以使用 Weak Perspective projection,因为您已经计算了很多数据。再次来自维基百科:
To determine which screen x-coordinate corresponds to a point at
A_x_,A_z_
multiply the point coordinates by:
B_x = A_x * B_z / A_z
where
B_x
is the screen x coordinate
A_x
is the model x coordinate
B_z
is the focal length—the axial distance from the camera center *to the image plane*
A_z
is the subject distance.
Because the camera is in 3D, the same works for the screen y-coordinate, substituting y for x in the above diagram and equation.
在您的例子中,A_x
是墙在世界空间中的位置。 B_z
是焦距,就是1
。 A_z
是您使用光线追踪计算的距离。结果是代表视空间平移的 x 或 y 坐标。
与main draw routine for W3D documents the techniques used to raytrace and transform coordinates for rendering the game. The code is quite readable even if you're not familiar with C/ASM and is a great way to learn more about your topics of interest. For more reading, I would suggest performing a search in your engine of choice for things like "matrix transformation of coordinates for texture mapping", or search the GameDev SE site类似。
该文件的特定区域 zero-in 将是从 ln 267 开始的此部分:
> ========================
> =
> = TransformTile
> =
> = Takes paramaters:
> = tx,ty : tile the object is centered in
> =
> = globals:
> = viewx,viewy : point of view
> = viewcos,viewsin : sin/cos of viewangle
> = scale : conversion from global value to screen value
> =
> = sets:
> = screenx,transx,transy,screenheight: projected edge location and size
> =
> = Returns true if the tile is withing getting distance
> =
一本关于 "teh Maths" 的好书是 this one - 我强烈推荐给任何想要创造或提高这些技能的人。
更新:
本质上,您将把图像中的像素(点)映射到矩形 wall-tile 上的点,如光线追踪所报告的那样。
伪(ish)代码:
var image = getImage(someImage); // get the image however you want. make sure it finishes loading before drawing
var iWidth = image.width, iHeight = image.height;
var sX = 0, sY = 0; // top-left corner of image. Adjust when using e.g., sprite sheets
for(var i=0;i<scene.length;++i){
var c = scene[i] == Infinity ? 500 : scene[i];
var s = map(c,0,500,255,0);// how dark/bright the wall should be
var h = map(c,0,500,scene_height,10); // relative height of wall (farther the smaller)
var wX = i*w, wY = 200 - h * 0.5;
var wWidth = w + 1, wHeight = h;
//... render the rectangle shape
/* we are using the same image, but we are scaling it to the size of the rectangle
and placing it at the same location as the wall.
*/
var u, v, uW, vH; // texture x- and y- values and sizes. compute these.
ctx.drawImage(image, sX, sY, iWidth, iHeight, u, v, uW, vH);
}
由于我不熟悉您执行光线追踪的代码、它的坐标系等,您可能需要进一步调整 wX
、wY
、[=20= 的值],以及 wHeight
(例如,将点从中心平移到 top-left 角)。
我正在尝试构建光线投射引擎。我已经成功地使用 ctx.fillRect()
逐列渲染了一个场景,如下所示。
canvas-raycasting.png
我为上面的渲染写的代码:
var scene = [];// this contains distance of wall from player for an perticular ray
var points = [];// this contains point at which ray hits the wall
/*
i have a Raycaster class which does all math required for ray casting and returns
an object which contains two arrays
1 > scene : array of numbers representing distance of wall from player.
2 > points : contains objects of type { x , y } representing point where ray hits the wall.
*/
var data = raycaster.cast(wall);
/*
raycaster : instance of Raycaster class ,
walls : array of boundries constains object of type { x1 , y1 , x2 , y2 } where
(x1,y1) represent start point ,
(x2,y2) represent end point.
*/
scene = data.scene;
var scene_width = 800;
var scene_height = 400;
var w = scene_width / scene.length;
for(var i=0;i<scene.length;++i){
var c = scene[i] == Infinity ? 500 : scene[i] ;
var s = map(c,0,500,255,0);// how dark/bright the wall should be
var h = map(c,0,500,scene_height,10); // relative height of wall (farther the smaller)
ctx.beginPath();
ctx.fillStyle = 'rgb('+s+','+s+','+s+')';
ctx.fillRect(i*w,200-h*0.5,w+1,h);
ctx.closePath();
}
现在我正在尝试构建一个基于网络的 FPS(第一人称射击游戏)并坚持在 canvas 上渲染墙壁纹理。
ctx.drawImage()
方法接受参数如下
void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
ctx.drawImage_arguments
但是 ctx.drawImage()
方法将图像绘制为矩形,没有像 Wolfenstein 3D
我不知道该怎么做。
我应该使用ctx.tranform()
吗?如果是,如何?如果不是,我该怎么办?
我正在寻找用于使用 2D 光线投射产生伪 3d 效果的数学。
一些伪3d游戏是Wolfenstein 3D Doom
我正在尝试构建类似 this
的东西谢谢你:)
您映射(或不映射,视情况而定)纹理坐标的方式未按预期工作。
I am looking for the Maths used to produce pseudo 3d effect using 2D raycasting
Wikipedia Entry for Texture Mapping 有一个很好的部分,其中包含有关 Doom 如何执行纹理贴图的特定数学运算。来自条目:
The Doom engine restricted the world to vertical walls and horizontal floors/ceilings, with a camera that could only rotate about the vertical axis. This meant the walls would be a constant depth coordinate along a vertical line and the floors/ceilings would have a constant depth along a horizontal line. A fast affine mapping could be used along those lines because it would be correct.
A "fast affine mapping" 只是纹理坐标的简单 2D 插值,对于您正在尝试的操作来说是一个合适的操作。 Doom 引擎的限制还在于
Doom renders vertical and horizontal spans with affine texture mapping, and is therefore unable to draw ramped floors or slanted walls.
您的逻辑似乎不包含任何用于在各种坐标空间之间转换坐标的代码。您至少需要在给定的光线追踪坐标和纹理坐标空间之间应用变换。这通常涉及矩阵数学并且非常常见,也可以称为 Projection,就像将点从一个 space/surface 投影到另一个。通过仿射变换,您可以避免使用矩阵来支持线性插值。
适用于您的变量(见上文)的坐标方程可能如下所示:
u = (1 - a) * wallStart + a * wallEnd
where 0 <= *a* <= 1
或者,您可以使用 Weak Perspective projection,因为您已经计算了很多数据。再次来自维基百科:
To determine which screen x-coordinate corresponds to a point at
A_x_,A_z_
multiply the point coordinates by:
B_x = A_x * B_z / A_z
where
B_x
is the screen x coordinate
A_x
is the model x coordinate
B_z
is the focal length—the axial distance from the camera center *to the image plane*
A_z
is the subject distance.
Because the camera is in 3D, the same works for the screen y-coordinate, substituting y for x in the above diagram and equation.
在您的例子中,A_x
是墙在世界空间中的位置。 B_z
是焦距,就是1
。 A_z
是您使用光线追踪计算的距离。结果是代表视空间平移的 x 或 y 坐标。
与main draw routine for W3D documents the techniques used to raytrace and transform coordinates for rendering the game. The code is quite readable even if you're not familiar with C/ASM and is a great way to learn more about your topics of interest. For more reading, I would suggest performing a search in your engine of choice for things like "matrix transformation of coordinates for texture mapping", or search the GameDev SE site类似。 该文件的特定区域 zero-in 将是从 ln 267 开始的此部分:
> ========================
> =
> = TransformTile
> =
> = Takes paramaters:
> = tx,ty : tile the object is centered in
> =
> = globals:
> = viewx,viewy : point of view
> = viewcos,viewsin : sin/cos of viewangle
> = scale : conversion from global value to screen value
> =
> = sets:
> = screenx,transx,transy,screenheight: projected edge location and size
> =
> = Returns true if the tile is withing getting distance
> =
一本关于 "teh Maths" 的好书是 this one - 我强烈推荐给任何想要创造或提高这些技能的人。
更新: 本质上,您将把图像中的像素(点)映射到矩形 wall-tile 上的点,如光线追踪所报告的那样。
伪(ish)代码:
var image = getImage(someImage); // get the image however you want. make sure it finishes loading before drawing
var iWidth = image.width, iHeight = image.height;
var sX = 0, sY = 0; // top-left corner of image. Adjust when using e.g., sprite sheets
for(var i=0;i<scene.length;++i){
var c = scene[i] == Infinity ? 500 : scene[i];
var s = map(c,0,500,255,0);// how dark/bright the wall should be
var h = map(c,0,500,scene_height,10); // relative height of wall (farther the smaller)
var wX = i*w, wY = 200 - h * 0.5;
var wWidth = w + 1, wHeight = h;
//... render the rectangle shape
/* we are using the same image, but we are scaling it to the size of the rectangle
and placing it at the same location as the wall.
*/
var u, v, uW, vH; // texture x- and y- values and sizes. compute these.
ctx.drawImage(image, sX, sY, iWidth, iHeight, u, v, uW, vH);
}
由于我不熟悉您执行光线追踪的代码、它的坐标系等,您可能需要进一步调整 wX
、wY
、[=20= 的值],以及 wHeight
(例如,将点从中心平移到 top-left 角)。