简单的webgl图形的绘制和变换学习

21 阅读5分钟

缓冲区对象(多个顶点数据的绘制问题)

缓冲区对象是webGl系统中的一块内存区域,可以一次性向缓冲区对象中填入大量顶点数据,将数据保存在其中供顶点着色器使用

    //创建一组顶点数据
    //Float32Array是类型化数组
    const points = new Float32Array([
        -0.5,-0.5,
        0.5,-0.5,
        0.0,0.5,
    ])
    //创建一块内存区域
    const buffer = gl.createBuffer()
    //绑定缓存区对象
    //bindBuffer(target,buffer) buffer是已经创建好的缓存区对象 target分为 gl.ARRAY_BUFFER:表示缓存区储存的顶点的数据 gl.ELEMENT_ARRAY_BUFFER:表示缓冲区存储的是顶点的索引值
    gl.bindBuffer(gl.ARRAY_BUFFER,buffer)
    //将顶点数据写入
    //bufferData(target,data,type) data 写入缓冲区的顶点数据 type表示如何使用缓冲区对象中的数据,分为gl.STATIC_DRAW:写入一次,绘制多次,gl.STREAM_DRAW:写入一次,绘制若干次  ,gl.DYNAMIC_DRAW:写入多次,绘制多次
    gl.bufferData(gl.ARRAY_BUFFER,points,gl.STATIC_DRAW)
    //给attribute变量赋值
    //vertexAttribPointer(location,size,type,narmalized,stride,offset) 
    //size:每个顶点使用的个数,
    //type指定数据类型,要与顶点数据类型一致,分为gl.FLOAT 浮点型,gl.UNSIGNED_BYTE 无符号字节,gl.SHORT 短整型,gl.UNSIGNED_SHORT无符号短整型, gl.INT整形, gl.UNSIGN_INT无符号整型
    // normalized 是否将数据归一化到[0,1][-1,1]这个区间
    // stride两个相邻顶点间字节数 offset 数据偏移量
    gl.vertexAttribPointer(aPosition,2,gl.FLOAT,false,0,0);
    //激活attribute变量       disableVertexAttribArray禁用
    gl.enableVertexAttribArray(aPosition)

    gl.drawArrays(gl.POINTS,0,3);

缓冲区对象的执行流程

image.png

多缓冲区和数据偏移

 const aPointSize = gl.getAttribLocation(program,'aPointSize')

    const size = new Float32Array([
        10.0,
        20.0,
        30.0,
        40.0,
        50.0
    ])
    //创建一块内存区域
    const sizebuffer = gl.createBuffer()

    gl.bindBuffer(gl.ARRAY_BUFFER,sizebuffer)

    gl.bufferData(gl.ARRAY_BUFFER,size,gl.STATIC_DRAW)
    //只要一个数据 size改为1
    gl.vertexAttribPointer(aPointSize,1,gl.FLOAT,false,0,0);

    gl.enableVertexAttribArray(aPointSize)
    gl.drawArrays(gl.POINTS,0,5);

image.png 通过偏移量实现只使用一组数据完成以上操作

    const aPosition = gl.getAttribLocation(program,'aPosition')
    const points = new Float32Array([
        -0.5,-0.5,10.0,
        0.5,-0.5,20.0,
        0.0,0.0,30.0,
        0.0,0.5,40.0,
        0.5,0.5,50.0
    ])
    //创建一块内存区域
    const buffer = gl.createBuffer()

    gl.bindBuffer(gl.ARRAY_BUFFER,buffer)

    gl.bufferData(gl.ARRAY_BUFFER,points,gl.STATIC_DRAW)

    const aPointSize = gl.getAttribLocation(program,'aPointSize')
    const BYTES = points.BYTES_PER_ELEMENT
    //                                   获取两个数据间间距字节数
    //                                   需要用到三个数据,没两组数据的间距为BYTES*3
    gl.vertexAttribPointer(aPosition,2,gl.FLOAT,false,BYTES*3,0);

    gl.enableVertexAttribArray(aPosition)
    //                                      每次使用需要略过前两个数据
    gl.vertexAttribPointer(aPointSize,1,gl.FLOAT,false,BYTES*3,BYTES*2);
    //激活attribute变量       disableVertexAttribArray禁用
    gl.enableVertexAttribArray(aPointSize)
    gl.drawArrays(gl.POINTS,0,5);

多种图形绘制

    //gl.LINES如果顶点数是奇数个,最后一个参数会被忽略,如果三个点都需要被使用,可以用LINE_STRIP
    //LINE_STRIP会绘制连续的线段,但是不会闭合起点和终点
    //LINE_LOOP会闭合起点和终点
    gl.drawArrays(gl.LINE_STRIP,0,5);
        //TRIANGLES需要的顶点数至少为3或3的倍数
    gl.drawArrays(gl.TRIANGLES,0,5);
    gl.drawArrays(gl.TRIANGLE_FAN,0,5);
    
    //gl.PointSize只有在绘制点时会生效,绘制图形不生效

image.png

图形平移

着色器中

attribute vec4 aPosition;
    attribute float aTranslate;
    void main(){
        gl_Position = vec4(aPosition.x + aTranslate,aPosition.y,aPosition.z,1.0);

    }

script中

    let x = -1
    setInterval(() => {
        x += 0.01
        if(x>1){
            x = -1
        }
    gl.vertexAttrib1f(aTranslate,x);
        gl.drawArrays(gl.TRIANGLES,0,3);
    }, 60);

图形缩放

    attribute vec4 aPosition;
    attribute float aScale;
    void main(){
        gl_Position = vec4(aPosition.x * aScale,aPosition.y,aPosition.z,1.0);

    }

与图形平移原理一致

图形旋转

与平移缩放类似,但是需要给xyzw单独赋值

    const VERTEX_SHADER_SOURCE = `
    //需要控制多个属性需要创建多个变量
    attribute vec4 aPosition;
    attribute float deg;
    void main(){
        gl_Position.x = aPosition.x*cos(deg)-aPosition.y*sin(deg);
        gl_Position.y = aPosition.x*sin(deg)+aPosition.y*cos(deg);
        gl_Position.z = aPosition.z
        gl_Position.w = aPosition.w

    }
    ` //顶点着色器

计时器实现动画比较卡顿,用requestAnimationFrame

    let x = 1

    function animation(){
        x += 0.1

        gl.vertexAttrib1f(deg,x);
        gl.drawArrays(gl.TRIANGLES,0,3);
        //用于实现动画效果
        requestAnimationFrame(animation)
    }
    animation()

图形平移PLUS

矩阵用于表示点到点的转换关系

image.png

image.png 通过此法获得矩阵

1,0,0,x1,      此法获得的矩阵为行主序                        1,0,0,0,
0,1,0,y1,     webgl中矩阵为列主序,故转换为                 0,1,0,0,
0,0,1,z1,                                                 0,0,1,0,
0,0,0,1,                                                  x1,y1,z1,1,

着色器中

  const VERTEX_SHADER_SOURCE = `
    attribute vec4 aPosition;
    //使用uniform声明接受矩阵的变量,uniform对所有点生效,平移是平移所有点
    uniform mat4 mat;
    void main(){
        gl_Position = mat * aPosition;

    }
    ` //顶点着色器
    const mat = gl.getUniformLocation(program,'mat')

    //创建一个获取矩阵的函数
    function getTranlateMatrix(x = 0,y = 0,z = 0){
        return new Float32Array([
            1.0,0.0,0.0,0.0,
            0.0,1.0,0.0,0.0,
            0.0,0.0,1.0,0.0,
              x, y, z, 1.0,

    ])
    }
    
...

    let x = -1
    function animation(){
        x += 0.1
        if(x>1){
            x = -1
        }
        //获取矩阵
        const matrix = getTranlateMatrix(x)

        //给mat4变量赋值uniformMatrix4fv(location,transpose,array)location uniform变量,transpose恒为false,array矩阵
        gl.uniformMatrix4fv(mat,false,matrix);
        gl.drawArrays(gl.TRIANGLES,0,3);
        //用于实现动画效果
        requestAnimationFrame(animation)
    }
    animation()

图形缩放PLUS

image.png

image.png 只有获取矩阵的函数不同和设置x的范围不同

    //创建一个获取矩阵的函数
    function getScaleMatrix(x = 1,y = 1,z = 1){
        return new Float32Array([
            x,0.0,0.0,0.0,
            0.0,y,0.0,0.0,
            0.0,0.0,z,0.0,
            0.0,0.0,0.0,1.0,

    ])
    }

图形旋转PLUS

image.png

image.png 只有获取矩阵的函数不同和设置x的大小不同

    function getRotateMatrix(deg){
        return new Float32Array([
            Math.cos(deg),Math.sin(deg),0.0,0.0,
            -Math.sin(deg),Math.cos(deg),0.0,0.0,
            0.0,0.0,1.0,0.0,
            0.0,0.0,0.0,1.0,

    ])
    }

图形的复合变换

方法1,声明多个变量多次赋值

    const VERTEX_SHADER_SOURCE = `
    attribute vec4 aPosition;
    //使用uniform声明接受矩阵的变量,uniform对所有点生效,平移是平移所有点
    uniform mat4 translateMatrix;
    uniform mat4 scaleMatrix;
    uniform mat4 rotationMatrix;
    void main(){
        gl_Position = translateMatrix * scaleMatrix * rotationMatrix * aPosition;

    }
    ` //顶点着色器

...

    const aPosition = gl.getAttribLocation(program,'aPosition')
    const translate = gl.getUniformLocation(program,'translateMatrix')
    const scale = gl.getUniformLocation(program,'scaleMatrix')
    const rotation = gl.getUniformLocation(program,'rotationMatrix')


...

    let deg = 0
    let translateX = -1
    let scale1 = 0.1
    function animation(){
        deg += 0.01
        translateX += 0.01
        scale1 += 0.01
        if (translateX > 1) {
            translateX = -1
        }
        if (scale1 > 0.5) {
            translateX = 0.1
        }
        //获取矩阵
        const r = getRotateMatrix(deg)
        const t = getTranlateMatrix(translateX)
        const s = getScaleMatrix(scale1)

        //给mat4变量赋值uniformMatrix4fv(location,transpose,array)location uniform变量,transpose恒为false,array矩阵
        gl.uniformMatrix4fv(translate,false,t);
        gl.uniformMatrix4fv(rotation,false,r);
        gl.uniformMatrix4fv(scale,false,s);
        gl.drawArrays(gl.TRIANGLES,0,3);
        //用于实现动画效果
        requestAnimationFrame(animation)
    }
    animation()

方法2:使用多个矩阵复合成为一个矩阵 矩阵a的列* 矩阵b的行,得到新矩阵的行

    let deg = 0
    let translateX = -1
    let scale1 = 0.1
    function animation(){
        deg += 0.01
        translateX += 0.01
        scale1 += 0.01
        if (translateX > 1) {
            translateX = -1
        }
        if (scale1 > 1.5) {
            scale1 = 0.1
        }
        //获取矩阵
        const r = getRotateMatrix(deg)
        const t = getTranlateMatrix(translateX,translateX)
        const s = getScaleMatrix(scale1)

        const m = mixMatrix(mixMatrix(r,t),s)

        //给mat4变量赋值uniformMatrix4fv(location,transpose,array)location uniform变量,transpose恒为false,array矩阵
        gl.uniformMatrix4fv(mat,false,m);
        gl.drawArrays(gl.TRIANGLES,0,3);
        //用于实现动画效果
        requestAnimationFrame(animation)
    }
    animation()


//矩阵复合函数
function mixMatrix(a,b){
    const result = new Float32Array(16)
    for (let i = 0; i < 4; i++) {
        result[i] = a[i]*b[0] +a[i+4]*b[1]+a[i+8]*b[2]+a[i+12]*b[3]
        //列主序,故处理i+4
        result[i +4] = a[i]*b[4] +a[i+4]*b[5]+a[i+8]*b[2]+a[i+12]*b[3]
        result[i +8] = a[i]*b[8] +a[i+4]*b[9]+a[i+8]*b[10]+a[i+12]*b[11]
        result[i +12] = a[i]*b[12] +a[i+4]*b[13]+a[i+8]*b[14]+a[i+12]*b[15]
        
    }

    return result
}