Showing axis in WebGL

Hi,

I want to show the input box and allow users to input the rotation value and translation value to update the model. However, I think users does not know where is X, Y, Z axis. Is it possible to show the X, Y, Z axis in WebGL and how to show them correctly? Is there any existing functions?

There is no “glShowAxis” function. And how could there be? OpenGL doesn’t have a concept of “model”. And WebGL, which is shader-based, has no concept of “space” or “axes” or anything of the like.

If you want to draw axes, then you must draw axes. And if you want these axes to align with a particular model’s orientation, then you must do the math to align them with that model’s orientation (which usually consists of using that model’s orientation when rendering the axes).

Oh, do you mean I need to create some buffers to store the axis vertex, color and draw them using line, create some textures to show the x, y, z axis labels?

Hello Phinehas,

I am totally new to webGL and trying to do something what you are trying above. I need to show the x, y and z axes in a 3D perspective and place 3D cubes on the graph based
on the data. I do have some code written for drawing the x y and z axes by using the mode “LINES” but I am passing the wrong co-ordinates. I believe I shouldnt have to pass such coordinates manually. Here is my code:

var canvas = document.getElementById(‘my_Canvas’);
var gl = canvas.getContext(‘experimental-webgl’);

     var vertices = [
    -0.8,-0.8, 0.8 , 0.8,-0.8,0,
    -0.8,-0.8, 0.8,  -0.8,0.8,0,
    -0.8,-0.8, 0.8,  0.6,0.6,-0.6
     ]          
  
     var vertex_buffer = gl.createBuffer();
    
     gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);      
    
     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
  
     gl.bindBuffer(gl.ARRAY_BUFFER, null);        

    
     var vertCode =
        'attribute vec3 coordinates;' +
        'void main(void) {' +
           ' gl_Position = vec4(coordinates, 1.0);' +
        '}';
  
     var vertShader = gl.createShader(gl.VERTEX_SHADER);
    
     gl.shaderSource(vertShader, vertCode);
    
     gl.compileShader(vertShader);
   
     var fragCode =
        'void main(void) {' +
           'gl_FragColor = vec4(0.5, 0.5, 0.5, 0.9);' +
        '}';
    
     var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
   
     gl.shaderSource(fragShader, fragCode);
   
     gl.compileShader(fragShader);
   
     var shaderProgram = gl.createProgram();
    
     gl.attachShader(shaderProgram, vertShader);
    
     gl.attachShader(shaderProgram, fragShader);
    
     gl.linkProgram(shaderProgram);
   
     gl.useProgram(shaderProgram);
  
     gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
    
     var coord = gl.getAttribLocation(shaderProgram, "coordinates");
     
     gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
   
     gl.enableVertexAttribArray(coord);
    
     gl.clearColor(0.0, 0.0, 0.0, 0.1);
    
     gl.enable(gl.DEPTH_TEST);
   
     gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    
     gl.viewport(0,0,canvas.width,canvas.height);
   
     gl.drawArrays(gl.LINES, 0, 6);

Do you think I need to do matrix math to get the 3d axes shown? Could you please help me.

If you want to draw object-space axes, you need to multiply this by the appropriate model-view matrix.

Also, those vertex coordinates are clearly wrong. Start with:

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

Change the 1s if you want different lengths, but the zeros should stay as-is (otherwise you’re drawing lines parallel to the axes rather than the axes themselves).

Thanks GClements for responding. I get what you mean. I changed my vertices to be

 var vertices = [
        0,0,0 , 1,0,0,
        0,0,0 ,  0,1,0,
        0,0,0 ,  0,0,1
         ] 

Since I don’t want to show the negative axes. I also modified the vertex shader such that the origin shows up near the bottom left corner of the canvas.

var vertCode =
            'attribute vec4 coordinates;' +
            'void main(void) {' +
            'vec2 zeroToOne = coordinates.xy; ' +
               'vec2 zeroToTwo = zeroToOne * 2.0;' +
               'vec2 clipSpace = zeroToTwo - 0.8;' +
               'gl_Position = vec4(clipSpace ,0, 1);' +
            '}';

I can see the x and the y axes but not the z axes for some reason.

Without any transformation, you’re looking at the Z axis end-on. Both vertices will have identical screen coordinates.

1 Like

I see what you mean. Thanks GClements.

I tried doing some matrix math. I actually referred an example under WebGL Fundamentals, where they try to translate and rotate the letter F in 3D. Felt I need to do something similar. This is the modified code.

         var canvas = document.getElementById('my_Canvas');
         var gl = canvas.getContext('experimental-webgl');
        

         var vertices = [
        0,0,0 , 1,0,0,
        0,0,0 ,  0,1,0,
        0,0,0 ,  0,0,1
         ];     
      
         var vertex_buffer = gl.createBuffer();
        
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);      
        
         gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
      
         gl.bindBuffer(gl.ARRAY_BUFFER, null);        

        
         var vertCode =
            'attribute vec4 coordinates;' +

            'uniform mat4 u_matrix;'+

            'void main(void) {' +
            'vec2 zeroToOne = coordinates.xy; ' +
               'vec2 zeroToTwo = zeroToOne * 2.0;' +
               'vec2 clipSpace = zeroToTwo - 0.8;' +
               'gl_Position = u_matrix * vec4(clipSpace ,0, 1);' +
            '}';
      
         var vertShader = gl.createShader(gl.VERTEX_SHADER);
        
         gl.shaderSource(vertShader, vertCode);
        
         gl.compileShader(vertShader);
       
         var fragCode =
            'void main(void) {' +
               'gl_FragColor = vec4(0.5, 0.5, 0.5, 0.9);' +
            '}';
        
         var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
       
         gl.shaderSource(fragShader, fragCode);
       
         gl.compileShader(fragShader);
       
         var shaderProgram = gl.createProgram();
        
         gl.attachShader(shaderProgram, vertShader);
        
         gl.attachShader(shaderProgram, fragShader);
        
         gl.linkProgram(shaderProgram);
       
         gl.useProgram(shaderProgram);
      
         gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
        
         var coord = gl.getAttribLocation(shaderProgram, "coordinates");

         var matrixLocation = gl.getUniformLocation(shaderProgram,"u_matrix");
         
         gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
       
         gl.enableVertexAttribArray(coord);
        
         gl.clearColor(0.0, 0.0, 0.0, 0.1);
        
         gl.enable(gl.DEPTH_TEST);
       
         gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        
         gl.viewport(0,0,canvas.width,canvas.height);

          var translation = [45, 150, 0];
          var rotation = [degToRad(40), degToRad(25), degToRad(325)];

          var matrix = m4.projection(gl.canvas.clientWidth, gl.canvas.clientHeight, 400);
         var matrix = m4.translate(matrix, translation[0], translation[1], translation[2]);
           matrix = m4.xRotate(matrix, rotation[0]);
          matrix = m4.yRotate(matrix, rotation[1]);
         // matrix = m4.zRotate(matrix, rotation[2]);

         
         gl.uniformMatrix4fv(matrixLocation, false, matrix);
       
         gl.drawArrays(gl.LINES, 0, 6);

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

         var m4 = {

          projection: function(width, height, depth){
           
             return [
             2 / width, 0, 0, 0,
             0, -2 / height, 0, 0,
             0, 0, 2 / depth, 0,
             -1, 1, 0, 1,
	 		   ];


          },

          multiply: function(a, b) {
          var a00 = a[0 * 4 + 0];
          var a01 = a[0 * 4 + 1];
          var a02 = a[0 * 4 + 2];
          var a03 = a[0 * 4 + 3];
          var a10 = a[1 * 4 + 0];
          var a11 = a[1 * 4 + 1];
          var a12 = a[1 * 4 + 2];
          var a13 = a[1 * 4 + 3];
          var a20 = a[2 * 4 + 0];
          var a21 = a[2 * 4 + 1];
          var a22 = a[2 * 4 + 2];
          var a23 = a[2 * 4 + 3];
          var a30 = a[3 * 4 + 0];
          var a31 = a[3 * 4 + 1];
          var a32 = a[3 * 4 + 2];
          var a33 = a[3 * 4 + 3];
          var b00 = b[0 * 4 + 0];
          var b01 = b[0 * 4 + 1];
          var b02 = b[0 * 4 + 2];
          var b03 = b[0 * 4 + 3];
          var b10 = b[1 * 4 + 0];
          var b11 = b[1 * 4 + 1];
          var b12 = b[1 * 4 + 2];
          var b13 = b[1 * 4 + 3];
          var b20 = b[2 * 4 + 0];
          var b21 = b[2 * 4 + 1];
          var b22 = b[2 * 4 + 2];
          var b23 = b[2 * 4 + 3];
          var b30 = b[3 * 4 + 0];
          var b31 = b[3 * 4 + 1];
          var b32 = b[3 * 4 + 2];
          var b33 = b[3 * 4 + 3];

    return [
      b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
      b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
      b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
      b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
      b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
      b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
      b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
      b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
      b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
      b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
      b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
      b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
      b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
      b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
      b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
      b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33,
    ];
  },


    translation : function(tx, ty, tz) {
                return [
                    1, 0, 0, 0,
                    0, 1, 0, 0,
                    0, 0, 1, 0,
                    tx,ty,tz,1
                ];
    },

    xRotation: function(angleInRadians) {
    var c = Math.cos(angleInRadians);
    var s = Math.sin(angleInRadians);

                return [
                1, 0, 0, 0,
                0, c, s, 0,
                0, -s, c, 0,
                0, 0, 0, 1,
                ];
    },

    yRotation: function(angleInRadians) {
    var c = Math.cos(angleInRadians);
    var s = Math.sin(angleInRadians);

                return [
                c, 0, -s, 0,
                0, 1, 0, 0,
                s, 0, c, 0,
                0, 0, 0, 1,
                ];
    },

    zRotation: function(angleInRadians) {
    var c = Math.cos(angleInRadians);
    var s = Math.sin(angleInRadians);

                return [
                c, s, 0, 0,
                -s, c, 0, 0,
                0, 0, 1, 0,
                0, 0, 0, 1,
                ];
    },

    translate: function(m, tx, ty, tz) {
    return m4.multiply(m, m4.translation(tx, ty, tz));
    },

    xRotate: function(m, angleInRadians) {
    return m4.multiply(m, m4.xRotation(angleInRadians));
    },

    yRotate: function(m, angleInRadians) {
    return m4.multiply(m, m4.yRotation(angleInRadians));
    },

    zRotate: function(m, angleInRadians) {
    return m4.multiply(m, m4.zRotation(angleInRadians));
    },

    };

But I don’t clearly understand the projection matrix or would I really need to do that. As for my understanding, I assumed I would have to only rotate along the y axis. So that I would be able to see the x and z tilted as well. But after I added the matrix math, I cant see anything appear on the canvas.

Could you please help me understand how should I come up with an appropriate matrix.

I modified the code a little. But I can still see only 2 axes on the canvas… I would really appreciate if someone could help me with understanding how to write a matrix that would just tilt the axes a little so that it looks something like a 3d Object axes. What I have now is something really funny.

     var canvas = document.getElementById('my_Canvas');
     var gl = canvas.getContext('experimental-webgl');
    

     var vertices = [
    0,0,0 , 1,0,0,
    0,0,0 ,  0,1,0,
    0,0,0 ,  0,0,-1
     ];     
  
     var vertex_buffer = gl.createBuffer();
    
     gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);      
    
     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
  
     gl.bindBuffer(gl.ARRAY_BUFFER, null);        

    
     var vertCode =
        'attribute vec4 coordinates;' +

        'uniform mat4 u_matrix;'+
   

        'void main(void) {' +
        'vec2 zeroToOne = coordinates.xy; ' +
           'vec2 zeroToTwo = zeroToOne * 2.0;' +
           'vec2 clipSpace = zeroToTwo - 0.8;' +
           'gl_Position = u_matrix * vec4(clipSpace ,0, 1);' +
        
        '}';
  
     var vertShader = gl.createShader(gl.VERTEX_SHADER);
    
     gl.shaderSource(vertShader, vertCode);
    
     gl.compileShader(vertShader);
   
     var fragCode =
        'void main(void) {' +
           'gl_FragColor = vec4(0.5, 0.5, 0.5, 0.9);' +
        '}';
    
     var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
   
     gl.shaderSource(fragShader, fragCode);
   
     gl.compileShader(fragShader);
   
     var shaderProgram = gl.createProgram();
    
     gl.attachShader(shaderProgram, vertShader);
    
     gl.attachShader(shaderProgram, fragShader);
    
     gl.linkProgram(shaderProgram);
   
     gl.useProgram(shaderProgram);
  
     gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
    
     var coord = gl.getAttribLocation(shaderProgram, "coordinates");

     

     var matrixLocation = gl.getUniformLocation(shaderProgram,"u_matrix");
     
     gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
   
     gl.enableVertexAttribArray(coord);
    
     gl.clearColor(0.0, 0.0, 0.0, 0.1);
    
     gl.enable(gl.DEPTH_TEST);
   
     gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    
     gl.viewport(0,0,canvas.width,canvas.height);

      var translation = [45, 150, 0];
      var rotation = [1, 0.69,0.6 ];

       var matrix = getProjection(40, canvas.width/canvas.height, 1, 100);
      // matrix = translate(matrix, translation[0], translation[1], translation[2]);           
      matrix =  yRotate(matrix, rotation);
      matrix = xRotate(matrix, rotation);
       matrix =  zRotate(matrix, rotation);        

     
     gl.uniformMatrix4fv(matrixLocation, false, matrix);

         
   
      gl.drawArrays(gl.LINES, 0, 6);

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

      function getProjection(angle, a, zMin, zMax)
      {
        
        return [
           1, 0 , 0, 0,
           0, 1, 0, 0,
           0, 0, 1, 0,
           0, 0, 0, 1 
		   ];
      };

      function translate(m, tx, ty, tz)
      {
          var translation =  doTranslation(tx, ty, tz);
          var translationProduct = multiply(m, translation);
          return translationProduct;
      };

      function doTranslation(tx, ty, tz)
      {
        return [
                1, 0, 0, 0,
                0, 1, 0, 0,
                0, 0, 1, 0,
                tx,ty,tz,1
            ];
      };

      function xRotate(m, rotation)
      {
          var xRotation = doXRotation(rotation[0]);
          var xRotationProduct = multiply(m , xRotation);
          return xRotationProduct;
      };

      function doXRotation(angleInRadians)
      {
        var c = Math.cos(angleInRadians);
        var s = Math.sin(angleInRadians);

            return [
            1, 0, 0, 0,
            0, c, s, 0,
            0, -s, c, 0,
            0, 0, 0, 1,
            ];
      };

      function yRotate(m, rotation)
      {
          var yRotation = doYRotation(rotation[1]);
          var yRotationProduct = multiply(m, yRotation);
          return yRotationProduct;
      };

      function doYRotation(angleInRadians)
      {
        var c = Math.cos(angleInRadians);
        var s = Math.sin(angleInRadians);

            return [
            c, 0, -s, 0,
            0, 1, 0, 0,
            s, 0, c, 0,
            0, 0, 0, 1,
            ];
      };

      function zRotate(m, rotation)
      {
          var zRotation = doZRotation(rotation[2]);
          var zRotationProduct = multiply(m, zRotation);
          return zRotationProduct;
      };

      function doZRotation(angleInRadians)
      {
        var c = Math.cos(angleInRadians);
        var s = Math.sin(angleInRadians);

            return [
            c, s, 0, 0,
            -s, c, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1,
            ];
      };



      function rotateX(m, angle) {
        var c = Math.cos(angle);
        var s = Math.sin(angle);
        var mv1 = m[1], mv5 = m[5], mv9 = m[9];

        m[1] = m[1]*c-m[2]*s;
        m[5] = m[5]*c-m[6]*s;
        m[9] = m[9]*c-m[10]*s;

        m[2] = m[2]*c+mv1*s;
        m[6] = m[6]*c+mv5*s;
        m[10] = m[10]*c+mv9*s;
     }

     function rotateY(m, angle) {
        var c = Math.cos(angle);
        var s = Math.sin(angle);
        var mv0 = m[0], mv4 = m[4], mv8 = m[8];

        m[0] = c*m[0]+s*m[2];
        m[4] = c*m[4]+s*m[6];
        m[8] = c*m[8]+s*m[10];

        m[2] = c*m[2]-s*mv0;
        m[6] = c*m[6]-s*mv4;
        m[10] = c*m[10]-s*mv8;
     }

      function multiply(a, b)
      {
        var a00 = a[0 * 4 + 0];
        var a01 = a[0 * 4 + 1];
        var a02 = a[0 * 4 + 2];
        var a03 = a[0 * 4 + 3];
        var a10 = a[1 * 4 + 0];
        var a11 = a[1 * 4 + 1];
        var a12 = a[1 * 4 + 2];
        var a13 = a[1 * 4 + 3];
        var a20 = a[2 * 4 + 0];
        var a21 = a[2 * 4 + 1];
        var a22 = a[2 * 4 + 2];
        var a23 = a[2 * 4 + 3];
        var a30 = a[3 * 4 + 0];
        var a31 = a[3 * 4 + 1];
        var a32 = a[3 * 4 + 2];
        var a33 = a[3 * 4 + 3];
        var b00 = b[0 * 4 + 0];
        var b01 = b[0 * 4 + 1];
        var b02 = b[0 * 4 + 2];
        var b03 = b[0 * 4 + 3];
        var b10 = b[1 * 4 + 0];
        var b11 = b[1 * 4 + 1];
        var b12 = b[1 * 4 + 2];
        var b13 = b[1 * 4 + 3];
        var b20 = b[2 * 4 + 0];
        var b21 = b[2 * 4 + 1];
        var b22 = b[2 * 4 + 2];
        var b23 = b[2 * 4 + 3];
        var b30 = b[3 * 4 + 0];
        var b31 = b[3 * 4 + 1];
        var b32 = b[3 * 4 + 2];
        var b33 = b[3 * 4 + 3];

return [
  b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
  b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
  b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
  b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
  b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
  b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
  b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
  b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
  b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
  b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
  b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
  b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
  b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
  b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
  b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
  b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33,
];

      };    

Please see attached the image of how it looks now. Nothing similar to what I want. :frowning:

You’re discarding the Z coordinate from the vertices, so the Z axis isn’t going to be visible. The body of your vertex shader should be just

    gl_Position = u_matrix * coordinates;

Any other transformations should be folded into the matrix.

I did that extra calculation of clipSpace so as to move the origin close to the bottom left corner of the canvas. Else the origin was always at the center. I used translate and moved the origin where I wanted to. But transformation matrices are the real challenge now. I can see three axes now by the way… Thanks Clements. I wish there was an easier way to understand the projection, view and model matrices.

You can place your examples on Playground: Plunker - Triangle. glMatrix, WebGL, TypeScript I will help to show your problem. I use it this example: WebGL 1.0, TypeScript and glMatrix

Triangle_WebGLTypeScript