我一直被数学难倒,无法进行3D编程。在编程流程中使用方法和函数时,我可以很好地理解数学,一切都很清晰和合乎逻辑。但是在数学符号中,我就是看不懂。
我一直在阅读网站并观看机构的视频,他们尝试解释这个问题,但他们都使用数学符号,我只会迷失其中,无法将其翻译成可理解的东西。我可能在这方面有缺陷。
此外,仅仅使用别人的代码并不是我的兴趣所在,我想要理解它的运作机制、逻辑。我很乐意使用别人的代码,但我真的想知道它是如何工作的。
问题
能否用简单易懂的术语而不使用数学符号,只用编程符号/函数/伪代码来解释如何在所有三个轴上实现矩阵变换?
理想情况下,我想要的是材料/理解,以编写一个方法/对象,在其中可以定义类似于glRotate的三个轴的角度,从而旋转我拥有的四边形/三角形收集体。(我正在尝试编写一个3D旋转立方体的程序,而没有访问OpenGL函数来完成这个操作,因为每次在显示列表中更改某些内容时,它都是在一个绘制调用中完成的。)
我做了什么?
我尝试制作一个90度变换函数,以掌握数学技巧,但在制作一个正确的矩阵方面却彻底失败了。您可以在http://jsfiddle.net/bLfg0tj8/5/上看到我的失败尝试。
Vec3 = function(x,y,z) {
this.x = x;
this.y = y;
this.z = z;
}
Matrix = function Matrix() {
this.matrixPoints = new Array();
this.rotationPoint = new Vec3(0,0,0);
this.rotationAngle = 90;
}
Matrix.prototype.addVector = function(vector) {
this.matrixPoints.push(vector);
}
Matrix.prototype.setRotationPoint = function(vector) {
this.rotationPoint = vector;
}
Matrix.prototype.setRotationAngle = function(angle) {
this.rotationAngle = angle;
}
Matrix.prototype.populate = function() {
translateToOrigin = [[1,0,0-this.rotationPoint.x],
[0,1,0-this.rotationPoint.y],
[0,0,0-this.rotationPoint.z]];
rotationMatrix = [[0,-1,0],
[0,1,0],
[0,0,1]];
translateEnd = [[1,0,this.rotationPoint.x],
[0,1,this.rotationPoint.y],
[0,0,this.rotationPoint.z]];
currentColumn = 0;
currentRow = 0;
this.combomatrix = this.mergeMatrices(this.mergeMatrices(translateEnd,rotationMatrix),
translateToOrigin);
}
Matrix.prototype.transform = function() {
newmatrix = new Array();
for(c = 0;c<this.matrixPoints.length;c++) {
newmatrix.push(this.applyToVertex(this.matrixPoints[c]));
}
return newmatrix;
}
Matrix.prototype.applyToVertex = function(vertex) {
ret = new Vec3(vertex.x,vertex.y,vertex.z);
ret.x = ret.x + this.combomatrix[0][0] * vertex.x +
this.combomatrix[0][1] * vertex.y +
this.combomatrix[0][2] * vertex.z;
ret.y = ret.y + this.combomatrix[1][0] * vertex.x +
this.combomatrix[1][1] * vertex.y +
this.combomatrix[1][2] * vertex.z;
ret.z = ret.z + this.combomatrix[2][0] * vertex.x +
this.combomatrix[2][1] * vertex.y +
this.combomatrix[2][2] * vertex.z;
return ret;
}
Matrix.prototype.mergeMatrices = function(lastStep, oneInFront) {
step1 = [[0,0,0],[0,0,0],[0,0,0]];
step1[0][0] = lastStep[0][0] * oneInFront[0][0] +
lastStep[0][1] * oneInFront[1][0] +
lastStep[0][2] * oneInFront[2][0];
step1[0][1] = lastStep[0][0] * oneInFront[0][1] +
lastStep[0][1] * oneInFront[1][1] +
lastStep[0][2] * oneInFront[2][1];
step1[0][2] = lastStep[0][0] * oneInFront[0][2] +
lastStep[0][1] * oneInFront[1][2] +
lastStep[0][2] * oneInFront[2][2];
//============================================================
step1[1][0] = lastStep[1][0] * oneInFront[0][0] +
lastStep[1][1] * oneInFront[1][0] +
lastStep[1][2] * oneInFront[2][0];
step1[1][1] = lastStep[1][0] * oneInFront[0][1] +
lastStep[1][1] * oneInFront[1][1] +
lastStep[1][2] * oneInFront[2][1];
step1[1][2] = lastStep[1][0] * oneInFront[0][2] +
lastStep[1][1] * oneInFront[1][2] +
lastStep[1][2] * oneInFront[2][2];
//============================================================
step1[2][0] = lastStep[2][0] * oneInFront[0][0] +
lastStep[2][1] * oneInFront[1][0] +
lastStep[2][2] * oneInFront[2][0];
step1[2][1] = lastStep[2][0] * oneInFront[0][1] +
lastStep[2][1] * oneInFront[1][1] +
lastStep[2][2] * oneInFront[2][1];
step1[2][2] = lastStep[2][0] * oneInFront[0][2] +
lastStep[2][1] * oneInFront[1][2] +
lastStep[2][2] * oneInFront[2][2];
return step1;
}
Matrix.prototype.getCurrentMatrix = function() {
return this.matrixPoints;
}
myvectors = [new Vec3(50,50,0), new Vec3(20,80,0), new Vec3(80, 80, 0)];
function drawVectors(vectors,color) {
for(c=0;c<vectors.length;c++) {
document.getElementById("whoa").innerHTML += '<div style="color:'+color+';position:absolute;left:'+vectors[c].x+'px; top:'+vectors[c].y+'px;z-index:'+vectors[c].z+';">('+c+').</div>';
}
}
matrix = new Matrix();
for(c=0;c<myvectors.length;c++) {
matrix.addVector(myvectors[c]);
}
matrix.setRotationPoint(new Vec3(50,70,0));
matrix.populate();
somematrix = matrix.transform();
drawVectors(matrix.getCurrentMatrix(),"lime"); // draw current matrix that was hand coded
drawVectors([matrix.rotationPoint],'white'); // draw rotation point
drawVectors(somematrix,"red"); // transformed matrix... somehow two points merge
<div id="whoa" style="position:relative;top:50px;left:150px;background-color:green;color:red;width:400px;height:300px;">
</div>
绿色的文字代表原始三角形,白点是中心点,红点表示失败的变换(我认为是因为它们没有围绕中心点对齐)。我学习的教程教我如何将矩阵组合成一个组合矩阵,但我想我在某个地方搞砸了。
正如我所说的,对于我来说,理解数学符号并进行讲解真的非常非常困难。而且更加不利的是,大多数老师都会跳过某些部分的解释。我花了两个小时才明白,在乘法矩阵时你需要将每一步加在一起,而不是以继续相乘的方式。感谢这样的解释。
一个我正在使用/想要使用的实际示例
例如,我有一个立方体,从位于世界中的Wavefront obj文件加载:
x = 50
y = 100
z = 200
这个立方体使用四边形和一些UV映射来绘制。没有问题。它可以渲染出美丽的效果,并正确显示所有纹理。
这些是每个“面”(由四边形绘制而成)的位置坐标。
// Front face
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
// Back face
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
// Top face
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
// Bottom face
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
// Right face
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
// Left face
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0
这很好地解决了问题。但是如果我想让这个立方体绕X轴旋转90度,绕Z轴旋转45度呢?我不能使用glRotate,因为在将数据传递给细分器对象的时候,我无法通过OpenGL函数对其进行任何矩阵变换,因为它只是接收数据,没有实际上渲染它。
数据的存储方式如下:
WaveFrontObject()
|
|-> Groups(String groupname)
|
|-> Faces()
|
|-> Vertex(float x, float y, float z)[]
|-> Float UVmap[] corresponding to each vertex
|-> drawFace() // Draws the face as a quad or triangle
每个上述给出的坐标都作为波前对象中组“cube”的一个面存储。
当将立方体添加到填充器中时,它会被转换为世界中的正确坐标,并正常呈现。
但是它总是呈现相同的状态。如果我想让它以角度呈现,那么此时我必须制作一个单独的波前对象才能做到这一点。在我看来,这种方法在使用一些数学方法解决时就有些疯狂了。
需要回答的问题:
- 分步解释如何构建翻译矩阵以及试图向我解释其中的数学原理。
- 解释如何将翻译矩阵应用于四边形/三角形面,同时保持它们围绕其位置的中心保持定向
- 提供一些示例/伪代码以说明解释。
编程语言并不是很重要,只要是C家族的语言就可以。
请尽量避免使用数学符号/术语。我不知道什么是alpha beta, thetha,我知道什么是x轴、y轴和z轴。我知道什么是角度,但我不知道数学家为其取的名称是什么。
如果您想使用数学术语,请向我解释它们在3D世界/代码中是什么以及如何计算。
我只是想制作一个类似于以下内容的方法/对象:
Matrix.transformVertices(vertices[], 90deg x, 45 deg y, 0 deg z);