立方体旋转动画

优质
小牛编辑
134浏览
2023-12-01

要通过WebGL渲染出立方体旋转的动画效果,你首要了解“帧”这个概念,比如你观看的视频其实就是一帧一帧的图片连续播放的效果,只要图片刷新的频率的不是太低,人的眼睛都不会察觉,一般30~60FPS就可以。 WebGL如何产生一帧一帧的图片,这个很简单,执行一次绘制函数gl.drawArrays(),WebGL图形系统就会通知GPU渲染管线处理顶点数据生成一帧RGB像素数据显示在屏幕canvas画布上。只要周期性保持一定的频率调用gl.drawArrays()就可以生成一帧一帧的图片, 在这个过程中同时要利用Javascript程序更新顶点的旋转矩阵,如果顶点的位置不变化,渲染出来的都是一样的图片,自然也没有动画的效果。

浏览器提供了一个方法requestAnimationFrame()可以实现周期性调用某个函数,主要用于动画,该方法如何使用可以查看文章《HTML5 定时器》。

在1.9节光照立方体的基础上进行更改,一方面是周期性执行绘制方法gl.drawArrays(),另一方面以一定的旋转速度更新立方体的旋转矩阵,原来使用着色器语言定义的旋转矩阵更改为使用Javascript语句创建好再传递给顶点着色器, gl.drawArrays()每次执行的时候,都会重新传入着色器顶点旋转矩阵数据,并渲染出来。

声明矩阵变量

/**uniform声明旋转矩阵变量mx、my**/
uniform mat4 mx;//绕x轴旋转矩阵
uniform mat4 my;//绕y轴旋转矩阵

在顶点着色器代码中使用关键字uniform声明两个旋转矩阵变量mxmy,分别表示绕x轴、y轴的旋转矩阵。旋转矩阵数据和光照数据一样适用于所有的非顶点数据,使用关键字uniform声明,不能使用attribute关键字声明。

传入mx矩阵数据

/**从program对象获得旋转矩阵变量mx、my地址**/
var mx = gl.getUniformLocation(program,'mx');
var my = gl.getUniformLocation(program,'my');
/**绕x轴旋转45度**/
var mxArr = new Float32Array([
    1,0,0,0,
    0,Math.cos(Math.PI/4),-Math.sin(Math.PI/4),0,
    0,Math.sin(Math.PI/4),Math.cos(Math.PI/4),0,
    0,0,0,1
]);
//把数据mxArr传递给着色器旋转矩阵变量mx
gl.uniformMatrix4fv(mx, false, mxArr);

上节课定义顶点旋转矩阵使用的着色器语言,下面的案例是先在Javascript程序中使用类型数组Float32Array()创建旋转矩阵的数据,然后使用WebGL APIgl.uniformMatrix4fv()把数据传递给着色器。WebGL中给着色器中不同关键字声明的不同类型变量传递数据, 要使用不同的WebGL API,uniform关键字声明的mat4类型变量使用WebGL APIgl.uniformMatrix4fv()uniform关键字声明的mat2类型变量使用WebGL APIuniformMatrix2fv()uniform关键字声明的一个浮点数使用gl.uniform1f()传递, uniform关键字声明的vec4类型变量和mat4一样使用uniform4fv(变量地址名,new Float32Array([a,b,c,d]))传递,也可以使用uniform4f(变量地址名,a,b,c,d)传递,attribute关键字声明的变量使用WebGL APIgl.vertexAttribPointer()传递。

绘制函数draw()

该绘制函数draw()可以使用第requestAnimationFrame(draw);代码实现draw()函数的循环调用,因为要实现立方体绕y轴旋转,所以要在draw函数中更新my旋转矩阵的数据,同时利用WebGL APIgl.uniformMatrix4fv()把新的矩阵数据传递给着色器矩阵变量my, 在函数重复调用gl.drawArrays(gl.TRIANGLES,0,36);不停绘制旋转后的顶点数据,所有需要更新的数据都要写在draw函数中,不需要反复执行的代码写在draw函数外,比如mx旋转矩阵只需要执行传入一次不在改变。

/**
 * 定义绘制函数draw(),定时更新旋转矩阵数据,并调用WebGL绘制API
 ***/
var angle = Math.PI/4;//起始角度
function draw() {
    // gl.clear(gl.COLOR_BUFFER_BIT);//清空画布上一帧图像
    /**
     * 立方体绕y轴旋转
     ***/
    angle += 0.01;//每次渲染角度递增,每次渲染不同的角度
    var sin = Math.sin(angle);//旋转角度正弦值
    var cos = Math.cos(angle);//旋转角度余弦值
    var myArr = new Float32Array([cos,0,-sin,0,  0,1,0,0,  sin,0,cos,0,  0,0,0,1]);
    gl.uniformMatrix4fv(my, false, myArr);
    requestAnimationFrame(draw);
    /**执行绘制命令**/
    gl.drawArrays(gl.TRIANGLES,0,36);
}
draw();