WebGL基础教程：第二部分

WebGL在3D世界中操纵物体的方式是使用称为变换的数学公式。所以，在我们开始构建3D类之前，我将向你展示不同类型的一些变换，以前它们是如何实现的。

变换

• 移动
• 缩放
• 旋转

移动又称为平移 (Translation)。

```function MH(A, B) {
var Sum = 0;
for (var i = 0; i < A.length; i++) {
Sum += A[i] * B[i];
}
return Sum;
}
function MultiplyMatrix(A, B) {
var A1 = [A[0], A[1], A[2], A[3]];
var A2 = [A[4], A[5], A[6], A[7]];
var A3 = [A[8], A[9], A[10], A[11]];
var A4 = [A[12], A[13], A[14], A[15]];
var B1 = [B[0], B[4], B[8], B[12]];
var B2 = [B[1], B[5], B[9], B[13]];
var B3 = [B[2], B[6], B[10], B[14]];
var B4 = [B[3], B[7], B[11], B[15]];
return [
MH(A1, B1), MH(A1, B2), MH(A1, B3), MH(A1, B4),
MH(A2, B1), MH(A2, B2), MH(A2, B3), MH(A2, B4),
MH(A3, B1), MH(A3, B2), MH(A3, B3), MH(A3, B4),
MH(A4, B1), MH(A4, B2), MH(A4, B3), MH(A4, B4)];
}```

GL对象

```function GLObject(VertexArr, TriangleArr, TextureArr, ImageSrc) {
this.Pos = {
X: 0,
Y: 0,
Z: 0
};
this.Scale = {
X: 1.0,
Y: 1.0,
Z: 1.0
};
this.Rotation = {
X: 0,
Y: 0,
Z: 0
};
this.Vertices = VertexArr;
this.Triangles = TriangleArr;
this.TriangleCount = TriangleArr.length;
this.TextureMap = TextureArr;
this.Image = new Image();
};
this.Image.src = ImageSrc;
}```

```this.GetTransforms = function () {
//Create a Blank Identity Matrix
var TMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
//Scaling
var Temp = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
Temp[0] *= this.Scale.X;
Temp[5] *= this.Scale.Y;
Temp[10] *= this.Scale.Z;
TMatrix = MultiplyMatrix(TMatrix, Temp);
//Rotating X
Temp = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
var X = this.Rotation.X * (Math.PI / 180.0);
Temp[5] = Math.cos(X);
Temp[6] = Math.sin(X);
Temp[9] = -1 * Math.sin(X);
Temp[10] = Math.cos(X);
TMatrix = MultiplyMatrix(TMatrix, Temp);
//Rotating Y
Temp = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
var Y = this.Rotation.Y * (Math.PI / 180.0);
Temp[0] = Math.cos(Y);
Temp[2] = -1 * Math.sin(Y);
Temp[8] = Math.sin(Y);
Temp[10] = Math.cos(Y);
TMatrix = MultiplyMatrix(TMatrix, Temp);
//Rotating Z
Temp = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
var Z = this.Rotation.Z * (Math.PI / 180.0);
Temp[0] = Math.cos(Z);
Temp[1] = Math.sin(Z);
Temp[4] = -1 * Math.sin(Z);
Temp[5] = Math.cos(Z);
TMatrix = MultiplyMatrix(TMatrix, Temp);
//Moving
Temp = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
Temp[12] = this.Pos.X;
Temp[13] = this.Pos.Y;
Temp[14] = this.Pos.Z * -1;
return MultiplyMatrix(TMatrix, Temp);
}

OBJ导入器

`.obj`文件中，每一行的头两个字母告诉我们该行中包含了什么类型的数据。 "`v`"表示一个"顶点坐标"行，"`vt`"表示一个"纹理坐标"行，而"`f`"是一个映射行。基于这些信息，我编写了下面的函数：

```function LoadModel(ModelName, CB) {
var Ajax = new XMLHttpRequest();
if (Ajax.readyState == 4 && Ajax.status == 200) {
//Parse Model Data
var Script = Ajax.responseText.split("\n");
var Vertices = [];
var VerticeMap = [];
var Triangles = [];
var Textures = [];
var TextureMap = [];
var Normals = [];
var NormalMap = [];
var Counter = 0;```

```for (var I in Script) {
var Line = Script[I];
//If Vertice Line
if (Line.substring(0, 2) == "v ") {
var Row = Line.substring(2).split(" ");
Vertices.push({
X: parseFloat(Row[0]),
Y: parseFloat(Row[1]),
Z: parseFloat(Row[2])
});
}
//Texture Line
else if (Line.substring(0, 2) == "vt") {
var Row = Line.substring(3).split(" ");
Textures.push({
X: parseFloat(Row[0]),
Y: parseFloat(Row[1])
});
}
//Normals Line
else if (Line.substring(0, 2) == "vn") {
var Row = Line.substring(3).split(" ");
Normals.push({
X: parseFloat(Row[0]),
Y: parseFloat(Row[1]),
Z: parseFloat(Row[2])
});
```

```//Mapping Line
else if (Line.substring(0, 2) == "f ") {
var Row = Line.substring(2).split(" ");
for (var T in Row) {
//Remove Blank Entries
if (Row[T] != "") {
//If this is a multi-value entry
if (Row[T].indexOf("/") != -1) {
//Split the different values
var TC = Row[T].split("/");
//Increment The Triangles Array
Triangles.push(Counter);
Counter++;
//Insert the Vertices
var index = parseInt(TC[0]) - 1;
VerticeMap.push(Vertices[index].X);
VerticeMap.push(Vertices[index].Y);
VerticeMap.push(Vertices[index].Z);
//Insert the Textures
index = parseInt(TC[1]) - 1;
TextureMap.push(Textures[index].X);
TextureMap.push(Textures[index].Y);
//If This Entry Has Normals Data
if (TC.length > 2) {
//Insert Normals
index = parseInt(TC[2]) - 1;
NormalMap.push(Normals[index].X);
NormalMap.push(Normals[index].Y);
NormalMap.push(Normals[index].Z);
}
}
//For rows with just vertices
else {
Triangles.push(Counter); //Increment The Triangles Array
Counter++;
var index = parseInt(Row[T]) - 1;
VerticeMap.push(Vertices[index].X);
VerticeMap.push(Vertices[index].Y);
VerticeMap.push(Vertices[index].Z);
}
}
}
}
```

``` }
//Return The Arrays
CB(VerticeMap, Triangles, TextureMap, NormalMap);
}
}
Ajax.open("GET", ModelName + ".obj", true);
Ajax.send();
}```

```this.Draw = function (Model) {
this.PrepareModel(Model);
}
this.GL.bindBuffer(this.GL.ARRAY_BUFFER, Model.Vertices);
this.GL.vertexAttribPointer(this.VertexPosition, 3, this.GL.FLOAT, false, 0, 0);
this.GL.bindBuffer(this.GL.ARRAY_BUFFER, Model.TextureMap);
this.GL.vertexAttribPointer(this.VertexTexture, 2, this.GL.FLOAT, false, 0, 0);
this.GL.bindBuffer(this.GL.ELEMENT_ARRAY_BUFFER, Model.Triangles);
//Generate The Perspective Matrix
var PerspectiveMatrix = MakePerspective(45, this.AspectRatio, 1, 1000.0);
var TransformMatrix = Model.GetTransforms();
//Set slot 0 as the active Texture
this.GL.activeTexture(this.GL.TEXTURE0);
//Load in the Texture To Memory
this.GL.bindTexture(this.GL.TEXTURE_2D, Model.Image);
//Update The Texture Sampler in the fragment shader to use slot 0
//Set The Perspective and Transformation Matrices
this.GL.uniformMatrix4fv(pmatrix, false, new Float32Array(PerspectiveMatrix));
this.GL.uniformMatrix4fv(tmatrix, false, new Float32Array(TransformMatrix));
//Draw The Triangles
this.GL.drawElements(this.GL.TRIANGLES, Model.TriangleCount, this.GL.UNSIGNED_SHORT, 0);
}
};```

`PrepareModel`函数只不过是将纹理和数据数组转变为与WebGL兼容的变量。下面就是这个函数；将它加到绘制函数之前。

```this.PrepareModel = function (Model) {
//Convert Arrays to buffers
var Buffer = this.GL.createBuffer();
this.GL.bindBuffer(this.GL.ARRAY_BUFFER, Buffer);
this.GL.bufferData(this.GL.ARRAY_BUFFER, new Float32Array(Model.Vertices), this.GL.STATIC_DRAW);
Model.Vertices = Buffer;
Buffer = this.GL.createBuffer();
this.GL.bindBuffer(this.GL.ELEMENT_ARRAY_BUFFER, Buffer);
this.GL.bufferData(this.GL.ELEMENT_ARRAY_BUFFER, new Uint16Array(Model.Triangles), this.GL.STATIC_DRAW);
Model.Triangles = Buffer;
Buffer = this.GL.createBuffer();
this.GL.bindBuffer(this.GL.ARRAY_BUFFER, Buffer);
this.GL.bufferData(this.GL.ARRAY_BUFFER, new Float32Array(Model.TextureMap), this.GL.STATIC_DRAW);
Model.TextureMap = Buffer;
};```

HTML页面

```var GL;
var Building;

LoadModel("House", function (VerticeMap, Triangles, TextureMap) {
Building = new GLObject(VerticeMap, Triangles, TextureMap, "House.png");

Building.Pos.Z = 650;

//My Model Was a bit too big
Building.Scale.X = 0.5;
Building.Scale.Y = 0.5;
Building.Scale.Z = 0.5;

//And Backwards
Building.Rotation.Y = 180;

setInterval(Update, 33);
});
}

function Update() {
Building.Rotation.Y += 0.2
GL.Draw(Building);
}```

键盘控制

`document.onkeydown = handleKeyDown;`

```function handleKeyDown(event) {
//You can uncomment the next line to find out each key's code
if (event.keyCode == 37) {
//Left Arrow Key
Building.Pos.X -= 4;
} else if (event.keyCode == 38) {
//Up Arrow Key
Building.Pos.Y += 4;
} else if (event.keyCode == 39) {
//Right Arrow Key
Building.Pos.X += 4;
} else if (event.keyCode == 40) {
//Down Arrow Key
Building.Pos.Y -= 4;
}
}```

新年大礼包

0 条评论

• WebGL基础教程：第三部分

欢迎回到第三部分，也是我们的迷你WebGL教程系列的最后一部分。在此课程中，我们会会介绍光照和添加2D对象到场景中。新的内容很多，我们还是直接开始吧。

• JavaScript 数据结构：栈和队列

上周小编已经介绍了什么是数据结构，没看过的同学，可以点击《JavaScript 数据结构：什么是数据结构》，今天小编会和大家一起学习栈和队列。

• 【数据结构基础】队列简介（使用ES6）

在上一篇系列文章《【数据结构基础】栈简介（使用ES6）》笔者介绍了什么是数据结构、什么是栈及相关代码实现，本篇文章笔者给大家介绍下什么是队列以及相关的代码实现。

• WebGL基础教程：第三部分

欢迎回到第三部分，也是我们的迷你WebGL教程系列的最后一部分。在此课程中，我们会会介绍光照和添加2D对象到场景中。新的内容很多，我们还是直接开始吧。

• RocketMQ 源码分析 —— Message 存储

本文接《RocketMQ 源码分析 —— Message 发送与接收》。主要解析 CommitLog 存储消息部分。

• 使用electron制作满屏心特效

这样就会让窗口全屏 但是有一个问题 就是这样做界面不会正确响应 我们可以使用进程通信去解决