本教程的目的是解释如何在没有WebGL的情况下为Web构建简单的3D引擎。我们将首先看到如何存储3D形状。然后,我们将在两个不同的视图中看到如何显示这些形状。
>钥匙要点
>仅利用JavaScript,就可以创建一个简单的3D引擎,而无需诸如WebGl之类的高级库,使其适合呈现基本形状,例如立方体。 在该发动机中,多面体在3D建模中至关重要,在该发动机中,使用扁平面近似复杂的形状,类似于2D中的多边形。 通过存储对顶点而不是复制它们来实现有效的内存管理,从而大大降低了内存使用情况,因为每个顶点都在多个面上共享。
猜猜如何存储多面体,我们必须记住如何在数学中识别这种东西。在您的学年期间,您肯定已经做了一些基本的几何形状。例如,要识别一个正方形,您将其称为ABCD,用A,B,C和D称为构成正方形每个角落的顶点。 对于我们的3D发动机,
将是相同的。我们将首先存储形状的每个顶点。然后,此形状将列出其面孔,每个面都会列出其顶点。为代表顶点,我们需要正确的结构。在这里,我们创建一个以存储顶点的坐标的类
现在可以像任何其他对象一样创建顶点:
><span>var <span>Vertex</span> = function(x<span>, y, z</span>) { </span> <span>this.x = parseFloat(x); </span> <span>this.y = parseFloat(y); </span> <span>this.z = parseFloat(z); </span><span>}; </span>
接下来,我们创建一个代表多面体的类。让我们以立方体为例。班级的定义在下面,并在此之后进行解释。
><span>var A = new Vertex(10, 20, 0.5); </span>
使用此类,我们可以通过指示其中心及其边缘长度来创建一个虚拟立方体。
<span>var <span>Cube</span> = function(center<span>, size</span>) { </span> <span>// Generate the vertices </span> <span>var d = size / 2; </span> <span>this.vertices = [ </span> <span>new Vertex(center.x - d, center.y - d, center.z + d), </span> <span>new Vertex(center.x - d, center.y - d, center.z - d), </span> <span>new Vertex(center.x + d, center.y - d, center.z - d), </span> <span>new Vertex(center.x + d, center.y - d, center.z + d), </span> <span>new Vertex(center.x + d, center.y + d, center.z + d), </span> <span>new Vertex(center.x + d, center.y + d, center.z - d), </span> <span>new Vertex(center.x - d, center.y + d, center.z - d), </span> <span>new Vertex(center.x - d, center.y + d, center.z + d) </span> <span>]; </span> <span>// Generate the faces </span> <span>this.faces = [ </span> <span>[this.vertices[0], this.vertices[1], this.vertices[2], this.vertices[3]], </span> <span>[this.vertices[3], this.vertices[2], this.vertices[5], this.vertices[4]], </span> <span>[this.vertices[4], this.vertices[5], this.vertices[6], this.vertices[7]], </span> <span>[this.vertices[7], this.vertices[6], this.vertices[1], this.vertices[0]], </span> <span>[this.vertices[7], this.vertices[0], this.vertices[3], this.vertices[4]], </span> <span>[this.vertices[1], this.vertices[6], this.vertices[5], this.vertices[2]] </span> <span>]; </span><span>}; </span>
>立方体类的构造函数首先从指示中心的位置生成立方体的顶点。模式将更清晰,因此请参阅下面我们生成的八个顶点的位置:
<span>var cube = new Cube(new Vertex(0, 0, 0), 200); </span>
然后,我们列出了面孔。每张脸是一个正方形,因此我们需要为每个脸部指出四个顶点。在这里,我选择代表带有数组的面孔,但是,如果您需要的话,您可以为此创建一个专门的类。
>创建面部时,我们会使用四个顶点。我们不需要再次表示它们的位置,因为它们存储在the.vertices [i]对象中。这是实用的,但是还有另一个原因。>
默认情况下,JavaScript试图使用最少的内存数量。为了实现这一目标,它不会复制通过函数参数传递的对象,甚至存储在数组中。就我们的情况而言,这是完美的行为。
实际上,每个顶点包含三个数字(它们的坐标),以及如果需要添加几种方法。如果对于每张脸,我们存储了顶点的副本,我们将使用很多内存,这是没有用的。在这里,我们只有参考文献:坐标(和其他方法)一次存储一次,只有一次。由于每个顶点由三个不同的面都使用,而不是存储参考而不是副本,因此我们将所需的内存除以三个(或多或少)!
>!我们需要三角形吗?
如果您已经玩过3D(例如,使用Blender之类的软件,或WebGl等库),也许您听说我们应该使用三角形。在这里,我选择不使用三角形。>
这个选择背后的原因是本文是对该主题的介绍,我们将显示类似立方体的基本形状。在我们的情况下,使用三角形展示正方形比其他任何事物都更像是并发症。
> 但是,如果您打算构建一个更完整的渲染器,那么通常需要知道,三角形是首选的。这样的主要原因有两个:
>
>在这里,我们会做同样的事情:我们不会碰到面孔。我们在每个顶点上应用所需的操作,我们完成了。当面部使用参考时,面部的坐标会自动更新。例如,查看我们如何翻译我们先前创建的立方体:
渲染图像
<span>var <span>Vertex</span> = function(x<span>, y, z</span>) { </span> <span>this.x = parseFloat(x); </span> <span>this.y = parseFloat(y); </span> <span>this.z = parseFloat(z); </span><span>}; </span>我们知道如何存储3D对象以及如何对它们作用。现在是时候看看如何查看它们了!但是,首先,我们需要该理论的少量背景,以了解我们将要做什么。
>目前,我们存储3D坐标。但是,屏幕只能显示2D坐标,因此我们需要一种将3D坐标转换为2D的方法:这就是我们所谓的数学投影。 3D到2D投影是由称为虚拟相机的新对象进行的抽象操作。该摄像机将3D对象带入2D对象,并将其发送到渲染器,以将其显示在屏幕上。我们将在这里假设我们的相机位于3D空间的起源(因此其坐标为(0,0,0))。自本文开头以来,我们已经讨论了坐标,由三个数字表示:x,y和z。但是要定义坐标,我们需要一个基础:z是垂直坐标吗?它是到顶部还是底部?没有通用的答案,也没有惯例,因为您可以选择自己想要的任何东西。您唯一要记住的是,当您对3D对象采取行动时,您必须保持一致,因为公式会根据它而改变。在本文中,我选择了您可以在上面的立方体模式中看到的基础:x从左至右,y从后到正面,从下到顶部。 >
如何渲染我们的场景 >数组可以包含几个以渲染的对象。这些对象必须尊重一件事:拥有一个名为“面孔”的公共属性,该属性是一个数组列出对象的所有面(如我们先前创建的Cube)。这些面孔可以是任何东西(如果需要的话,正方形,三角形,甚至是十二杆):它们只需要列出其顶点的数组。 让我们看一下功能的代码,然后说明: 这个功能值得一些解释。更确切地说,我们需要解释该项目()函数是什么,这些DX和DY参数是什么。其余的基本上除了列出对象,然后绘制每个脸。
>而不是命名坐标x和z我在这里选择将z坐标命名为y,而是要维护我们经常在2D几何形状中找到的经典惯例,但是如果您喜欢的话,可以保留z。
>
最后,最后一个细节:为什么我们使用-y而不是直接使用y?答案是我们选择的基础:Z轴针对顶部。然后,在我们的场景中,带有正面Z坐标的Vertice将上升。但是,在画布上,Y轴定向到底部:带正Y坐标的佛特(Vertice)将向下移动。这就是为什么我们需要在画布上定义画布的y坐标为我们场景的z坐标的逆转。 >现在呈渲染函数很明显,是时候查看project()。
>您现在可以测试自本文开头以来我们编写的所有代码:它有效!恭喜,您刚刚在平面上显示了一个3D对象! 请参见codepen上的sitepoint(@sitepoint)的笔3D拼字图。 >
。
,但是,在现实生活中,我们的眼睛更像以下模式。 基本上,我们有两个步骤: >
现在,如果您从侧面看同一场景,则获得了类似的模式,允许您获得Z'的值,这要归功于Z,Y和D:Z'= D / Y *Z。 我们现在可以使用透视视图编写project()函数: 可以在下面的实时示例中测试此功能。再一次,您可以与Cube进行交互。
其他事情可以更改。我们将相机放置在空间的原点,但是您可以移动它(在投射顶点之前需要更改基础)。另外,放置在相机后面的顶点在这里绘制,这不是我们想要的。剪裁平面可以解决这个问题(易于理解,易于实现)。
>!
>向JavaScript 3D引擎添加照明涉及创建光源并实现阴影模型。光源可以是定向,点或聚光灯。确定表面如何响应光的阴影模型可以很简单(例如兰伯特阴影)或复杂的(例如基于物理的渲染)。 >如何将动画添加到我的javascript 3D引擎? >调试JavaScript 3D引擎可能会很具有挑战性3D图形的复杂性。但是,WebGL检查员和Chrome DevTools之类的工具可能非常有帮助。这些工具允许您检查WebGL上下文的状态,查看缓冲区和纹理的内容,并跨过着色器。>在投影我们的对象之前,让我们编写显示它们的功能。此函数接受为参数,列出了渲染对象的数组,必须用于显示对象的画布的上下文,以及在正确位置绘制对象所需的其他详细信息。
>
<span>var <span>Vertex</span> = function(x<span>, y, z</span>) {
</span> <span>this.x = parseFloat(x);
</span> <span>this.y = parseFloat(y);
</span> <span>this.z = parseFloat(z);
</span><span>};
</span>
<span>var A = new Vertex(10, 20, 0.5);
</span>
>实际上,project()函数返回虚拟2D平面上的坐标,但具有相同的原点,而不是我们为3D空间定义的坐标。但是,我们希望原点位于画布的中心,这就是为什么我们翻译坐标的原因:顶点(0,0)不在画布的中心,但是(0 dx,0 dy)是,如果我们是选择DX并明智地选择DY。正如我们想要的(DX,DY)位于画布的中心一样,我们实际上没有选择,我们定义dx = canvas.width / 2和dy = canvas.height / 2. >
我们有三个坐标,我们只需要两个。在这种情况下,最简单的事情是什么?删除其中一种坐标。这就是我们在拼字法中所做的。我们将删除表示深度的坐标:y坐标。<span>var <span>Vertex</span> = function(x<span>, y, z</span>) {
</span> <span>this.x = parseFloat(x);
</span> <span>this.y = parseFloat(y);
</span> <span>this.z = parseFloat(z);
</span><span>};
</span>
此功能在下面的实时示例中实现,您可以通过与鼠标旋转来与立方体进行交互。
>
>与拼字法视图相反,这里的飞机的确切位置很重要:如果将飞机放在远离相机的位置,则不会获得与将其放置在接近的情况下相同的效果。在这里,我们将其放在距相机的距离D。>猜测我们将如何计算这些坐标,让我们采取另一个观点并查看与上面相同的模式,但从顶部看。
>我们可以识别拦截定理中使用的配置。在上面的架构上,我们知道一些值:x,y和d等。我们要计算X',以便应用截距定理并获得此方程:x'= d / y * x。
<span>var <span>Vertex</span> = function(x<span>, y, z</span>) {
</span> <span>this.x = parseFloat(x);
</span> <span>this.y = parseFloat(y);
</span> <span>this.z = parseFloat(z);
</span><span>};
</span>
请参阅codepen上的sitepoint(@sitepoint)的笔3D透视图。>
>另外,我们没有谈论纹理。在这里,我们所有的形状都具有相同的颜色。您可以通过例如在对象中添加颜色属性来更改它,以了解如何绘制它们。您甚至可以选择每张脸部的一种颜色,而无需更改很多事情。您也可以尝试在脸上显示图像。但是,更加困难,并且详细说明如何做这样的事情将花费整篇文章。
>在JavaScript中构建3D引擎的先决条件是什么?熟悉HTML和CSS也是有益的。 3D数学的知识,包括媒介,矩阵和四元素,至关重要。了解计算机图形的基础知识,例如渲染管道,着色器和纹理映射,也将很有帮助。
我如何开始学习3D数学用于JavaScript 3D引擎开发?在线可用的几种资源可以学习3D数学。像汗学院这样的网站提供了有关线性代数和矢量微积分的课程,这些课程对于理解3D数学至关重要。诸如“图形和游戏开发的3D数学底漆”之类的书也可能会有所帮助。>在JavaScript中构建3D引擎的最佳图书馆是什么?在JavaScript中构建3D引擎的两个最受欢迎的库。这两个库都为WebGL提供了高级接口,从而更容易创建复杂的3D场景。他们还提供了广泛的文档和社区支持。>如何优化我的JavaScript 3D引擎以提高性能?>
>有几种方法可以优化您的3D引擎以提高性能。一种方法是通过使用诸如实例和批处理之类的技术来最大程度地减少呼叫的数量。另一种方法是使用压缩纹理和几何形状减少发送到GPU的数据量。您还可以优化着色器以获得更好的性能。>如何处理JavaScript 3D引擎中的用户输入?>在JavaScript 3D引擎中处理用户输入通常涉及聆听键盘和鼠标事件。您可以使用浏览器提供的“ AddEventListener”方法来聆听这些事件。然后,您可以使用事件数据来控制3D场景中的相机或其他元素。
>如何将照明添加到我的JavaScript 3D引擎?
>将纹理添加到JavaScript 3D引擎中涉及加载图像文件,创建纹理对象并将它们映射到几何形状上。您可以使用浏览器提供的“图像”对象来加载图像。然后,您可以使用WebGl提供的“ gl.CreateTexture”方法创建纹理对象。
以上是使用JavaScript构建3D引擎的详细内容。更多信息请关注PHP中文网其他相关文章!