在 WebGL 中每个绘制调用是否可以 运行 编码一次?

Is it possible to run code once per draw call in WebGL?

我正在 NTNU 学习 WebGL 课程。我目前正在探索着色器的作用以及如何使用它们。

我们有一个例子向我们展示了我们计算投影矩阵,然后在顶点着色器中设置它,然后进行绘制调用。我想尝试在着色器中进行矩阵计算。

这意味着我必须将代码放在顶点着色器中 main() 函数之外的其他地方,因为每次绘制调用都会调用该函数多次。

顶点着色器:

uniform vec3 camRotation;
attribute vec3 position;
void main() {

    // I want this code to run only once per draw call
    float rX = camRotation[0];
    float rY = camRotation[1];
    float rZ = camRotation[2];
    mat4 camMatrix = mat4(
        cos(rY) * cos(rZ), cos(rZ) * sin(rX) * sin(rY) - cos(rX) * sin(rZ), sin(rX) * sin(rZ) + cos(rX) * cos(rZ) * sin(rY), 0, //
        cos(rY) * sin(rZ), cos(rX) * cos(rZ) + sin(rX) * sin(rY) * sin(rZ), cos(rX) * sin(rY) * sin(rZ) - cos(rZ) * sin(rX), 0, //
        -sin(rY), cos(rY) * sin(rX), cos(rX) * cos(rY), 0, //
        0, 0, 0, 1
    );
    // End of code in question

    gl_Position = camMatrix * vec4(position, 1);
    gl_PointSize = 5.0;
}

可能吗?我是傻瓜吗?

据我所知,没有办法做到这一点。您应该在 JS 代码中计算 camMatrix 并通过 uniform:

将其传递给着色器
uniform mat4 camMatrix;
attribute vec3 position;
void main() {
    gl_Position = camMatrix * vec4(position, 1);
    gl_PointSize = 5.0;
}

现在你需要在 JS 中计算矩阵:

// assuming that program is your compiled shader program and
// gl is your WebGL context.
const cos = Math.cos;
const sin = Math.sin;
gl.uniformMatrix4fv(gl.getUniformLocation(program, 'camMatrix'), [
    cos(rY) * cos(rZ), cos(rZ) * sin(rX) * sin(rY) - cos(rX) * sin(rZ), sin(rX) * sin(rZ) + cos(rX) * cos(rZ) * sin(rY), 0,
    cos(rY) * sin(rZ), cos(rX) * cos(rZ) + sin(rX) * sin(rY) * sin(rZ), cos(rX) * sin(rY) * sin(rZ) - cos(rZ) * sin(rX), 0,
    -sin(rY), cos(rY) * sin(rX), cos(rX) * cos(rY), 0,
    0, 0, 0, 1
]);

不,这不可能,着色器的整个概念都是可向量化的,因此它们可以 运行 并行。即使你可以,也不会有太大的收获,因为 GPU 的速度优势(除其他外)本质上是基于其并行计算的能力。除此之外,通常你有一个组合视图投影矩阵,它在所有绘制调用(帧的)期间保持静态,并且 model/world 矩阵附加到你正在绘制的每个对象。

投影矩阵的作用正如其名称所暗示的那样,以透视或正交方式投影点(您可以将其视为相机的镜头)。 视图矩阵是 translate/rotate 投影(相机位置和方向)的变换,而 per-object world/model 矩阵包含单个对象的变换(平移、旋转和缩放)。

然后在着色器中使用 per-object model/world 矩阵将顶点位置转换为世界 space,最后使用预乘 ViewProjection 将其转换为相机 space矩阵:

gl_Position = matViewProjection * (matWorld * vPosition)

当您根据用例绘制点时,您可以将世界矩阵简化为一个平移向量。