Math 根据墙的旋转角度感知贴花的宽度

Math 根据墙的旋转角度感知贴花的宽度,math,graphics,geometry,raycasting,perspective,Math,Graphics,Geometry,Raycasting,Perspective,我正在使用JavaScript画布从头开始创建一个光线投射游戏 挑战的一部分(对我来说)是用随机图像(图片)装饰墙壁。我已经实现了墙壁、地板、天花板和精灵的绘制。 在绘制墙时,我为每个x(描绘屏幕坐标)存储到墙的距离(Z-BUFFER)、墙的高度(H-BUFFER)以及底层二维网格中像素的实际坐标(grid\u BUFFER) 我在墙上绘制贴花(图片)的方法如下(在确定理论上可以看到的贴花列表后): 计算贴标位置的距离(位置定义为位于观察者的网格顶点中间) 屏幕坐标屏幕X是根据从栅格坐标到屏幕

我正在使用JavaScript画布从头开始创建一个光线投射游戏

挑战的一部分(对我来说)是用随机图像(图片)装饰墙壁。我已经实现了墙壁、地板、天花板和精灵的绘制。 在绘制墙时,我为每个
x
(描绘屏幕坐标)存储到墙的距离(
Z-BUFFER
)、墙的高度(
H-BUFFER
)以及底层二维网格中像素的实际坐标(
grid\u BUFFER

我在墙上绘制贴花(图片)的方法如下(在确定理论上可以看到的贴花列表后):

  • 计算贴标位置的距离(位置定义为位于观察者的网格顶点中间)
  • 屏幕坐标
    屏幕X
    是根据从栅格坐标到屏幕坐标的转换矩阵计算的。这是正确的:
让贴花屏幕x=数学地板((RAYCAST.SCREEN_WIDTH/2)*(1+CAMERA.transformX/CAMERA.transformDepth))

  • 然后我检索相关贴花的图像数据,并获取其宽度和高度
  • 根据距离和观察角度,我计算出贴花的感知宽度。这就是真正的问题所在,因为我发现我计算的宽度并不完全准确
  • 有了所有这些信息,就可以很容易地计算左右屏幕坐标-绘制贴花的起始位置和结束位置,使用
    H-BUFFER
    计算高度因子,使用
    GRID\u BUFFER
    仅在属于此贴花的网格上绘制
我看到了宽度计算,即如果播放器方向与贴花面向空间的方向不相反,贴花将从播放器方向向量旋转一个角度(示例):

或者,如果播放器方向与贴花方向正好相反,则此角度为0°(示例):

我的第一种方法是使用反向播放器方向和贴花朝向方向的点积,从而获得向量之间的角度余弦,并将其用作减少感知宽度的一个因素:

let CosA = PLAYER.dir.mirror().dot(decal.facingDir);
let widthScale = CosA * (CAMERA.transformDepth / decal.distance);
此解决方案的问题是,当垂直时,因子为0且不绘制贴花,但当使用透视绘制墙时,情况不应如此。于是我开始即兴创作。我定义了
CAMERA.minPerspective
因子,如下所示。视野(FOV)为70°

我的直觉是(因为我缺乏透视和几何学知识,唉),对于小角度,因子应该保持为1。对于接近90°的角度,应该有一些最小的因素,以便贴花保持可见。所以我带来了这个“改进”代码:

这种方法的效果要好得多,但也有一些缺陷。从视觉上看,对于角度0-50°,折减系数太大。这可以观察到,如果我使用这样的宽度贴花,他们应该涵盖整个网格表面。(见下图;楼梯左侧下方的墙可见,贴花应覆盖整个网格,但不覆盖,因为
系数太小)

我已经搜索了Stack Overflow和Web的其他部分,寻找更好的解决方案,因为我的几何知识似乎也阻止我识别出正确的解决方案,如果它们不在这个上下文中


所以,请。在计算感知宽度时,可能存在确定性解决方案,无需再次使用光线投射相位,也无需使用我能够存储在光线投射相位中的信息。虽然在代码示例中使用JavaScript,但我认为这个问题不是针对任何编程语言的。

< P>我找到了一种解决方案,它保留(甚至改善)问题中的方法的简单性和时间复杂度。
  • 我在贴花定义中添加了两点-
    leftDrawStart
    rightStartDraw
    。这些在贴花点很容易计算 实例化,基于真实精灵(贴花)宽度和定义 网格(块)大小的。在进行这个计算时,我从摄像机透视图(不是网格坐标)考虑 LeftRoxStuts。< /LI>
  • 渲染贴花时,我使用变换矩阵(如下面的代码示例所述)计算
    leftDrawStart
    rightStartDraw
    的屏幕坐标,这些坐标来自它们的网格坐标:
  • 我区分计算出的绝对值
    drawStartX
    draundx
    ,以及它们的调整,以便它们符合屏幕边界,或者如果完全脱离屏幕,则从功能返回
  • 最后,甚至不需要贴花的感知宽度,因为纹理位置可以通过使用当前绘制<代码>条纹-绝对绘制开始和绝对绘制结束-绝对绘制开始之间的差值比率来计算:

与在光线投射步骤中加入贴花投射的方法相比,该方法完全准确且速度更快。

hmm这听起来像是光线投射与3D渲染引擎的结合。。。这很奇怪,但速度很快。然而,这恰恰导致了这样的问题。我会以不同的方式来做,并以与墙相同的方式渲染“贴花”图像。。。但是,为此,您需要将其存储为与墙纹理相同的分辨率/大小,并将其用作精灵(不渲染边界空间),以便混合或其他测试。。。
CAMERA.minPerspective = Math.cos(Math.radians((90 + this.FOV) / 2));
let CosA = PLAYER.dir.mirror().dot(decal.facingDir);
let FACTOR = Math.min(1, CosA + CAMERA.minPerspective);
FACTOR = Math.max(FACTOR, CAMERA.minPerspective);
let widthScale = FACTOR * (CAMERA.transformDepth / decal.distance);
transform(spritePos) {
    let invDet = 1.0 / (CAMERA.dir.x * PLAYER.dir.y - PLAYER.dir.x * CAMERA.dir.y);
    CAMERA.transformX = invDet * (PLAYER.dir.y * spritePos.x - PLAYER.dir.x * spritePos.y);
    CAMERA.transformDepth = invDet * (-CAMERA.dir.y * spritePos.x + CAMERA.dir.x * spritePos.y);
  }
let texX = (((stripe - drawStartX_abs) / (drawEndX_abs - drawStartX_abs)) * imageData.width) | 0;