在 WebGL 中转换立方体

Translation of a cube in a WebGL

我想在 WebGL 中执行立方体的平移,但我得到的是立方体的变形而不是平移 这是我的代码:

HTML 文件

<!DOCTYPE html>
<html>

<script id="vertex-shader" type="x-shader/x-vertex">

attribute  vec4 vPosition;
attribute  vec4 vColor;
varying vec4 fColor;

uniform vec3 theta;
uniform vec3 tr;

void main()
{
    // Compute the sines and cosines of theta for each of
    //   the three axes in one computation.
    vec3 angles = radians( theta );
    vec3 c = cos( angles );
    vec3 s = sin( angles );

    // Remeber: thse matrices are column-major
    mat4 rx = mat4( 1.0,  0.0,  0.0, 0.0,
                 0.0,  c.x,  s.x, 0.0,
                 0.0, -s.x,  c.x, 0.0,
                 0.0,  0.0,  0.0, 1.0 );

    mat4 ry = mat4( c.y, 0.0, -s.y, 0.0,
                 0.0, 1.0,  0.0, 0.0,
                 s.y, 0.0,  c.y, 0.0,
                 0.0, 0.0,  0.0, 1.0 );


    mat4 rz = mat4( c.z, s.z, 0.0, 0.0,
               -s.z,  c.z, 0.0, 0.0,
                0.0,  0.0, 1.0, 0.0,
                 0.0,  0.0, 0.0, 1.0 );

    mat4 t= mat4( 1.0, 0.0, 0.0, tr.x,
                  0.0, 1.0, 0.0, tr.y,
                  0.0, 0.0, 1.0, tr.z,
                  0.0, 0.0, 0.0, 1.0 );



    fColor = vColor;
    gl_Position = rz * ry * rx  * t * vPosition;
    gl_Position.z = -gl_Position.z;
}
</script>

<script id="fragment-shader" type="x-shader/x-fragment">

precision mediump float;

varying vec4 fColor;

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

<script type="text/javascript" src="../Common/webgl-utils.js"></script>
<script type="text/javascript" src="../Common/initShaders.js"></script>
<script type="text/javascript" src="../Common/MV.js"></script>
<script type="text/javascript" src="Lab20170314_1.js"></script>

<body>
<canvas id="gl-canvas" width="512"" height="512">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>

<br/>

<button id= "xButton">Rotate X</button>
<button id= "yButton">Rotate Y</button>
<button id= "zButton">Rotate Z</button>
<button id= "stopAnimation"> Start/Stop Animation</button>
<div>
rotation angle 0  <input id="slide" type="range"
min="0" max="10" step="1" value="5" />
10 </div>

<div>
translation on x -1  <input id="slide1" type="range"
min="-1" max="1" step="0.1" value="0" />
1 </div>

<div>
translation on y -1  <input id="slide2" type="range"
min="-1" max="1" step="0.1" value="0" />
1 </div>

<div>
translation on z -1  <input id="slide3" type="range"
min="-1" max="1" step="0.1" value="0" />
1 </div>




</body>
</html>

JavaScript代码

"use strict";

var canvas;
var gl;

var NumVertices  = 36;

var points = [];
var colors = [];

var xAxis = 0;
var yAxis = 1;
var zAxis = 2;

var axis = 0;
var axisTr = 0;
var theta = [ 0, 0, 0 ];
var tr=[ 0, 0, 0 ];

var thetaLoc;
var trLoc;
var stop = false;
var trInput = 0;
var degrees=5;

window.onload = function init()
{
    canvas = document.getElementById( "gl-canvas" );

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

    colorCube();

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

    gl.enable(gl.DEPTH_TEST);

    //
    //  Load shaders and initialize attribute buffers
    //
    var program = initShaders( gl, "vertex-shader", "fragment-shader" );
    gl.useProgram( program );

    var cBuffer = gl.createBuffer();
    gl.bindBuffer( gl.ARRAY_BUFFER, cBuffer );
    gl.bufferData( gl.ARRAY_BUFFER, flatten(colors), gl.STATIC_DRAW );

    var vColor = gl.getAttribLocation( program, "vColor" );
    gl.vertexAttribPointer( vColor, 4, gl.FLOAT, false, 0, 0 );
    gl.enableVertexAttribArray( vColor );

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


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

    thetaLoc = gl.getUniformLocation(program, "theta");
    trLoc = gl.getUniformLocation(program, "tr")

    //event listeners for buttons

    document.getElementById( "xButton" ).onclick = function () {
        axis = xAxis;
    };
    document.getElementById( "yButton" ).onclick = function () {
        axis = yAxis;
    };
    document.getElementById( "zButton" ).onclick = function () {
        axis = zAxis;
    };
    document.getElementById( "stopAnimation" ).onclick= function() {
        stop =! stop;
   };
    document.getElementById( "slide" ).onchange= function(){
        degrees = parseInt(event.target.value,10);
   };

    document.getElementById( "slide1" ).onchange= function(){
        axisTr = xAxis;
        trInput = parseFloat(event.target.value,10);
   };

    document.getElementById( "slide2" ).onchange= function(){
        axisTr = yAxis;
        trInput = parseFloat(event.target.value,10);
   };

    document.getElementById( "slide3" ).onchange= function(){
        axisTr = zAxis;
        trInput = parseFloat(event.target.value,10);
   };


    render();
}

function colorCube()
{
    quad( 1, 0, 3, 2 );
    quad( 2, 3, 7, 6 );
    quad( 3, 0, 4, 7 );
    quad( 6, 5, 1, 2 );
    quad( 4, 5, 6, 7 );
    quad( 5, 4, 0, 1 );
}

function quad(a, b, c, d)
{
    var vertices = [
        vec4( -0.5, -0.5,  0.5, 1.0 ),
        vec4( -0.5,  0.5,  0.5, 1.0 ),
        vec4(  0.5,  0.5,  0.5, 1.0 ),
        vec4(  0.5, -0.5,  0.5, 1.0 ),
        vec4( -0.5, -0.5, -0.5, 1.0 ),
        vec4( -0.5,  0.5, -0.5, 1.0 ),
        vec4(  0.5,  0.5, -0.5, 1.0 ),
        vec4(  0.5, -0.5, -0.5, 1.0 )
    ];

    var vertexColors = [
        [ 0.0, 0.0, 0.0, 1.0 ],  // black
        [ 1.0, 0.0, 0.0, 1.0 ],  // red
        [ 1.0, 1.0, 0.0, 1.0 ],  // yellow
        [ 0.0, 1.0, 0.0, 1.0 ],  // green
        [ 0.0, 0.0, 1.0, 1.0 ],  // blue
        [ 1.0, 0.0, 1.0, 1.0 ],  // magenta
        [ 0.0, 1.0, 1.0, 1.0 ],  // cyan
        [ 1.0, 1.0, 1.0, 1.0 ]   // white
    ];

    // We need to parition the quad into two triangles in order for
    // WebGL to be able to render it.  In this case, we create two
    // triangles from the quad indices

    //vertex color assigned by the index of the vertex

    var indices = [ a, b, c, a, c, d ];

    for ( var i = 0; i < indices.length; ++i ) {
        points.push( vertices[indices[i]] );
        //colors.push( vertexColors[indices[i]] );

        // for solid colored faces use
        colors.push(vertexColors[a]);

    }
}

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

    if(stop)
{
}
    else
{
    theta[axis] += degrees;
}

    tr[axisTr] = trInput;

    gl.uniform3fv(thetaLoc, theta);

    gl.uniform3fv(trLoc, tr);

    gl.drawArrays( gl.TRIANGLES, 0, NumVertices );

    requestAnimFrame( render );
}

我使用平移矩阵根据齐次坐标系进行平移。此外,我有三个滑块来设置翻译参数。我哪里错了?

提前致谢。

如果你有 post 工作代码会更好。 Link 在来自 CDN 的公共库中,其余部分使用片段。

您的代码可以正常工作并修复了吗?

  • 首先我使用 twgl 提供一些东西来编译着色器。
  • 您的 canvas 标签中有额外的 ",这使得文件的所有其余部分都被忽略了
  • 没有理由再为 requestAnimationFrame 使用助手了。所有支持 WebGL 的浏览器都支持 requestAnimationFrame.
  • 几乎没有理由使用任何特殊函数来获取 webgl 上下文。只要做 someCanvas.getContext("webgl")
  • 您没有提供 flatten 函数。看起来您正在使用的 flatten 函数生成了一个 Float32Array,这对于习惯于 JavaScript 并期望它生成一个实际的 JavaScript 数组的人来说会非常混乱。
  • 您没有提供 vec4 函数。不确定为什么你有那个功能,但很容易猜到
  • 没有理由使用 window.onload。只需将您的脚本放在 HTML 的底部,然后直接调用 init
  • 使用 oninput 而不是 onchange 进行实时更新(因为您会在幻灯片滑动时被调用,而不是仅在用户松开滑块时被调用)。虽然我们正在讨论它,但可以说使用 elem.addEventListener('input', listener)elem.oninput 更好。第一个更灵活。它允许多个听众。

终于到了你的实际问题。从编程的角度来看,WebGL 的存储顺序是行优先的,但它的乘法函数将这些行解释为列。

因此,您可以交换乘法顺序,也可以更改矩阵。我建议更改矩阵,否则您将与几乎所有其他 webgl 应用程序不同。

我还建议在着色器中创建矩阵既不灵活也不常见。有时候这是正确的做法,但这可以说不是其中之一,您可能想要 try these webgl tutorials

"use strict";

var canvas;
var gl;

var NumVertices  = 36;

var points = [];
var colors = [];

var xAxis = 0;
var yAxis = 1;
var zAxis = 2;

var axis = 0;
var axisTr = 0;
var theta = [ 0, 0, 0 ];
var tr=[ 0, 0, 0 ];

var thetaLoc;
var trLoc;
var stop = false;
var trInput = 0;
var degrees=5;

function init()
{
    canvas = document.getElementById( "gl-canvas" );

    gl = canvas.getContext("webgl");
    if ( !gl ) { alert( "WebGL isn't available" ); return; }

    colorCube();

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

    gl.enable(gl.DEPTH_TEST);

    //
    //  Load shaders and initialize attribute buffers
    //
    var program = initShaders( gl, "vertex-shader", "fragment-shader" );
    gl.useProgram( program );

    var cBuffer = gl.createBuffer();
    gl.bindBuffer( gl.ARRAY_BUFFER, cBuffer );

    gl.bufferData( gl.ARRAY_BUFFER, flatten(colors), gl.STATIC_DRAW );

    var vColor = gl.getAttribLocation( program, "vColor" );
    gl.vertexAttribPointer( vColor, 4, gl.FLOAT, false, 0, 0 );
    gl.enableVertexAttribArray( vColor );

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

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

    thetaLoc = gl.getUniformLocation(program, "theta");
    trLoc = gl.getUniformLocation(program, "tr")

    //event listeners for buttons

    document.getElementById( "xButton" ).onclick = function () {
        axis = xAxis;
    };
    document.getElementById( "yButton" ).onclick = function () {
        axis = yAxis;
    };
    document.getElementById( "zButton" ).onclick = function () {
        axis = zAxis;
    };
    document.getElementById( "stopAnimation" ).onclick= function() {
        stop =! stop;
   };
    document.getElementById( "slide" ).oninput = function(){
        degrees = parseInt(event.target.value,10);
   };

    document.getElementById( "slide1" ).oninput = function(){
        axisTr = xAxis;
        trInput = parseFloat(event.target.value,10);
   };

    document.getElementById( "slide2" ).oninput = function(){
        axisTr = yAxis;
        trInput = parseFloat(event.target.value,10);
   };

    document.getElementById( "slide3" ).oninput= function(){
        axisTr = zAxis;
        trInput = parseFloat(event.target.value,10);
   };


    render();
}

function colorCube()
{
    quad( 1, 0, 3, 2 );
    quad( 2, 3, 7, 6 );
    quad( 3, 0, 4, 7 );
    quad( 6, 5, 1, 2 );
    quad( 4, 5, 6, 7 );
    quad( 5, 4, 0, 1 );
}

function quad(a, b, c, d)
{
    var vertices = [
        vec4( -0.5, -0.5,  0.5, 1.0 ),
        vec4( -0.5,  0.5,  0.5, 1.0 ),
        vec4(  0.5,  0.5,  0.5, 1.0 ),
        vec4(  0.5, -0.5,  0.5, 1.0 ),
        vec4( -0.5, -0.5, -0.5, 1.0 ),
        vec4( -0.5,  0.5, -0.5, 1.0 ),
        vec4(  0.5,  0.5, -0.5, 1.0 ),
        vec4(  0.5, -0.5, -0.5, 1.0 )
    ];

    var vertexColors = [
        [ 0.0, 0.0, 0.0, 1.0 ],  // black
        [ 1.0, 0.0, 0.0, 1.0 ],  // red
        [ 1.0, 1.0, 0.0, 1.0 ],  // yellow
        [ 0.0, 1.0, 0.0, 1.0 ],  // green
        [ 0.0, 0.0, 1.0, 1.0 ],  // blue
        [ 1.0, 0.0, 1.0, 1.0 ],  // magenta
        [ 0.0, 1.0, 1.0, 1.0 ],  // cyan
        [ 1.0, 1.0, 1.0, 1.0 ]   // white
    ];

    // We need to parition the quad into two triangles in order for
    // WebGL to be able to render it.  In this case, we create two
    // triangles from the quad indices

    //vertex color assigned by the index of the vertex

    var indices = [ a, b, c, a, c, d ];

    for ( var i = 0; i < indices.length; ++i ) {
        points.push( vertices[indices[i]] );
        //colors.push( vertexColors[indices[i]] );

        // for solid colored faces use
        colors.push(vertexColors[a]);

    }
}

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

    if(stop)
{
}
    else
{
    theta[axis] += degrees;
}

    tr[axisTr] = trInput;

    gl.uniform3fv(thetaLoc, theta);

    gl.uniform3fv(trLoc, tr);

    gl.drawArrays( gl.TRIANGLES, 0, NumVertices );

    requestAnimationFrame( render );
}

function initShaders(gl, vsId, fsId) {
  return twgl.createProgramFromScripts(gl, [vsId, fsId]);
}

function vec4(x, y, z, w) {
 return [x, y, z, w];
}

function flatten(arrays) {
  return new Float32Array([].concat.apply([], arrays));
}
init();
<script src="https://twgljs.org/dist/3.x/twgl.min.js"></script>
<script id="vertex-shader" type="x-shader/x-vertex">

attribute  vec4 vPosition;
attribute  vec4 vColor;
varying vec4 fColor;

uniform vec3 theta;
uniform vec3 tr;

void main()
{
    // Compute the sines and cosines of theta for each of
    //   the three axes in one computation.
    vec3 angles = radians( theta );
    vec3 c = cos( angles );
    vec3 s = sin( angles );

    // Remeber: thse matrices are column-major
    mat4 rx = mat4( 1.0,  0.0,  0.0, 0.0,
                    0.0,  c.x, -s.x, 0.0,
                    0.0,  s.x,  c.x, 0.0,
                    0.0,  0.0,  0.0, 1.0 );

    mat4 ry = mat4( c.y, 0.0,  s.y, 0.0,
                    0.0, 1.0,  0.0, 0.0,
                   -s.y, 0.0,  c.y, 0.0,
                    0.0, 0.0,  0.0, 1.0 );


    mat4 rz = mat4( c.z, -s.z, 0.0, 0.0,
                    s.z,  c.z, 0.0, 0.0,
                    0.0,  0.0, 1.0, 0.0,
                    0.0,  0.0, 0.0, 1.0 );

    mat4 t= mat4( 1.0, 0.0, 0.0, 0,
                  0.0, 1.0, 0.0, 0,
                  0.0, 0.0, 1.0, 0,
                  tr.xyz, 1.0 );



    fColor = vColor;
    gl_Position = rz * ry * rx  * t * vPosition;
    gl_Position.z = -gl_Position.z;
    
    // this would too but would be different than most WebGL programs
    //gl_Position = vPosition * t * rx * ry * rz;
    //gl_Position.z = -gl_Position.z;
}
</script>

<script id="fragment-shader" type="x-shader/x-fragment">

precision mediump float;

varying vec4 fColor;

void
main()
{
    gl_FragColor = fColor;
}
</script>
<canvas id="gl-canvas" width="512" height="512">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>

<br/>

<button id= "xButton">Rotate X</button>
<button id= "yButton">Rotate Y</button>
<button id= "zButton">Rotate Z</button>
<button id= "stopAnimation"> Start/Stop Animation</button>
<div>
rotation angle 0  <input id="slide" type="range"
min="0" max="10" step="1" value="5" />
10 </div>

<div>
translation on x -1  <input id="slide1" type="range"
min="-1" max="1" step="0.1" value="0" />
1 </div>

<div>
translation on y -1  <input id="slide2" type="range"
min="-1" max="1" step="0.1" value="0" />
1 </div>

<div>
translation on z -1  <input id="slide3" type="range"
min="-1" max="1" step="0.1" value="0" />
1 </div>