在 x 轴上平移 3D 立方体

Translate 3D cube on x-axis

我正在尝试将两个 3D 立方体彼此分开。我试过使用裁剪空间和转换矩阵,但没有任何效果。我正在寻找的解决方案是最好在 x 轴上并排立方体。

这是我的代码:

var gl,program,canvas;

var vBuffer, vPosition;
var idxBuffer;

var vertices = [
    -0.5, 0.5, 1,
     0.5, 0.5, 1,
     0.5, -0.5, 1,
    -0.5, -0.5, 1,
    -0.5, 0.5, 0,
     0.5, 0.5, 0,
     0.5, -0.5, 0,
    -0.5, -0.5, 0
];

var dVecIdx = new Uint16Array([
  0, 1, 1, 2,
        2, 3, 3, 0,
  4, 5, 5, 6,
        6, 7, 7, 4,
  0, 4, 1, 5,
        2, 6, 3, 7
]);

var projection = [
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 1,
    0, 0, 0, 1
];

var a = Math.sqrt(0.5);
var rotation = [
   a, 0, a, 0,
   0, 1, 0, 0,
  -a, 0, a, 0,
   0, 0, 0, 1
];

window.onload = function init() {

    canvas = document.getElementById("gl-canvas");

    gl = WebGLUtils.setupWebGL(canvas);
    if (!gl) { alert("WebGL isn't available"); }

    gl.viewport(0, 0, canvas.width, canvas.height);

    gl.clearColor(1.0, 1.0, 1.0, 1.0);

    gl.enable(gl.DEPTH_TEST);

    program = initShaders(gl, "vertex-shader", "fragment-shader");

    vBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW);

    vPosition = gl.getAttribLocation(program, "vPosition");
    gl.enableVertexAttribArray(vPosition);
    gl.vertexAttribPointer(vPosition, 3, gl.FLOAT, false, 0, 0);

    idxBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, idxBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, dVecIdx, gl.STATIC_DRAW);

    render();
}

function render() {
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    gl.useProgram(program);
    gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
    projLoc = gl.getUniformLocation(program, "projectionMatrix");
    loc = gl.getUniformLocation(program, "rotate");
    gl.uniformMatrix4fv(projLoc, false, projection);
    gl.uniformMatrix4fv(loc, false, projection);
    gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);
    
    gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
    projLoc = gl.getUniformLocation(program, "projectionMatrix");
    loc = gl.getUniformLocation(program, "rotate");
    gl.uniformMatrix4fv(projLoc, false, projection);
    gl.uniformMatrix4fv(loc, false, rotation);
    gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);

    requestAnimFrame(render);
}
<html>
<head>
    <script src="https://www.cs.unm.edu/~angel/WebGL/7E/Common/initShaders.js"></script>
    <script src="https://www.cs.unm.edu/~angel/WebGL/7E/Common/MV.js"></script>
    <script src="https://www.cs.unm.edu/~angel/WebGL/7E/Common/webgl-utils.js"></script>

    <script id="vertex-shader" type="x-shader/x-vertex">
        attribute vec3 vPosition;
        attribute vec4 vColor;
        varying vec4 fColor;
        uniform mat4 projectionMatrix;
        uniform mat4 rotate;

        void main() {
            gl_Position = projectionMatrix * rotate * vec4(vPosition, 1);
            fColor = vColor;
        }
    </script>

    <script id="fragment-shader" type="x-shader/x-fragment">
        precision mediump float;
        varying vec4 fColor;

        void main() {
            gl_FragColor = fColor;
        }
    </script>

</head>
<body>
    <div style="border: 1px dotted black;">
        <div style="text-align:center">
            <canvas id="gl-canvas" width="500" height="500"></canvas>
        </div>
    </div>
</body>
</html>

感谢任何帮助!

如果你试图让你的设置变得更高级,我建议你要么使用 WebGL 库,比如 Three.js 来抽象一些数学,要么真的花时间去 google并了解对象和相机变换矩阵。

答案:

话虽如此,简单的答案是为平移添加另一个矩阵并将其插入着色器中的投影和旋转矩阵之间:

attribute vec3 vPosition;
attribute vec4 vColor;
varying vec4 fColor;
uniform mat4 projectionMatrix;
uniform mat4 rotate;
uniform mat4 translate;

void main() {
    gl_Position = projectionMatrix * translate * rotate * vec4(vPosition, 1);
    fColor = vColor;
}

翻译矩阵如下所示:

var translation = [
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    x, y, z, 1
];

其中xyz分别是沿X轴、Y轴和Z轴的平移距离。 然后将其以与旋转矩阵相同的方式添加到渲染方法中:

transLoc = gl.getUniformLocation(program, "translate");
gl.uniformMatrix4fv(transLoc, false, translation);

优化:

现在也说了那个,还有一些optimizations/corrections你可以做:

1) 因为 WebGL 保持它的 "state" 直到改变(保持东西 bound/set/enabled/etc),你可以在你的代码中删除很多重复的代码render()方法:

function render() {
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    // set state
    gl.useProgram(program);
    gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
    projLoc = gl.getUniformLocation(program, "projectionMatrix");
    loc = gl.getUniformLocation(program, "rotate");
    gl.uniformMatrix4fv(projLoc, false, projection);

    // draw shape 1
    gl.uniformMatrix4fv(loc, false, projection);
    gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);

    // draw shape 2
    gl.uniformMatrix4fv(loc, false, rotation);
    gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);

    requestAnimFrame(render);
}

2) 如果你不想在渲染过程中使用特定的矩阵,你应该将它设置为单位矩阵,它不会改变其他 matrices/vectors 时相乘:

var identity = [
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1
];

这是您应该在第一个形状上使用的旋转矩阵,而不是透视矩阵:

// draw shape 1
gl.uniformMatrix4fv(loc, false, identity);
gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);

3) 您可以将progLoc、rotLoc 和transLoc 声明为全局变量,并在程序初始化后立即设置它们的值。这些不会因单个程序而改变,也不需要在渲染循环中重置。

program = initShaders(gl, "vertex-shader", "fragment-shader");
projLoc = gl.getUniformLocation(program, "projectionMatrix");
rotLoc = gl.getUniformLocation(program, "rotate");
transLoc = gl.getUniformLocation(program, "translate");

制作最终代码:

var gl,program,canvas;

var vBuffer, vPosition;
var idxBuffer;
var projLoc, rotLoc, transLoc;


var vertices = [
    -0.5, 0.5, 1,
     0.5, 0.5, 1,
     0.5, -0.5, 1,
    -0.5, -0.5, 1,
    -0.5, 0.5, 0,
     0.5, 0.5, 0,
     0.5, -0.5, 0,
    -0.5, -0.5, 0
];

var dVecIdx = new Uint16Array([
  0, 1, 1, 2,
        2, 3, 3, 0,
  4, 5, 5, 6,
        6, 7, 7, 4,
  0, 4, 1, 5,
        2, 6, 3, 7
]);

var identity = [
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1
];

var projection = [
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 1,
    0, 0, 0, 1
];

var a = Math.sqrt(0.5);
var rotation = [
   a, 0, a, 0,
   0, 1, 0, 0,
  -a, 0, a, 0,
   0, 0, 0, 1
];

// actual translations are set in the render() function
var translation = [
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1
];


window.onload = function init() {

    canvas = document.getElementById("gl-canvas");

    gl = WebGLUtils.setupWebGL(canvas);
    if (!gl) { alert("WebGL isn't available"); }

    gl.viewport(0, 0, canvas.width, canvas.height);

    gl.clearColor(1.0, 1.0, 1.0, 1.0);

    gl.enable(gl.DEPTH_TEST);

    program = initShaders(gl, "vertex-shader", "fragment-shader");
    projLoc = gl.getUniformLocation(program, "projectionMatrix");
    rotLoc = gl.getUniformLocation(program, "rotate");
    transLoc = gl.getUniformLocation(program, "translate");

    vBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW);

    vPosition = gl.getAttribLocation(program, "vPosition");
    gl.enableVertexAttribArray(vPosition);
    gl.vertexAttribPointer(vPosition, 3, gl.FLOAT, false, 0, 0);

    idxBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, idxBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, dVecIdx, gl.STATIC_DRAW);

    render();
}

function render() {
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    // set non-changing states
    gl.useProgram(program);
    gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
    gl.uniformMatrix4fv(projLoc, false, projection);
    
    // draw shape 1
    translation[12] = 1; // x-axis translation (y and z are 0)
    gl.uniformMatrix4fv(transLoc, false, translation);
    gl.uniformMatrix4fv(rotLoc, false, identity);
    gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);

    // draw shape 2
    translation[12] = -1; // set x-axis translation
    gl.uniformMatrix4fv(transLoc, false, translation);
    gl.uniformMatrix4fv(rotLoc, false, rotation);
    gl.drawElements(gl.LINES, dVecIdx.length, gl.UNSIGNED_SHORT, 0);

    requestAnimFrame(render);
}
<html>
  <head>
    <script src="https://www.cs.unm.edu/~angel/WebGL/7E/Common/initShaders.js"></script>
    <script src="https://www.cs.unm.edu/~angel/WebGL/7E/Common/MV.js"></script>
    <script src="https://www.cs.unm.edu/~angel/WebGL/7E/Common/webgl-utils.js"></script>

    <script id="vertex-shader" type="x-shader/x-vertex">
        attribute vec3 vPosition;
        attribute vec4 vColor;
        varying vec4 fColor;
        uniform mat4 projectionMatrix;
        uniform mat4 rotate;
        uniform mat4 translate;

        void main() {
            gl_Position = projectionMatrix * translate * rotate * vec4(vPosition, 1);
            fColor = vColor;
        }
    </script>

    <script id="fragment-shader" type="x-shader/x-fragment">
        precision mediump float;
        varying vec4 fColor;

        void main() {
            gl_FragColor = fColor;
        }
    </script>
  </head>

  <body>
    <div style="border: 1px dotted black;">
      <div style="text-align:center">
        <canvas id="gl-canvas" width="500" height="500"></canvas>
      </div>
    </div>
  </body>
</html>

4) 如果你想使用你的 MV.js 脚本你也可以将你的矩阵声明为 mat4() 对象并使用 mult() 来在将数据传输到 GPU 之前将 CPU 上的矩阵相乘(每个形状乘法一次而不是每个顶点一次)。您还可以使用它来创建更通用和更准确的相机矩阵:

var persp = perspective(30.0, 1, 0.1, 100); // fovy, aspect, near, far
var view = lookAt([0, 0, 5], [0, 0, 0], [0, 1, 0]); // eye, look, up
var projection2D = mult(persp, view);

var projection = []; // convert to 1D array
for(var i = 0; i < projection2D.length; i++) {
    projection = projection.concat(projection2D[i]);
}

希望对您有所帮助!干杯!