如果我想在WebGL中更改对象的大小或位置,我会获取此对象的原始顶点数据{x,y,z}并将其传递给顶点着色器,在顶点着色器中,它与矩阵相乘有关转换的信息,产生一组新的坐标,这些坐标只有我的GPU知道,但对我来说是不可见的.
这是唯一可行的,只要重要的是屏幕上的图片,但我希望能够保存变换后的顶点,而无需关于矩阵乘法的附加信息,简单地作为x,y,z值,就像原始数据.
我的想法是,在JavaScript的最后一步中将转换矩阵与我自己的原始顶点数据相乘,但我只是不能让它工作!
我编写了以下函数,通过添加第四个值{1.0}将4x4矩阵与x,y,z值数组相乘,将vec3数据更改为vec4-data ...
function matrixVectorProduct (mat4, data) {
var result = new Array( );
for (var i = 0; i < data.length / 3; i++) {
var n = i * 3;
var vec4 = [data[n], data[n + 1], data[n + 2], 1.0];
var x = mat4[0] * vec4[0] + mat4[1] * vec4[1] +
mat4[2] * vec4[2] + mat4[3] * vec4[3];
var y = mat4[4] * vec4[0] + mat4[5] * vec4[1] +
mat4[6] * vec4[2] + mat4[7] * vec4[3];
var z = mat4[8] * vec4[0] + mat4[9] * vec4[1] +
mat4[10] * vec4[2] + mat4[11] * vec4[3];
var w = mat4[12] * vec4[0] + mat4[13] * vec4[1] +
mat4[14] * vec4[2] + mat4[15] * vec4[3];
result.push(x, y, z, w);
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
...但除了我不知道如何将vec4值转换回vec3值之外,整个概念一定有问题:
我们假设我们有一个简单三角形的顶点
var vertices = [0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0];
Run Code Online (Sandbox Code Playgroud)
我们创建了一个4x4矩阵
function createMat4 ( ) {
var mat4 = new Float32Array(16);
mat4[0] = 1; mat4[1] = 0; mat4[2] = 0; mat4[3] = 0;
mat4[4] = 0; mat4[5] = 1; mat4[6] = 0; mat4[7] = 0;
mat4[8] = 0; mat4[9] = 0; mat4[10] = 1; mat4[11] = 0;
mat4[12] = 0; mat4[13] = 0; mat4[14] = 0; mat4[15] = 1;
return mat4;
}
Run Code Online (Sandbox Code Playgroud)
翻译它 - 假设[0,0,-5] - 使用此功能
function translateMat4 (mat4, vec3) {
var x = vec3[0],
y = vec3[1],
z = vec3[2];
var a = mat4;
mat4[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
mat4[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
mat4[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
mat4[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
return mat4;
}
Run Code Online (Sandbox Code Playgroud)
最后vertices使用matrixVectorProduct上面提到的-function 将创建和翻译的矩阵与存储的顶点数据相乘,结果应该是
translatedVertices = [0.0, 1.0, -5.0, -1.0, -1.0, -5.0, 1.0, -1.0, -5.0];
Run Code Online (Sandbox Code Playgroud)
但它只是[x,y,z,1],因为-5 x 0.0(z值)等于0,所以它不能以这种方式工作.
我究竟做错了什么?
你们非常接近。我将从一开始就实施它,只是为了以防万一。
让我们有一个带有顶点的简单三角形模型:
var triangleVertices = [ 0.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0 ];
Run Code Online (Sandbox Code Playgroud)
现在我们创建一个 mat4 来表示在此模型上完成的所有转换。单位矩阵不表示任何变换,对角线上全为 0,但为 1。
var identityMatrix = [ 1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0 ];
var modelMatrix = identityMatrix.slice(0); // slice = clone the array
Run Code Online (Sandbox Code Playgroud)
我们准备好进行平移、旋转或缩放。将其应用到模型上的最佳方法是创建另一个 mat4 来表示我们所需的变换,然后将模型矩阵与变换矩阵相乘。这样我们就可以只需要很少的矩阵运算就可以按所需的顺序进行多次变换,而且我们现在根本不必担心顶点。
因此,让我们创建最简单的平移矩阵。从单位矩阵开始,必须仅更改 mat[12, 13, 14],即 x、y、z。
翻译它 - 比方说 [0, 0, -5] - 使用这个函数
var translate = [ 1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0,-5.0, 1.0 ];
Run Code Online (Sandbox Code Playgroud)
转换矩阵已完成,我们要将其与模型矩阵相乘。矩阵相乘就是变换,任何变换。我现在已经完成了矩阵乘法的函数。我希望它应该有效:)来自 wiki 的数学源。
var mat4Multiply = (function () {
function ABProduct(i, j, a, b) {
var k, sum = 0;
for (k = 0; k < 4; k++) {
sum += a[i * 4 + k] * b[j + k * 4];
}
return sum;
}
function mat4Multiply(a, b) {
var i, j, product = [];
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
product[i * 4 + j] = ABProduct(i, j, a, b);
}
}
return product;
}
return mat4Multiply;
})();
// a = model matrix, b = transformation
Run Code Online (Sandbox Code Playgroud)
只是为了演示如何进行乘法,简短的代码:
modelMatrix = mat4Multiply(modelMatrix, translate);
// but now I change my mind and I want to move model again by another 3 in Z
translate = [ 1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 3.0, 1.0 ];
modelMatrix = mat4Multiply(modelMatrix, translate);
// after this content of the modelMatrix is:
// [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -2, 1]
Run Code Online (Sandbox Code Playgroud)
现在我们只需要做顶点的最终变换。正如我所说,翻译是最简单的转换,所以我只会做翻译功能,这是来自 wiki 的相关文章。
笔记
不要被这张图搞糊涂了:
这是 OpenGL 中使用的表示形式,但后来我们决定使用矩阵的转置版本,这对下图来说是简单的操作:

结束语
翻译功能:
function vec3TranslateMat4(vec, mat4) {
vec[0] += mat4[12];
vec[1] += mat4[13];
vec[2] += mat4[14];
return vec;
}
Run Code Online (Sandbox Code Playgroud)
要平移三角形顶点,您只需执行以下操作:
var i, vertex;
for(i=0;i<triangleVertices.length;i+=3) {
vertex = triangleVertices.slice(i, i+3);
vertex = vec3TranslateMat4(vertex, modelMatrix)
triangleVertices[i] = vertex[0];
triangleVertices[i+1] = vertex[1];
triangleVertices[i+2] = vertex[2];
}
Run Code Online (Sandbox Code Playgroud)
一旦我们应用了变换矩阵,我们应该再次将模型矩阵设置为单位矩阵。之后,我们可以在修改后的顶点上进行新的变换。
我们就完成了。我个人使用gl 矩阵库,它已经包含我需要的矩阵和向量的所有操作。
有矩阵和无矩阵的变换
所有变换也可以在没有矩阵的情况下完成。但矩阵允许我们将当前位置保留在一个变量中,而不需要立即变换顶点。顶点变换是漫长且耗时的。
另一个很好的理由是您可以只使用几个核心函数,mat4Multiply 和 vec3TransformMat4(我没有向您展示这个)。看看旋转和缩放是如何集成到矩阵中的(图像的左侧部分,右侧部分是关于相机的)。
