javascript - 在 x 轴上平移 3D 立方体

标签 javascript matrix 3d webgl

我正在尝试将两个 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>

感谢任何帮助!

最佳答案

如果您尝试让您的设置变得更高级,我建议您要么使用诸如 Three.js 之类的 WebGL 库来抽象出一些数学知识,要么花时间去谷歌搜索并理解对象和相机变换矩阵。

答案:

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

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);

优化:

现在也说了,您还可以进行一些优化/更正:

1) 由于 WebGL 会保持其“状态”直到发生更改(保持绑定(bind)/设置/启用/等),因此您可以在 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) 如果您不想在渲染过程中使用特定矩阵,则应将其设置为单位矩阵,该矩阵在相乘时不会更改其他矩阵/向量:

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

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

关于javascript - 在 x 轴上平移 3D 立方体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33188895/

相关文章:

json - 将 R 中的矩阵/数据帧转换为具有一些约束的 JSON 对象

python - 使用 Numpy 高效计算欧氏距离矩阵

3d - 使用开源 3D 引擎从 Openstreetmap 数据渲染 map ?

graphics - HLSL 点光源着色器导致图 block 单独着色

javascript - JQuery - SlideToggle 全部但不折叠其他类别(常见问题解答/问答部分)

javascript - iframe 附加到制作副本

javascript - 切换按钮激活导致功能触发

opencv - 不能使用 copyTo 将矩形矩阵复制到 3D 矩阵

cocoa - Mac 加载和操作 3D 对象的库

javascript - HTML 文件中包含的脚本无法在 Google Apps 脚本 HTMLService 中运行