立方体表面的 webgl 旋转纹理不会绕自身旋转

webgl rotate texture on the face of cube does not rotate around itself

我需要 webgl 方面的帮助。

我画了一个简单的立方体并在立方体的每个面上应用纹理,我正在旋转立方体每个面上的纹理,但问题是:

纹理不围绕自身旋转它围绕立方体的左下角旋转:

这是我关于旋转的代码:

在顶点着色器中:

    vTextureCoord = ((uTMatrix) * vec4(aTextureCoord, 0.0 , 1.0)).xy;

在脚本中:

    mat4.identity(TMatrix);

    mat4.rotate(TMatrix,xRot3, [0.0, 0.0, 1.0]);

this article and maybe this one

旋转总是在 0,0 左右,所以如果你想围绕其他点旋转,你需要先将东西移动到 0,0,然后在你的情况下你需要将它移回来

// moves texcoords back since they're we moved from where they need to be
mat4.translate(TMatrix, [.5, .5, 0]);

// rotates around 0,0
mat4.rotate(TMatrix,degToRad(xRot3), [0.0, 0.0, 1.0]);

// moves texcoords so they're centered around 0,0
mat4.translate(TMatrix, [-.5, -.5, 0]);  

    var gl;

    function initGL(canvas) {
        try {
            gl = canvas.getContext("webgl");
            gl.viewportWidth = canvas.width;
            gl.viewportHeight = canvas.height;
        } catch (e) {
        }
        if (!gl) {
            alert("Could not initialise WebGL, sorry");
        }
    }


    function getShader(gl, id) {
        var shaderScript = document.getElementById(id);
        if (!shaderScript) {
            return null;
        }

        var str = "";
        var k = shaderScript.firstChild;
        while (k) {
            if (k.nodeType == 3) {
                str += k.textContent;
            }
            k = k.nextSibling;
        }

        var shader;
        if (shaderScript.type == "x-shader/x-fragment") {
            shader = gl.createShader(gl.FRAGMENT_SHADER);
        } else if (shaderScript.type == "x-shader/x-vertex") {
            shader = gl.createShader(gl.VERTEX_SHADER);
        } else {
            return null;
        }

        gl.shaderSource(shader, str);
        gl.compileShader(shader);

        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            alert(gl.getShaderInfoLog(shader));
            return null;
        }

        return shader;
    }


    var shaderProgram;

    function initShaders() {
        var fragmentShader = getShader(gl, "shader-fs");
        var vertexShader = getShader(gl, "shader-vs");

        shaderProgram = gl.createProgram();
        gl.attachShader(shaderProgram, vertexShader);
        gl.attachShader(shaderProgram, fragmentShader);
        gl.linkProgram(shaderProgram);

        if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
            alert("Could not initialise shaders");
        }

        gl.useProgram(shaderProgram);

        shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
        gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

        shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
        gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);

        shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
        shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
        shaderProgram.TMatrixUniform = gl.getUniformLocation(shaderProgram, "uTMatrix");
        shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
    }

    var mvMatrix = mat4.create();
    var mvMatrixStack = [];
    var pMatrix = mat4.create();
    var TMatrix = mat4.create();


    function mvPushMatrix() {
        var copy = mat4.create();
        mat4.set(mvMatrix, copy);
        mvMatrixStack.push(copy);
    }

    function mvPopMatrix() {
        if (mvMatrixStack.length == 0) {
            throw "Invalid popMatrix!";
        }
        mvMatrix = mvMatrixStack.pop();
    }


    function setMatrixUniforms() {
        gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
        gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
        gl.uniformMatrix4fv(shaderProgram.TMatrixUniform, false, TMatrix);
    }


    function degToRad(degrees) {
        return degrees * Math.PI / 180;
    }

    var xRot1 = 0;
    var xSpeed1 = 0;

    var xRot2 = 0;
    var xSpeed2 = 0;

    var xRot3 = 0;
    var xSpeed3 = 0;

    var z = -5.0;

    var filter = 0;

    var depth = 0;

    var currentlyPressedKeys = {};

    var rot_flag = 0;

    var text_rot_flag = 0;


///////////////////////////////////////// keyboard ///////////////////////////////////////////

    function handleKeyDown(event) {
        currentlyPressedKeys[event.keyCode] = true;

        if (String.fromCharCode(event.keyCode) == "F") {
            filter += 1;
            if (filter == 3) {
                filter = 0;
            }
        }
        
        if(String.fromCharCode(event.keyCode) == "R"){
            if (rot_flag == 0){
                rot_flag = 1;
                xSpeed1 = 20;
                xSpeed2 = 30;
            }
            else{
                rot_flag = 0;
                xSpeed1 = 0;
                xSpeed2 = 0;
            }
        }

        if(String.fromCharCode(event.keyCode) == "O"){
            depth = depth - 1;
        }

        if(String.fromCharCode(event.keyCode) == "I"){
            depth = depth + 1;
        }

        if(String.fromCharCode(event.keyCode) == "T"){
            if(text_rot_flag == 0){
                text_rot_flag = 1;
                xSpeed3 = 15;

            }
            else{
                text_rot_flag = 0;
                xSpeed3 = 0;
            }
        }

    }

    function handleKeyUp(event) {
        currentlyPressedKeys[event.keyCode] = false;
    }

///////////////////////////////////////// Texture ///////////////////////////////////////////

    function handleLoadedTexture(textures) {

        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);

        gl.bindTexture(gl.TEXTURE_2D, textures[0]);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textures[0].image);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);

        gl.bindTexture(gl.TEXTURE_2D, textures[1]);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textures[1].image);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);

        gl.generateMipmap(gl.TEXTURE_2D);
        gl.bindTexture(gl.TEXTURE_2D, null);

    }

    var crateTextures = Array();

    var crateTextures1 = Array();

    function initTexture() {

        var crateImage = document.createElement("canvas");
        crateImage.width = 64;
        crateImage.height = 64;
        var ctx = crateImage.getContext("2d");
        ctx.translate(32, 32);
        ctx.rotate(Math.PI * .25);
        ctx.fillRect(-24, -24, 48, 48);

        for (var i=0; i < 3; i++) {
            var texture = gl.createTexture();
            texture.image = crateImage;
            crateTextures.push(texture);
        }

        handleLoadedTexture(crateTextures)


        var crateImage1 = document.createElement("canvas");
        crateImage1.width = 64;
        crateImage1.height = 64;
        var ctx = crateImage1.getContext("2d");
        ctx.fillStyle="red";
        ctx.translate(32, 32);
        ctx.rotate(Math.PI * .25);
        ctx.fillRect(-24, -24, 48, 48);

        for (var i=0; i < 3; i++) {
            var texture = gl.createTexture();
            texture.image = crateImage1;
            crateTextures1.push(texture);
        }

        handleLoadedTexture(crateTextures1)
    }   

    var cubeVertexPositionBuffer;
    var cubeVertexTextureCoordBuffer;
    var cubeVertexIndexBuffer;

    function initBuffers() {

        cubeVertexPositionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
        vertices = [
            // Front face
            -2.00, -2.00,  2.00,
             2.00, -2.00,  2.00,
             2.00,  2.00,  2.00,
            -2.00,  2.00,  2.00,

            // Back face
            -2.00, -2.00, -2.00,
            -2.00,  2.00, -2.00,
             2.00,  2.00, -2.00,
             2.00, -2.00, -2.00,

            // Top face
            -2.00,  2.00, -2.00,
            -2.00,  2.00,  2.00,
             2.00,  2.00,  2.00,
             2.00,  2.00, -2.00,

            // Bottom face
            -2.00, -2.00, -2.00,
             2.00, -2.00, -2.00,
             2.00, -2.00,  2.00,
            -2.00, -2.00,  2.00,

            // Right face
             2.00, -2.00, -2.00,
             2.00,  2.00, -2.00,
             2.00,  2.00,  2.00,
             2.00, -2.00,  2.00,

            // Left face
            -2.00, -2.00, -2.00,
            -2.00, -2.00,  2.00,
            -2.00,  2.00,  2.00,
            -2.00,  2.00, -2.00,
        ];

        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        cubeVertexPositionBuffer.itemSize = 3;
        cubeVertexPositionBuffer.numItems = 24;

//////////////////////////////////////////////////// cube1 //////////////////////////////////////////////////////

        cubeVertexTextureCoordBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);

            var textureCoords = [
                // Front face
                1.000, 1.000,
                0.0, 1.000,
                0.0, 0.0,
                1.000, 0.0,

                // Back face
                1.000, 0.0,
                1.000, 1.000,
                0.0, 1.000,
                0.0, 0.0,

                // Top face
                0.0, 1.000,
                0.0, 0.0,
                1.000, 0.0,
                1.000, 1.000,

                // Bottom face
                0.0, 0.0,
                1.000, 0.0,
                1.000, 1.000,
                0.0, 1.000,

                // Right face
                1.000, 0.0,
                1.000, 1.000,
                0.0, 1.000,
                0.0, 0.0,

                // Left face
                0.0, 0.0,
                1.000, 0.0,
                1.000, 1.000,
                0.0, 1.000,
            ];

        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
        cubeVertexTextureCoordBuffer.itemSize = 2;
        cubeVertexTextureCoordBuffer.numItems = 24;

/////////////////////////////////////////////////////// cube2 /////////////////////////////////////////////////////

        cubeVertexTextureCoordBuffer1 = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer1);

            var textureCoords1 = [
                // Front face
                1.000, 1.000,
                0.0, 1.000,
                0.0, 0.0,
                1.000, 0.0,

                // Back face
                1.000, 0.0,
                1.000, 1.000,
                0.0, 1.000,
                0.0, 0.0,

                // Top face
                0.0, 1.000,
                0.0, 0.0,
                1.000, 0.0,
                1.000, 1.000,

                // Bottom face
                0.0, 0.0,
                1.000, 0.0,
                1.000, 1.000,
                0.0, 1.000,

                // Right face
                1.000, 0.0,
                1.000, 1.000,
                0.0, 1.000,
                0.0, 0.0,

                // Left face
                0.0, 0.0,
                1.000, 0.0,
                1.000, 1.000,
                0.0, 1.000,
            ];

        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords1), gl.STATIC_DRAW);
        cubeVertexTextureCoordBuffer1.itemSize = 2;


/////////////////////////////////////////////// vertex ////////////////////////////////////////////////

        cubeVertexIndexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
        var cubeVertexIndices = [
            0, 1, 2,      0, 2, 3,    // Front face
            4, 5, 6,      4, 6, 7,    // Back face
            8, 9, 10,     8, 10, 11,  // Top face
            12, 13, 14,   12, 14, 15, // Bottom face
            16, 17, 18,   16, 18, 19, // Right face
            20, 21, 22,   20, 22, 23  // Left face
        ]
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
        cubeVertexIndexBuffer.itemSize = 1;
        cubeVertexIndexBuffer.numItems = 36;
    }


    function drawScene() {

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

        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

        mat4.perspective((-50+depth) , canvas.width/canvas.height , 0.1 , 100.0 , pMatrix );

        //////////////////////////////////////////////////////// cube1 /////////////////////////////////////////////

        mat4.identity(mvMatrix);

        mat4.translate(mvMatrix, [4.0, 0.0, -10.0]);

        mat4.scale(mvMatrix, [1, 1, 1]);

        mat4.rotate(mvMatrix, degToRad(xRot1), [0, 1, 0]);


        mat4.identity(TMatrix);

        mat4.scale(TMatrix, [2.0, 2.0, 2.0]);


        gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
        gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
        gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, crateTextures[1]);

        gl.uniform1i(shaderProgram.samplerUniform, 0);

        setMatrixUniforms();
        gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

        /////////////////////////////////////////////// cube2 ///////////////////////////////////////////////

        mat4.identity(mvMatrix);

        mat4.translate(mvMatrix, [-4.0, 0.0, -10.0]);

        mat4.scale(mvMatrix, [1, 1, 1]);

        mat4.rotate(mvMatrix, degToRad(xRot2), [1, 0, 0]);

        gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
        gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

        mat4.identity(TMatrix);
        
        if(text_rot_flag){

            mat4.translate(TMatrix,[.5, .5, 0]);
            mat4.rotate(TMatrix,degToRad(xRot3), [0.0, 0.0, 1.0]);
            mat4.translate(TMatrix,[-.5, -.5, 0]);

        }

        gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer1);
        gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, 2, gl.FLOAT, false, 0.0, 0.0);


        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, crateTextures1[0]);
        gl.uniform1i(shaderProgram.samplerUniform, 0);

        setMatrixUniforms();
        gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);

    }

    var lastTime = 0;

    function animate() {
        var timeNow = new Date().getTime()/ 1000;
        if (lastTime != 0) {
            var elapsed = timeNow - lastTime;

            xRot1 += (xSpeed1 * elapsed);
            xRot2 += (xSpeed2 * elapsed);
            xRot3 += (xSpeed3 * elapsed);

        }
        lastTime = timeNow;
    }

    function render() {
        requestAnimFrame(render);
        drawScene();
        animate();
    }

    function webGLStart() {

        var canvas = document.getElementById("canvas");
        initGL(canvas);
        initShaders();
        initBuffers();
        initTexture();

        gl.clearColor(0.0, 0.0, 0.0, 1.0);
        gl.enable(gl.DEPTH_TEST);

        document.onkeydown = handleKeyDown;
        document.onkeyup = handleKeyUp;

        render();
    }
    
    webGLStart();
    
<script id="shader-vs" type="x-shader/x-vertex">
    attribute vec3 aVertexPosition;
    attribute vec2 aTextureCoord;
    uniform mat4 uMVMatrix;
    uniform mat4 uPMatrix;
    varying vec2 vTextureCoord;
    uniform mat4 uTMatrix;

    void main(void) {

        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);

        vTextureCoord = ((uTMatrix) * vec4(aTextureCoord, 0.0 , 1.0)).xy;

        // vTextureCoord = aTextureCoord;
    }

</script>

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

    precision mediump float;
    varying vec2 vTextureCoord;
    uniform sampler2D uSampler;

    void main(void) {

        gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));

    }

</script>

<canvas id="canvas"></canvas>
<script src="http://stabriz.bol.ucla.edu/hw3/gl-matrix.js"></script>
<script src="http://stabriz.bol.ucla.edu/hw3/webgl-utils.js"></script>

注意,我总是向后看矩阵。我从立方体的顶点开始,然后考虑应用每个矩阵。但是,大多数矩阵数学库都是这样的,最后应用的矩阵是第一个应用到顶点的矩阵。

换句话说,这 3 行

mat4.translate(TMatrix, [.5, .5, 0]);
mat4.rotate(TMatrix,degToRad(xRot3), [0.0, 0.0, 1.0]);
mat4.translate(TMatrix, [-.5, -.5, 0]);  

平均值

first translate the vertices by [-.5, -.5, 0]
then rotate around the origin by xRot
then translate the vertices by [.5, .5, 0]

请注意,这与代码的顺序相反,但实际上是这样。

其他人把矩阵看成"transforming the space",在这种情况下可以按顺序解释

move the origin to [.5, .5, 0]
rotate the origin by xRot
move the origin by [-.5, -.5, 0]