Search code examples
javascriptgraphics2dshaderwebgl

WebGl Triangle Rotation Problems: Triangle change size and get skewed


Dear Firend : I am new at WebGl. I have managed to draw and rotate a triangle -- but the problem is that the triangle 1, The triangle change size and shape while drawing. 2, I dont know how to rotate the triangle around one of its cornor.

Following is the code. I have written the code in a linear fashion with a utility class (GlUtil) that wraps up the boring tasks.

I am using a function called perc2glCoord that allow me to enter percentages and convert them into gl coordinates.

import GlUtil from "./glUtil/glUtil.js";
import perc2glCoord from "./functions/perc2glCoord.js";
const gl = GlUtil.getGl("bilza");
console.log(gl);

const vertices = [
    perc2glCoord (50)   ,perc2glCoord (50),    1,0,0,
    perc2glCoord (50)   ,perc2glCoord (75),      0,1,0,
    perc2glCoord (75)    ,perc2glCoord (50),       0,0,1,

    // perc2glCoord (25)  ,perc2glCoord (50),     1,0,0,
    // perc2glCoord (75) ,perc2glCoord (50),   0,1,0,
    // perc2glCoord (50) ,perc2glCoord (75),   0,0,1,
];

const vertexShaderSrc = 
`
attribute highp vec2 a_pos;
attribute highp vec3 a_clr;

uniform float translateX;
uniform float translateY;
uniform float angle;
varying highp vec3 vColor;

void main(void) {
    gl_Position = vec4( 
                        translateX  + (a_pos.x * cos(angle) - a_pos.y * sin(angle)),
                        translateY + (a_pos.x * sin(angle) + a_pos.y * cos(angle)),
                        1.0,
                        1.0 );
    vColor = a_clr;
}
`;
const fragShaderSrc = 
`
varying highp vec3 vColor;
void main(void) {
  gl_FragColor = vec4 (vColor , 1.0);
}
`;

const vertexShader = GlUtil.createShader(gl,vertexShaderSrc,gl.VERTEX_SHADER);
const fragmentShader = GlUtil.createShader(gl,fragShaderSrc,gl.FRAGMENT_SHADER);

const programe = GlUtil.getProgram(gl,vertexShader,fragmentShader);

const VOB = GlUtil.getBuffer(gl);

 GlUtil.bindBuffer(gl,VOB,vertices);

GlUtil.linkNuseProgram(gl,programe);

let angleValue = 0;
function draw(){
GlUtil.setAttribute(gl,"a_pos",programe, 2 ,4*5,0);
GlUtil.setAttribute(gl,"a_clr",programe, 3 , 4*5,2 * 4);

const translateXLoc = gl.getUniformLocation(programe, "translateX");
gl.uniform1f(translateXLoc,0.0);

const translateYLoc = gl.getUniformLocation(programe, "translateY");
gl.uniform1f(translateYLoc,0.0);
const angleLoc = gl.getUniformLocation(programe, "angle");
const rands  = Math.PI * angleValue /180;
gl.uniform1f(angleLoc,rands);
angleValue+= 0.1;

/////////////////////---draw-----------------
GlUtil.clear(gl,0.1,0.1,0.2);
gl.drawArrays(gl.TRIANGLES , 0, 3); 
requestAnimationFrame(draw);
}

draw(); 

---

Here is the GlUtil helper object



export default class GlUtil {


static getGl(canvasId :string ="bilza"):WebGLRenderingContext{
    const canvas = document.getElementById(canvasId) as HTMLCanvasElement;

    if (canvas == null){
        throw new Error("canvas not found");
    }

    const gl = canvas.getContext("webgl");
    if (gl == null) {
        throw new Error("Unable to initialize WebGL. Your browser or machine may not support it.");
    }
//---Got gl    
return gl;
}

static getProgram(gl :WebGLRenderingContext,vshader:WebGLShader, fshader :WebGLShader) :WebGLProgram {
    const pgm = gl.createProgram();
     
 if (pgm == null){throw new Error("failed to create program");}   
 //-----------   
    gl.attachShader(pgm, vshader);
    gl.attachShader(pgm, fshader);
 //-------------
 // pgm.vertexPosAttrib = gl.getAttribLocation( pgm , 'pos');
 // this.gl.useProgram(this.program);  
 return pgm; 
 }

static getBuffer(gl :WebGLRenderingContext):WebGLBuffer{
    let b  =  gl.createBuffer();
    if (b == null){throw("failed to create buffer");}
    return b;
}

static createShader(gl :WebGLRenderingContext, shaderSource :string, shaderType:number):WebGLShader {
    var shader = gl.createShader(shaderType);
if (shader == null){
    throw new Error("shaders could not be created");
}
    gl.shaderSource(shader, shaderSource);
    gl.compileShader(shader);

    let compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (!compiled) {
      // There are errors, so display them
      var errors = gl.getShaderInfoLog(shader);
      console.log('Failed to compile with these errors:' + "type:" + shaderType, errors );
    }
return shader;
}
static bindBuffer(gl :WebGLRenderingContext,buff :WebGLBuffer,buffData :number[]){
gl.bindBuffer(gl.ARRAY_BUFFER, buff);
gl.bufferData( gl.ARRAY_BUFFER, new Float32Array(buffData), 
gl.STATIC_DRAW);

}
static linkNuseProgram(gl :WebGLRenderingContext,prgrm :WebGLProgram){
    gl.linkProgram(prgrm);
    gl.useProgram(prgrm);
}
static clear(gl :WebGLRenderingContext,r:number=0,g:number=0,b:number=0,a:number=1){
    gl.clearColor(r,g,b,a);
    gl.clear(gl.COLOR_BUFFER_BIT);
}
////////////////////////////////////////////////////
static setAttribute(gl :WebGLRenderingContext,nameStr :string,programe :WebGLProgram,numberOfComps :number,stride:number, offset :number=0){

const vertexPosAttrib = gl.getAttribLocation( programe, `${nameStr}`);
gl.enableVertexAttribArray( vertexPosAttrib);

gl.vertexAttribPointer( 
vertexPosAttrib, //index 
numberOfComps,               //number of components =2 x and y
gl.FLOAT,       //data type
false,          //normalized
stride ,         //stride - the comple vertex row bytes
offset               //offset = 0 
); 
}




///////////////////////////////////////////////
}

Anf finally here is the picture of the triangle

enter image description here

Tried all the examples and help that I could find on the internet including re-learning the math


Solution

  • Since GL's drawing-area is stretched to fit the viewport, you need to take into account aspect ratio of the view in gl_Position calculation.

    For example, appending aspectRatio parameter to the vertex shader,

    uniform float aspectRatio;
    :
    
    gl_Position = vec4( 
                        (translateX + (a_pos.x * cos(angle) - a_pos.y * sin(angle))) / aspectRatio,
                        translateY + (a_pos.x * sin(angle) + a_pos.y * cos(angle)),
                        1.0,
                        1.0 );
    

    pass the viewport aspect-ratio as follows.

    const viewport = gl.getParameter(gl.VIEWPORT); // [x, y, width, height]
    const aspectRatioLoc = gl.getUniformLocation(programe, "aspectRatio");
    gl.uniform1f(aspectRatioLoc, viewport[2] / viewport[3]);