光线投射之伪3d

这篇具有很好参考价值的文章主要介绍了光线投射之伪3d。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

光线投射是一种在 2D 地图中创建 3D 透视的渲染技术。当计算机速度较慢时,不可能实时运行真正的 3D 引擎,光线投射是第一个解决方案。光线投射可以非常快,因为只需对屏幕的每条垂直线进行计算。

光线投射的基本思想如下:地图是一个 2D 方格网格,每个方格可以是 0(= 无墙),也可以是正值(= 具有特定颜色或纹理的墙)。

对于屏幕的每个 x(即屏幕的每个垂直条纹),发出一条从玩家位置开始的光线,其方向取决于玩家的观看方向和屏幕的 x 坐标。然后,让这条射线在 2D 地图上向前移动,直到它碰到一个地图方块(即墙)。如果它撞到了墙,计算这个击中点到玩家的距离,并使用这个距离来计算这堵墙必须在屏幕上绘制多高:墙越远,它在屏幕上越小,越近,看起来就越高。这些都是二维计算。此图显示了两条此类光线(红色)的自上而下的概述,它们从玩家(绿点​​)开始并击中蓝色墙壁:

 let rayDirX = Math.cos(theta) // -1 left 1 right
                            let rayDirY = Math.sin(theta) // -1 top 1 bottom

                            let cameraX = this.origin.x / cellWidth  // float
                            let cameraY = this.origin.y / cellHeight

                            let mapX = cameraX >> 0; // int
                            let mapY = cameraY >> 0;

                            // 从相机到当前格子的距离
                            let sideDistX, sideDistY;
                            // 1/cos =dist/dx= sec(正割)
                            // 1/sin =dist/dy =csc (余割)
                            // 计算距离,当x或y行进多少步后,当前距离是多少了
                            // 方便快速计算距离
                            // 保证x轴和y轴两个的步率是一样的
                            /**
                             *  ◿ c=5 a=3 b=4   
                             * cos=4/5, 1/cos=5/4  
                             * sin=3/5, 1/sin=5/3
                            */
                            let deltaDistX = rayDirX === 0 ? 1e30 : Math.abs(1 / rayDirX)
                            let deltaDistY = rayDirY === 0 ? 1e30 : Math.abs(1 / rayDirY)

                            let stepX, stepY;

                            // 侧面距离
                            if (rayDirX < 0) {
                                // 光线朝右
                                stepX = -1
                                // 当前相机距离当前格子距离是多少
                                sideDistX = (cameraX - mapX) * deltaDistX
                            } else {
                                stepX = 1
                                // 下一个格子,左侧距离相机距离是多少
                                sideDistX = (mapX + 1 - cameraX) * deltaDistX
                            }
                            // 正面,距离
                            if (rayDirY < 0) {
                                // 光线朝下
                                stepY = -1
                                // 当前相机距离当前格子距离是多少
                                sideDistY = (cameraY - mapY) * deltaDistY
                            } else {
                                stepY = 1;
                                // 下一个格子,左侧距离相机距离是多少
                                sideDistY = (cameraY + 1 - mapY) * deltaDistY
                            }

                            let isCollided = false;
                            let isSide = false // 是否是墙
                            let distance = 0

                            // 计算光线投射
                            while (!isCollided) {
                                // 如果横向距离小于纵向距离,就向x轴前进
                                if (sideDistX < sideDistY) {

                                    sideDistX += deltaDistX // 更新当前相机与当前格子的侧边距离
                                    mapX += stepX // 移动到左边或右边格子
                                    // 如果光线与侧边距近更近,那就是侧边
                                    isSide = true;
                                } else {
                                    sideDistY += deltaDistY
                                    mapY += stepY // 移动到上边或下边格子
                                    isSide = false;
                                }
                                if (mapY >= map.length || map[mapY][mapX] > 0) {
                                    isCollided = true;
                                }
                            }
                            if (isSide) {
                                distance = sideDistX - deltaDistX
                            } else {
                                distance = sideDistY - deltaDistY
                            }
                            // 是否鱼眼观看
                            // 鱼眼
                            let notFish = true;
                            if (notFish) {
                                distance = distance * Math.cos(theta - startRadian)
                            }

                            let lineHeight = canvasHeight / distance
                            // lineHeight=lineHeight*Math.cos(theta - startRadian)
                            // 距离越近物体看起来越高,距离越远看起来越矮
                            let halfCanvasHeight = canvasHeight * 0.5
                            let wallTop = halfCanvasHeight - lineHeight * 0.5
                            let wallBottom = halfCanvasHeight + lineHeight * 0.5
                            wallTop = clamp(wallTop, 0, canvasHeight)
                            wallBottom = clamp(wallBottom, 0, canvasHeight)


                            // 计算光线投射最终位置
                            let x = this.origin.x + rayDirX * (distance * cellWidth)
                            let y = this.origin.y + rayDirY * (distance * cellHeight)

光线投射之伪3d,3d,javascript,canvas,图形,图形渲染,raycasting



要找到光线在途中遇到的第一面墙,您必须让它从玩家的位置开始,然后始终检查光线是否在墙内。如果它在墙内(命中),则循环可以停止,计算距离,并以正确的高度绘制墙。如果光线位置不在墙壁内,则必须进一步追踪它:在该光线方向的方向上为其位置添加一定的值,对于这个新位置,再次检查它是否在墙壁内。继续这样做,直到最后撞到墙。

人类可以立即看到光线击中墙壁的位置,但不可能用单个公式找到光线立即击中哪个方块,因为计算机只能检查光线上有限数量的位置。许多光线投射器每一步都会为光线添加一个恒定值,但它有可能会错过墙壁!例如,对于这条红色光线,它的位置在每个红点处都被检查:

人类可以立即看到光线击中墙壁的位置,但不可能用单个公式找到光线立即击中哪个方块,因为计算机只能检查光线上有限数量的位置。许多光线投射器每一步都会为光线添加一个恒定值,但它有可能会错过墙壁!例如,对于这条红色光线,它的位置在每个红点处都被检查:

 

光线投射之伪3d,3d,javascript,canvas,图形,图形渲染,raycasting



正如你所看到的,光线直接穿过蓝色的墙壁,但计算机没有检测到这一点,因为它只在红色的位置检查点。检查的位置越多,计算机检测不到墙壁的机会就越小,但需要的计算也就越多。这里步距减半,所以现在他检测到光线穿过了墙,

 

光线投射之伪3d,3d,javascript,canvas,图形,图形渲染,raycasting



为了使用此方法获得无限精度,需要无限小的步长,因此需要无限数量的计算!这很糟糕,但幸运的是,有一种更好的方法,只需要很少的计算,就能检测到每面墙:其想法是检查射线将遇到的墙的每一侧。我们给每个正方形的宽度为 1,因此墙的每一边都是一个整数值,中间的位置在点之后有一个值。现在步长不是恒定的,它取决于到下一侧的距离:

 

光线投射之伪3d,3d,javascript,canvas,图形,图形渲染,raycasting



正如您在上图中看到的,光线准确地击中了墙壁上我们想要的位置。在本教程中介绍的方式中,使用基于 DDA 或“数字微分分析”的算法。DDA 是一种快速算法,通常用于方形网格,用于查找一条线击中了哪些方形(例如,在屏幕上绘制一条线,屏幕是方形像素的网格)。因此,我们还可以使用它来查找射线击中的地图的哪些方块,并在击中墙体方块后停止算法。

一些光线追踪器使用欧几里德角度来表示玩家和光线的方向,并用另一个角度确定视野。然而,我发现使用向量和相机要容易得多:玩家的位置始终是向量(x 和 y 坐标),但现在,我们也将方向设为向量:所以方向现在是由两个值确定:方向的 x 和 y 坐标。方向向量可以这样理解:如果你沿着玩家看的方向画一条线,穿过玩家的位置,那么这条线的每个点都是玩家位置与方向的倍数之和向量。方向向量的长度并不重要,重要的是它的方向。

这种使用向量的方法还需要一个额外的向量,即相机平面向量。在真正的 3D 引擎中,还有一个相机平面,并且该平面实际上是一个 3D 平面,因此需要两个向量(u 和 v)来表示它。然而,光线投射发生在 2D 地图中,因此这里的相机平面并不是真正的平面,而是一条线,并用单个向量表示。相机平面应始终垂直于方向矢量。相机平面代表计算机屏幕的表面,而方向矢量垂直于相机平面并指向屏幕内部。玩家的位置是一个点,是相机平面前面的一个点。屏幕某个 x 坐标的某条射线就是从该玩家位置开始的射线

光线投射之伪3d,3d,javascript,canvas,图形,图形渲染,raycasting

光线投射之伪3d,3d,javascript,canvas,图形,图形渲染,raycasting

光线投射之伪3d,3d,javascript,canvas,图形,图形渲染,raycasting文章来源地址https://www.toymoban.com/news/detail-703533.html

到了这里,关于光线投射之伪3d的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 奇舞周刊第497期:解锁 PDF 文件:使用 JavaScript 和 Canvas 渲染 PDF 内容

    记得点击文章末尾的“ 阅读原文 ”查看哟~ 下面先一起看下本期周刊 摘要 吧~  解锁 PDF 文件:使用 JavaScript 和 Canvas 渲染 PDF 内容 最近研究了 Web 的 FileSystemAccess Api,它弥补了 Web 长期以来缺少的能力:操作用户设备中的文件;而如今通过这个 Api 我们能够实现常见的文件

    2024年02月11日
    浏览(37)
  • JavaScript+canvas实现一个旋转的3D球动画效果

    1. 获取Canvas元素和设置初始参数 这部分代码主要是获取Canvas元素,并根据设备的DPI进行缩放。然后,定义了一些全局变量,包括Canvas的宽度、高度、球体旋转的角度和存储所有点的数组。 2. 定义一些常量 这部分代码定义了一些常量,如点的数量、点的半径、球半径等。 3.定

    2024年01月18日
    浏览(41)
  • 计算机视觉与图形学-神经渲染专题-pi-GAN and CIPS-3D

    《pi-GAN: Periodic Implicit Generative Adversarial Networks for 3D-Aware Image Synthesis 》 摘要 我们见证了3D感知图像合成的快速进展,利用了生成视觉模型和神经渲染的最新进展。然而,现有的方法在两方面存在不足:首先,它们可能缺乏底层的3D表示,或者依赖于视图不一致的渲染,从而合

    2024年02月14日
    浏览(52)
  • 体渲染光线行进算法【NeRF必读】

    为了积分由于内散射而沿射线产生的入射光,我们将射线穿过的体块分解为小体块元素,并将每个小体块元素对整个体块对象的贡献结合起来,有点像我们在 2D 编辑软件(例如 Photoshop)中将带有遮罩或 Alpha 通道(通常代表对象的不透明度)的图像彼此堆叠在一起。 这就是我

    2024年02月15日
    浏览(36)
  • 计算机视觉与图形学-神经渲染专题-Seal-3D(基于NeRF的像素级交互式编辑)

    摘要 随着隐式神经表示或神经辐射场 (NeRF) 的流行,迫切需要与隐式 3D 模型交互的编辑方法,以完成后处理重建场景和 3D 内容创建等任务。虽然之前的作品从不同角度探索了 NeRF 编辑,但它们在编辑灵活性、质量和速度方面受到限制,无法提供直接的编辑响应和即时预览。

    2024年02月13日
    浏览(32)
  • 【计算机图形学】OpenGL递归实现光线追踪

    计算机图形学课程设计:基于面向对象的光线跟踪算法设计与实现 目录 一、前言 二、项目实现与说明 1. 数据结构设计 1.1 光线 Ray 1.2 材质 Material 1.3 光照 Light 1.4 相机 Camera 1.5 球体Sphere 1.6 场景Scene 2. 算法实现 2.1 光线追踪算法原理与步骤 2.2 计算观察光线 2.3 光线与物体(球

    2024年02月08日
    浏览(32)
  • 3D激光线扫相机与结构光相机的区别

    激光相机基于三角测量,可精确捕获3D形状(数百万个点)。更精确地说,它们的工作原理是将激光点或激光线投射到物体上,然后用传感器捕获其反射。由于传感器的位置与激光源的距离已知,因此可以通过计算激光的反射角来进行精确的点测量。有了扫描仪到物体的距离的知

    2024年02月02日
    浏览(26)
  • Mars3d实现【按当前相机视域页在地球上投射视频】功能

    通过mars3d实现按当前相机视域页在地球上投射视频进行视频投射效果: 相关代码: 相关示例链接: http://mars3d.cn/editor-es5.html?id=graphic/video/video-edit 关键代码核心参考说明: 1.获得当前相机的视域边界位置,进行坐标转换后,转为new mars3d.graphic.VideoPrimitive({对象进行加载。 2

    2024年02月21日
    浏览(27)
  • Unity3D完成随时间变化的昼夜交替及光线变化

    Unity3D环境中的昼夜交替,并且控制好光线随着时间的变化而变化,太阳位置跟随经纬度的不同而不同。 重点: 时间范围为0~24小时; 太阳位置跟随当前经纬度; 太阳完成东升西落,世界坐标系中的x正为东方,x负为西方,z正为北; 太阳光线强度变化范围0~1,按照24小时的正

    2024年02月11日
    浏览(52)
  • 3D激光线轮廓传感器市场需求,预计2029年将达到734.86百万美元。

    3D激光线轮廓传感器采用激光三角反射式原理,采集不同材质表面的二维轮廓信息。通过特殊的透镜组,激光束被放大形成一条静态激光线投射到被测物体表面上。激光线在被测物体表面形成漫反射,反射光透过高质量光学系统,被投射到敏感感光矩阵上。除了传感器到被测

    2024年02月11日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包