我的github

I’d to like to share with you how I’ve learned to build what’s known as a “3D soft engine” through a series of tutorials. “Software engine” means that we will use only the CPU to build a 3D engine in an old school way (remember Doom on your 80386 ?). I’ll share with you the C#, TypeScript and JavaScript versions of the code. In this list, you should then find your favorite language or at least something near your favorite one. The idea is to help you transposing the following samples & concepts on your favorite platform. You’ll find the Visual Studio 2012 C#/TS/JS solutions to download at the end also.

我想要通过一系列教程和你分享我是如何学习构建一个“3D软引擎”的。“软引擎”即为“软件引擎”,顾名思义,意味着你只需要CPU就可构建一个3D引擎,以一种古老的方式(还记得80386上的Doom游戏吗?)。我会和你分享C#版本、TypeScript版本和JavaScript版本的代码。在这个列表里,你应该能够找到你感兴趣的语言的版本吧,或者至少接近你喜欢的语言的语言。这个方式是为了帮助你将以下的代码和概念翻译到你自己感兴趣的语言。在最后还会提供Visual Studio 2012 C#/TS/JS的解决方案供你下载。

So why building a 3D soft engine? Well, it’s simply because it really helps understanding how modern 3D works with our GPUs. Indeed, I’m currently learning the basics of 3D thanks to internal workshops delivered within Microsoft by the awesome David Catuhe. He’s been mastering 3D for many years now and matrices operations is hard-coded in his brain. When I was young, I was dreaming to be able to write such engines but I had the feeling it was too complex for me. Finally, you’ll see that this is not – that – complex. You simply need someone that will help you understanding the underlying principles in a simple way.

所以为什么要构建一个3D软引擎呢?好吧,仅仅是因为它能够很好地帮助你理解当前3D是如何在我们的GPU上工作的。当然,我当前正在学习的3D基础原理要多亏了微软内部的讨论会,在讨论会上David Catuhe传递给我的知识。他已经掌握了3D很多年了,矩阵向量的计算早已深深的刻在了他的脑子里。当我很小的时候,我多么希望能够写一个这样的三维引擎,但是没有人教给我。所以当时对我来说这是极其困难的事。文章最终,你将会明白其实这并没有所想的那么困难。你只是需要一个人来引导你,用一种简单的方式理解其背后的工作原理。

Through this series, you will learn how to project some 3D coordinates (X, Y, Z) associated to a point (a vertex) on a 2D screen, how to draw lines between each point, how to fill some triangles, to handle lights, materials and so on. This first tutorial will simply show you how to display 8 points associated to a cube and how to move them in a virtual 3D world.

通过本系列教程,你将会学习如何将一些和点相关的3D坐标(X,Y,Z)投影到2D屏幕,如何绘制点之间的连线,如何填充三角形,如何控制光线,材料等等。本第一个教程将简单地向你展示如何将立方体的8个点显示出来,以及如何将它们在三维世界中移动。

This tutorial is part of the following series:

本教程是如下系列中的一部分:

1 – Writing the core logic for camera, mesh & device object (this article)

       书写相机、网格和设备的核心逻辑(本文)
2 – Drawing lines and triangles to obtain a wireframe rendering

       绘制线、三角形从而得到一个线框渲染
3 – Loading meshes exported from Blender in a JSON format

      从Blender中导入网格模型,以json的格式
4 – Filling the triangle with rasterization and using a Z-Buffer

      使用光栅化和Z缓冲区填充三角形
4b – Bonus: using tips & parallelism to boost the performance

      额外奖励:使用技巧和并行度来提高性能
5 – Handling light with Flat Shading & Gouraud Shading

      使用平面着色和Gouraud着色处理光线
6 – Applying textures, back-face culling and WebGL

      应用纹理、背面剔除和WebGL

If you’re following the complete series, you will know how to build your own 3D software engine!

如果你遵照本完整的系列教程,你将会学会如何构建你自己的3D软件引擎!

Your engine will then start by doing some wireframe rendering, then rasterization followed by gouraud shading and lastly by applying textures like demonstrated in this sample: http://david.blob.core.windows.net/html5/SoftEngineProgression/wireframe/index.html

然后,您的引擎将一开始进行一些线框渲染,然后在如上的示例上进行进行光栅化,然后进行gouraud着色,最后设置纹理:http://david.blob.core.windows.net/html5/SoftEngineProgression/wireframe/index.html

It’s demonstrating the various stage we’ll cover during this series going from wireframe to textures.

它展示了我们将在本系列中涵盖的从线框到纹理的各个阶段。
By properly following this first tutorial, you’ll learn how to rotate the 8 points of a cube to obtain the following result at the end:

通过正确地遵循第一个教程,您将学习如何旋转立方体的8个点,以在最后获得以下结果:

Disclaimer: some of you are wondering why I’m building this 3D software engine rather than using GPU. It’s really for educational purposes. Of course, if you need to build a game with fluid 3D animations, you will need DirectX or OpenGL/WebGL. But once you will have understood how to build a 3D soft engine, more “complex” engine will be simpler to understand. To go further, you definitely should have a look to the BabylonJS WebGL engine built by David Catuhe and I. More details & tutorials here: https://doc.babylonjs.com

声明:你们中的有些人可能会好奇为什么我创建这个3D软件引擎而非使用GPU。这只是为了教育的目的。当然,如果你需要创建一个游戏,带有流体的3D动画的游戏,你需要DirectX或者OpenGL/WebGL。但是一旦你已经明白如何创建一个3D软引擎,更“复杂”的引擎将会更容易理解。更远一些说,你肯定需要看一下BabylonJS的webgl引擎,由David Catuhe和我创建的。更多的详细资料:https://doc.babylonjs.com。

Check the MVA video training version: with David Catuhe, we’ve made a free 8 modules course to let you learn the basics of 3D, WebGL and Babylon.js. The first module is containing a 40 min video version of this tutorial series: Introduction to WebGL 3D with HTML5 and Babylon.js

查看MVA视频培训版本:与David Catuhe一起,我们制作了一个免费的8个模块课程,让您学习3D、WebGL和Babylon.js的基础知识。第一个模块包含本系列教程的40分钟视频版本:使用HTML5和Babylon.js的WebGL 3D简介。

Reading prerequisites(预备知识)

I’ve been thinking on how to write these tutorials for a long time now. And I’ve finally decided not to explain each required principle myself. There is a lot of good resources on the web that will explain those important principles better than I. But I’ve then spent quite some time browsing the web for you to choose, according to myself, the best one to read:

很长一段时间以来,我一直在思考如何编写这些教程。我最终决定不自己解释每一个必要的原则。网上有很多好的资源可以比我更好地解释这些重要的原则。但据我自己说,我花了相当长的时间浏览网页,让你选择最好的阅读方式:

– World, View and Projection Matrix Unveiled
– Tutorial 3 : Matrices that will provide you an introduction to matrices, the model, view & projection matrices.
– Cameras on OpenGL ES 2.x – The ModelViewProjection Matrix : this one is really interesting also as it explains the story starting by how cameras and lenses work.
– Transforms (Direct3D 9)
– A brief introduction to 3D: an excellent PowerPoint slides deck ! Read at least up to slide 27. After that, it’s too linked to a technology talking to GPU (OpenGL or DirectX).
– OpenGL Transformation

Read those articles by not focusing on the technology associated (like OpenGL or DirectX) or on the concept of triangles you may have seen in the figures. We will see that later on.

阅读这些文章时,不要关注相关技术(如OpenGL或DirectX)或你可能在图中看到的三角形概念。我们稍后会看到。

By reading those articles, you really need to understand that there is a series of transformations done that way:

通过阅读这些文章,你真的需要明白,有一系列的转换是通过这种方式完成的:

– we start by a 3D object centered on itself

   我们从一个以自身为中心的3D对象开始
– the same object is then moved into the virtual 3D world by translation, scaling or rotation operations via matrices

   然后通过矩阵进行平移、缩放或旋转操作,将同一对象移动到虚拟3D世界中
– a camera will look at this 3D object positioned in the 3D world

   相机将看向这个位于3D世界中的3D对象
– the final projection of all that will be done into a 2D space which is your screen

   所有这些都将最终投影到2D空间中,即您的屏幕

All this magic is done by cumulating transformations through matrices operations. You should really be at least a bit familiar with those concepts before running through these tutorials. Even if you don’t understand everything by reading them the first time. You should read them first. You will probably go back to those articles later on while writing your own version of this 3D soft engine. This is completely normal, don’t worry!  The best way to learn 3D if by experimenting and doing mistakes.

所有这些魔术都是通过矩阵运算累积变换来实现的。在学习这些教程之前,您应该至少对这些概念有一点熟悉。即使你第一次读的时候并不是什么都懂。你应该先读一下。稍后,您可能会在编写自己版本的3D软引擎时回到这些文章。这完全正常,别担心!学习3D的最佳方法是通过实验和犯错。

We won’t neither spend some times on how matrix operations works. The good news is that you don’t really need to understand matrices. Simply view it as a black box doing the right operations for you. I’m not a master of matrices but I’ve managed to write a 3D soft engine by myself. So you should also succeed in doing so.

我们也不会花一些时间讨论矩阵运算是如何工作的。好消息是,你并不需要真正理解矩阵。只需将其视为一个为您做正确操作的黑盒。我不是矩阵大师,但我已经自己写了一个3D软引擎。所以你也应该成功地做到这一点。

We will then use libraries that will do the job for us: SharpDX, a managed wrapper on top of DirectX, for C# developers and babylon.math.js written by David Catuhe for JavaScript developers. I’ve rewritten it in TypeScript also.

然后,我们将使用为我们完成任务的库:SharpDX,一个在DirectX之上的托管包装器,用于C#开发人员,以及David Catuhe为JavaScript开发人员编写的babylon.math.js。我也用TypeScript重写了它。

Software prerequisites(软件预备)

We will write a WinRT/XAML Windows Store Apps in C# and/or a HTML5 application with TypeScript/JavaScript. So if you want to use the C# samples as-is, you need to install:

我们将用C#编写WinRT/XAML Windows应用商店应用程序和/或用TypeScript/JavaScript编写HTML5应用程序。因此,如果您想按原样使用C#示例,您需要安装:

1 – Windows 8
2 – Visual Studio 2012 Express for Windows Store Apps. You can download it for free: http://msdn.microsoft.com/en-US/windows/apps/br211386

If you choose to use the TypeScript samples, you need to install it from: http://www.typescriptlang.org/#Download . All samples have been updated and tested successfully with TypeScript 0.9.

如果您选择使用TypeScript示例,则需要从以下位置安装:http://www.typescriptlang.org/#Download . 所有样本都已使用TypeScript 0.9成功更新和测试。

You will find the plug-in for Visual Studio 2012 but there are other options available: Sublime Text, Vi, Emacs: TypeScript enabled! On my side, I’ve learned TypeScript by porting the C# version of my code to TypeScript. If you’re also interested in learning TypeScript, a first good introduction is this webcast: Anders Hejlsberg: Introducing TypeScript . Please install also Web Essentials 2012 which had a full support for TypeScript preview and compilation.

您可以找到Visual Studio 2012的插件,但还有其他可用选项:Sublime Text、Vi、Emacs:TypeScript enabled!就我而言,我通过将C#版本的代码移植到TypeScript来学习TypeScript。如果你也对学习TypeScript感兴趣,第一个很好的介绍是这个网络直播:Anders Hejlsberg:介绍TypeScript。请同时安装Web Essentials 2012,它完全支持TypeScript预览和编译。

If you choose JavaScript, you just need your favorite IDE and a HTML5 compatible browser. Please create a project named “SoftEngine” targeting the language you’d like to use. If it’s C#, add the “SharpDX core assembly” by using NuGet on your solution:

如果你选择JavaScript,你只需要你最喜欢的IDE和兼容HTML5的浏览器。请针对您要使用的语言创建一个名为“SoftEngine”的项目。如果是C#,请在您的解决方案中使用NuGet添加“SharpDX核心组件”:

If it’s TypeScript, download babylon.math.ts. If’ it’s JavaScript download babylon.math.js. Add a reference to those files in both cases.

如果是TypeScript,请下载babylon.math.ts。如果是JavaScript,请下载babylon.math.js。在这两种情况下都添加对这些文件的引用。

Back buffer & rendering loop(后缓冲区和渲染循环)

In a 3D engine, we’re rendering the complete scene during each frame with the hope of keeping an optimal 60 frames per second (FPS) to keep fluid animations. To do our rendering job, we need what we call a back buffer. This could be seen as 2 dimensional array mapping the screen/window size. Every cell of the array is mapped to a pixel on the screen.

在3D引擎中,我们在每一帧中渲染完整的场景,希望保持最佳的每秒60帧(FPS),以保持流畅的动画。为了完成渲染工作,我们需要一个我们称之为后台缓冲区的东西。这可以看作是映射屏幕/窗口大小的二维阵列。阵列中的每个单元都映射到屏幕上的一个像素。

In our XAML Windows Store Apps, we will use a byte[] array that will act as our dynamic back buffer. For every frame being rendered in the animation loop (tick), this buffer will be affected to a WriteableBitmap acting as the source of a XAML image control that will be called the front buffer. For the rendering loop, we’re going to ask to the XAML rendering engine to call us for every frame it will generate. The registration is done thanks to this line of code:

在我们的XAML Windows应用商店应用程序中,我们将使用byte[]数组作为我们的动态后台缓冲区。对于动画循环(tick)中渲染的每一帧,此缓冲区都将受到WriteableBitmap的影响,该WriteableBitprint将作为XAML图像控件的源,该XAML图像控件将被称为前缓冲区。对于渲染循环,我们将要求XAML渲染引擎为它将生成的每一帧调用我们。注册是通过这行代码完成的:

CompositionTarget.Rendering += CompositionTarget_Rendering;

In HTML5, we’re going to use of course the <canvas /> element. The canvas element has already a back buffer data array associated to it. You can access it through the getImageData() and setImageData() functions. The animation loop will be handled by the requestAnimationFrame() function. This one is much more efficient that an equivalent of a setTimeout(function() {], 1000/60) as it’s handled natively by the browser that will callback our code only when it will be ready to draw.

在HTML5中,我们当然要使用<canvas>元素。canvas元素已经有一个后台缓冲区数据数组与其关联。您可以通过getImageData()和setImageData(()函数访问它。动画循环将由requestAnimationFrame()函数处理。这一个比等效的setTimeout(function(){],1000/60)要高效得多,因为它是由浏览器本地处理的,只有当它准备好绘制时才会回调我们的代码。

Note: in both cases, you can render the frames in a different resolution that the actual width & height of the final window. For instance, you can have a back buffer of 640×480 pixels whereas the final display screen (front buffer) will be in 1920×1080. In XAML and thanks to CSS in HTML5, you will then benefit from “hardware scaling”. The rendering engines of XAML and of the browser will stretch the back buffer data to the front buffer window by even using an anti-aliasing algorithm. In both cases, this task is done by the GPU. This is why we call it “hardware scaling” (hardware is the GPU). You can read more about this topic addressed in HTML5 here: Unleash the power of HTML 5 Canvas for gaming . This approach is often used in games for instance to boost the performance as you have less pixels to address.

注意:在这两种情况下,可以使用与最终窗口的实际宽度和高度不同的分辨率来渲染帧。例如,您可以有一个640×480像素的后缓冲区,而最终的显示屏(前缓冲区)将是1920×1080。在XAML中,由于HTML5中的CSS,您将从“硬件扩展”中受益。XAML和浏览器的渲染引擎甚至会使用抗锯齿算法将后缓冲区数据扩展到前缓冲区窗口。在这两种情况下,此任务都是由GPU完成的。这就是为什么我们称之为“硬件缩放”(硬件就是GPU)。你可以在这里阅读更多关于HTML5主题的内容:释放HTML5 Canvas在游戏中的力量。例如,这种方法经常在游戏中使用,以提高性能,因为需要处理的像素较少。

Camera & Mesh objects(相机和网格对象)

Let’s start coding. First, we need to define some objects that will embed the details needed for a camera and for a mesh. A mesh is a cool name to describe a 3D object.

让我们开始编码。首先,我们需要定义一些对象,这些对象将嵌入相机和网格所需的细节。网格是描述三维对象的一个很酷的名称。

Our Camera will have 2 properties: its position in the 3D world and where it’s looking at, the target. Both are made of 3D coordinates named a Vector3. C# will use SharpDX.Vector3 and TypeScript & JavaScript will use BABYLON.Vector3.

我们的相机将有两个属性:它在3D世界中的位置,以及它所看向的位置-the target(目标)。两者都是由3D坐标组成,名为Vector3。C#将使用SharpDX.Vector3,TypeScript和JavaScript将使用BABYLON.Vector3。

Our Mesh will have a collection of vertices (several vertex or 3D points) that will be used to build our 3D object, its position in the 3D world and its rotation state. To identify it, it will also have a name.

我们的网格将有一组顶点(几个顶点或3D点),这些顶点将用于构建我们的3D对象、它在3D世界中的位置及其旋转状态。为了识别它,它还将有一个名称。

To resume, we need the following code:

要继续,我们需要以下代码:

var SoftEngine;
(function (SoftEngine) {
    var Camera = (function () {
        function Camera() {
            this.Position = BABYLON.Vector3.Zero();
            this.Target = BABYLON.Vector3.Zero();
        }
        return Camera;
    })();
    SoftEngine.Camera = Camera;    
    var Mesh = (function () {
        function Mesh(name, verticesCount) {
            this.name = name;
            this.Vertices = new Array(verticesCount);
            this.Rotation = BABYLON.Vector3.Zero();
            this.Position = BABYLON.Vector3.Zero();
        }
        return Mesh;
    })();
    SoftEngine.Mesh = Mesh;  
})

For instance, if you want to describe a cube using our Mesh object, you need to create 8 vertices associated to the 8 points of the cube. Here are the coordinates on a cube displayed in Blender:

例如,如果要使用Mesh对象描述立方体,则需要创建与立方体的8个点关联的8个顶点。以下是Blender中显示的立方体上的坐标:

With a left-handed world. Remember also that when you’re creating a mesh, the coordinates system is starting at the center of the mesh. So, X=0, Y=0, Z=0 is the center of the cube.

有一个左撇子的世界。还要记住,创建网格时,坐标系从网格的中心开始。因此,X=0,Y=0,Z=0是立方体的中心。

This could be created via this kind of code:

这可以通过以下代码创建:

var mesh = new Mesh("Cube", 8);
mesh.Vertices[0] = new Vector3(-1, 1, 1);
mesh.Vertices[1] = new Vector3(1, 1, 1);
mesh.Vertices[2] = new Vector3(-1, -1, 1);
mesh.Vertices[3] = new Vector3(-1, -1, -1);
mesh.Vertices[4] = new Vector3(-1, 1, -1);
mesh.Vertices[5] = new Vector3(1, 1, -1);
mesh.Vertices[6] = new Vector3(1, -1, 1);
mesh.Vertices[7] = new Vector3(1, -1, -1);

The most important part: the Device object(最重要的部分:设备对象)

Now that we have our basic objects and we know how to build 3D meshes, we need the most important part: the Device object. It’s the core of our 3D engine.

现在我们已经有了基本的对象,并且知道了如何构建3D网格,我们需要最重要的部分:Device对象。它是我们3D引擎的核心。

In it’s rendering function, we will build the view matrix and the projection matrix based on the camera we will have defined before.

Then, we will iterate through each available mesh to build their associated world matrix based on their current rotation and translation values. Finally, once done, the final transformation matrix to apply is:

var transformMatrix = worldMatrix * viewMatrix * projectionMatrix;

This is the concept you absolutely need to understand by reading the previous prerequisites resources. Otherwise, you will probably simply copy/paste the code without understanding anything about the magic underneath. This is not a very big problem for further tutorials but again, it’s better to know what’s you’re coding.

Using this transformation matrix, we’re going to project each vertex of each mesh in the 2D world to obtain X,Y coordinates from their X,Y,Z coordinates. To finally draw on screen, we’re adding a small clip logic to only display visible pixels via a PutPixel method/function.

Here are the various versions of the Device object. I’ve tried to comment the code to help you understanding it as much as possible.

Note: Microsoft Windows is drawing using the BGRA color space (Blue, Green, Red, Alpha) whereas the HTML5 canvas is drawing using the RGBA (Red, Green, Blue, Alpha) color space. That’s why, you will notice some slight differences in the code between C# and HTML5.

    var Device = (function () {
        function Device(canvas) {
            this.workingCanvas = canvas;
            this.workingWidth = canvas.width;
            this.workingHeight = canvas.height;
            this.workingContext = this.workingCanvas.getContext("2d");
        }
        Device.prototype.clear = function () {
            this.workingContext.clearRect(0, 0, this.workingWidth, this.workingHeight);
            this.backbuffer = this.workingContext.getImageData(0, 0, this.workingWidth, this.workingHeight);
        };
        Device.prototype.present = function () {
            this.workingContext.putImageData(this.backbuffer, 0, 0);
        };
        Device.prototype.putPixel = function (x, y, color) {
            this.backbufferdata = this.backbuffer.data;
            var index = ((x >> 0) + (y >> 0) * this.workingWidth) * 4;
            this.backbufferdata[index] = color.r * 255;
            this.backbufferdata[index + 1] = color.g * 255;
            this.backbufferdata[index + 2] = color.b * 255;
            this.backbufferdata[index + 3] = color.a * 255;
        };
        Device.prototype.project = function (coord, transMat) {
            var point = BABYLON.Vector3.TransformCoordinates(coord, transMat);
            var x = point.x * this.workingWidth + this.workingWidth / 2.0 >> 0;
            var y = -point.y * this.workingHeight + this.workingHeight / 2.0 >> 0;
            return (new BABYLON.Vector2(x, y));
        };
        Device.prototype.drawPoint = function (point) {
            if(point.x >= 0 && point.y >= 0 && point.x < this.workingWidth && point.y < this.workingHeight) {
                this.putPixel(point.x, point.y, new BABYLON.Color4(1, 1, 0, 1));
            }
        };
        Device.prototype.render = function (camera, meshes) {
            var viewMatrix = BABYLON.Matrix.LookAtLH(camera.Position, camera.Target, BABYLON.Vector3.Up());
            var projectionMatrix = BABYLON.Matrix.PerspectiveFovLH(0.78, this.workingWidth / this.workingHeight, 0.01, 1.0);
            for(var index = 0; index < meshes.length; index++) {
                var cMesh = meshes[index];
                var worldMatrix = BABYLON.Matrix.RotationYawPitchRoll(cMesh.Rotation.y, cMesh.Rotation.x, cMesh.Rotation.z).multiply(BABYLON.Matrix.Translation(cMesh.Position.x, cMesh.Position.y, cMesh.Position.z));
                var transformMatrix = worldMatrix.multiply(viewMatrix).multiply(projectionMatrix);
                for(var indexVertices = 0; indexVertices < cMesh.Vertices.length; indexVertices++) {
                    var projectedPoint = this.project(cMesh.Vertices[indexVertices], transformMatrix);
                    this.drawPoint(projectedPoint);
                }
            }
        };
        return Device;
    })();
    SoftEngine.Device = Device;  

Putting it all together(把它们放在一起)

We finally need to create a mesh (our cube), create a camera and target our mesh & instantiate our Device object.

Once done, we will launch the animation/rendering loop. In optimal cases, this loop will be called every 16ms (60 FPS). During each tick (call to the handler registered to the rendering loop), we will launch the following logic every time:

1 – Clear the screen and all associated pixels with black ones (Clear() function)

2 – Update the various position & rotation values of our meshes

3 – Render them into the back buffer by doing the required matrix operations (Render() function)

4 – Display them on screen by flushing the back buffer data into the front buffer (Present() function)

var canvas;
var device;
var mesh;
var meshes = [];
var mera;
document.addEventListener("DOMContentLoaded", init, false);
function init() {
    canvas = document.getElementById("frontBuffer");
    mesh = new SoftEngine.Mesh("Cube", 8);
    meshes.push(mesh);
    mera = new SoftEngine.Camera();
    device = new SoftEngine.Device(canvas);
    mesh.Vertices[0] = new BABYLON.Vector3(-1, 1, 1);
    mesh.Vertices[1] = new BABYLON.Vector3(1, 1, 1);
    mesh.Vertices[2] = new BABYLON.Vector3(-1, -1, 1);
    mesh.Vertices[3] = new BABYLON.Vector3(-1, -1, -1);
    mesh.Vertices[4] = new BABYLON.Vector3(-1, 1, -1);
    mesh.Vertices[5] = new BABYLON.Vector3(1, 1, -1);
    mesh.Vertices[6] = new BABYLON.Vector3(1, -1, 1);
    mesh.Vertices[7] = new BABYLON.Vector3(1, -1, -1);
    mera.Position = new BABYLON.Vector3(0, 0, 10);
    mera.Target = new BABYLON.Vector3(0, 0, 0);
    requestAnimationFrame(drawingLoop);
}
function drawingLoop() {
    device.clear();
    mesh.Rotation.x += 0.01;
    mesh.Rotation.y += 0.01;
    device.render(mera, meshes);
    device.present();
    requestAnimationFrame(drawingLoop);
}

If you’ve managed to follow properly this first tutorial, you should obtain something like that:

参考:https://www.davrous.com/2013/06/13/tutorial-series-learning-how-to-write-a-3d-soft-engine-from-scratch-in-c-typescript-or-javascript/

>>知识点1:线段的栅格化:一个线段占几个栅格

>>知识点2:点的栅格化:一个点只占一个栅格。一个点的3D坐标->2D屏幕坐标

>>知识点3:面的栅格化:一个面占几个栅格(一个面由几个三角形组成:一个面=一个三角形,但是大小不固定,即面积不固定)

>>知识点4:backbuffer:缓冲区。渲染生成之后再刷新到canvas。

>>知识点5:屏幕坐标是从(-1,1),所以经过MVP之后还需要进行一次计算。

>>知识点6:requestAnimation和settimeout:前者更合理,可以根据计算机性能而自适应帧率。

>>知识点7:device由几部分组成:各部分执行顺序是?

>>知识点8:renderer一帧的执行顺序:准备mesh(mesh不止一个点,要全渲染到一张图片上,所以不能是一个一个画上去,要全部画上去再清除缓存)

posted on 2023-04-23 10:32  XiaoNiuFeiTian  阅读(55)  评论(0编辑  收藏  举报