【问题标题】:How to transform vertices without changing the format?如何在不改变格式的情况下变换顶点?
【发布时间】:2015-05-14 17:47:17
【问题描述】:

如果我想在 WebGL 中更改对象的大小或位置,我会获取该对象的原始顶点数据 {x,y,z} 并将其传递给顶点着色器,在此与矩阵相乘,它包含有关转换的信息,从而产生一组新的坐标,该坐标只有我的 GPU 知道,但我不可见。

效果很好,就唯一重要的是屏幕上的图片,但我希望能够在没有关于矩阵乘法的其他信息的情况下保存转换后的顶点,就像 x、y、z 值一样,就像原始数据一样。

我的想法是,在 JavaScript 中将转换矩阵与原始顶点数据相乘的最后一步,但我就是不让它工作!

我编写了以下函数,将 4x4 矩阵与 x,y,z 值数组相乘,通过添加第四个值 {1.0} 将 vec3-data 更改为 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;

}

...但除了我不知道如何将 vec4 值转换回 vec3 值这一事实之外,整个概念肯定有问题:

假设我们有一个简单三角形的顶点

var vertices = [0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0];

我们创建了一个 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;

}

翻译它 - 比如说 [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;

}

最后使用上面提到的matrixVectorProduct函数将创建和转换的矩阵与存储在vertices中的顶点数据相乘,结果应该是

translatedVertices = [0.0, 1.0, -5.0, -1.0, -1.0, -5.0, 1.0, -1.0, -5.0];

但它只是[x,y,z,1],因为-5 x 0.0(z值)等于0,所以不能这样工作。

我做错了什么?

【问题讨论】:

  • "我不知道如何将 vec4 值转换回 vec3" 您需要将向量除以 w,这是在着色器中隐式完成的。跨度>
  • @LJ_1102 Moin und danke für die 信息!正如 Entity Black 所说,我将不得不分别编写单独的函数,用于将平移/旋转/缩放矩阵内容转换为顶点数据......
  • 好吧,也许我不明白你的目标是什么,但你当然不需要分解你的矩阵变换来将它应用到顶点。也许看看vec3.transformMat4的实现(翻译应用在第498行)。除了 perspective divide for gl_Position 之外,着色器中没有任何“神奇”发生。

标签: javascript webgl


【解决方案1】:

你很亲密。我将从一开始就实施它,以防万一。

让我们有一个带顶点的简单三角形模型:

var triangleVertices = [ 0.0,  1.0, 0.0, 
                        -1.0, -1.0, 0.0, 
                         1.0, -1.0, 0.0 ];

现在我们创建一个 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

我们已准备好进行平移、旋转或缩放。将其应用于模型的最佳方法是创建另一个 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 ];

变换矩阵已经完成,我们想将它与我们的模型矩阵相乘。乘法矩阵是变换,任何变换。我现在已经完成了矩阵乘法的函数。我希望它应该可以工作:) Math source from 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

只是为了演示如何做乘法,短代码:

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]

现在我们必须只做顶点的最终变换。正如我所说,翻译是最简单的转换,所以我只会做翻译功能,this is related article from wiki

注意

不要被这张图弄糊涂了:

这是 OpenGL 中使用的表示,但后来我们决定使用矩阵的转置版本,这是对下图的简单操作:

尾注

翻译功能:

function vec3TranslateMat4(vec, mat4) {
    vec[0] += mat4[12];
    vec[1] += mat4[13];
    vec[2] += mat4[14];
    return vec;
}

要平移三角形顶点,您只需:

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];
}

一旦我们应用了变换矩阵,我们应该再次将模型矩阵设置为单位矩阵。之后,我们可以在修改后的顶点上进行新的转换。

我们完成了。我个人使用gl matrix library,它已经包含了我需要的矩阵和向量的所有操作。

有和没有矩阵的变换

所有的转换也可以在没有矩阵的情况下完成。但是矩阵允许我们将当前位置保留在一个变量中,而无需立即转换顶点。顶点变换既长又耗时。

另一个很好的理由是你可以只使用几个核心函数,mat4Multiply 和 vec3TransformMat4(我没有向你展示这个)。看看旋转和缩放是如何集成在矩阵中的(图像的左边部分,右边部分是关于相机的)。

【讨论】:

  • 非常感谢您的回答 Entity Black 并对迟到的反应感到抱歉!你的解释很好,但我还是有点困惑:这是否意味着没有单一的函数可以做矩阵向量数学?我的意思是,当在着色器中进行计算时,我可以创建一个身份,然后使用 gl-matrix-lib 类似的函数来进行平移、旋转和缩放(可能还有透视)而不需要重置要标识,将包含 all 此信息的矩阵传递给着色器,写入uniformMat * posAttrib,它就可以工作了!?所以要自己做,我需要单独的功能吗?如何旋转和缩放?
  • @var 正确! JS 没有任何向量/矩阵数学。所有数学必须首先实现。 gl-matrix 是一个很好的解决方案。 GLSL(着色器)已经有了这个数学,所以在着色器中你可以将向量和矩阵相乘。顶点数据存储在缓冲区中。在大多数情况下,您不需要修改缓冲区,您保留模型矩阵以保持模型世界 pos/rot/sca。缓冲区中的数据应仅在您需要时修改:以程序方式创建一些模型(独特的树、花、房子...)或在运行时更改对象的形状(实时销毁)。
  • @var 如果您想了解有关转换的更多信息,请创建另一个问题。请先阅读webglfundamentals.org
  • 好吧,也许这些函数无论如何都是在 gl-matrix 中实现的。感谢您提供信息和链接!
  • @var 是的,都是在mat4上实现的glmatrix.net/docs/2.2.0/symbols/mat4.html,最后的顶点变换是在vec3上实现的vec3.transformMat4(out, a, m)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多