移动、旋转、缩放
#移动
只需要传入坐标偏移量即可:u_Translation就是传入的偏移量,每次a_Position加上偏移量之后,就会移动图形
varVSHADER_SOURCE=attributevec4a_Position;\n+uniformvec4u_Translation;\n+voidmain(){\n+gl_Position=a_Position+u_Translation;\n+}\n;//FragmentshaderprogramvarFSHADER_SOURCE=voidmain(){\n+gl_FragColor=vec4(1.0,0.0,0.0,1.0);\n+}\n;functionmain(){//Retrievecanvaselementvarcanvas=document.getElementById(webgl);vargl=getWebGLContext(canvas);if(!gl){console.log(FailedtogettherenderingcontextforWebGL);return;}if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)){console.log(Failedtointializeshaders.);return;}varn=initVertexBuffers(gl);if(n0){console.log(Failedtosetthepositionsofthevertices);return;}//偏移量传值varu_Translation=gl.getUniformLocation(gl.program,u_Translation);if(!u_Translation){console.log(Failedtogetthestoragelocationofu_Translation);return;}gl.uniform4f(u_Translation,Tx,Ty,Tz,0.0);gl.clearColor(0,0,0,1);gl.clear(gl.COLOR_BUFFER_BIT);gl.drawArrays(gl.TRIANGLES,0,n);}
#旋转
旋转必须要指明:
旋转轴:图形将围绕旋转轴旋转旋转方向:方向是顺时针还是逆时针旋转角度:图形旋转经过的角度描述旋转:绕Z轴旋转了β角度,如果β是正值,那么看到的物体就是逆时针旋转的。这种情况又称为正旋转。正旋转又称为右手法则旋转。
设点p(x,y,z)旋转β角度后变成p(x,y,z):首先旋转是绕Z轴信息的,所以z坐标不会变化,可以直接忽略;然后x坐标和y坐标如下:
上图,r是从圆点到点p的距离,而α是X轴旋转到p的角度,用这两个变量计算出点p的坐标,如下:
x=rcosαy=rsinα类似地,可以使用α、r、β来表示p的坐标:
x=rcos(α+β)y=rsin(α+β)利用三角函数两角和公示,可得:
x=r(cosαcosβ-sinαsinβ))y=r(sinαcosβ+cosαsinβ))消除r和α后:
x=xcosβ-ysinβy=xsinβ-ycosβz=z我们可以将sinβ和cosβ的值传给顶点着色器,然后在着色器中根据上面的式子计算旋转后的点坐标,就可以实现旋转了。使用JavaScript内置的Math对象的sin()和cos()方法来进行三角函数运算。
代码如下:
varVSHADER_SOURCE=//x=xcosβ-ysinβ//y=xsinβ+ycosβ Equation3.3//z=zattributevec4a_Position;\n+uniformfloatu_CosB,u_SinB;\n+voidmain(){\n+gl_Position.x=a_Position.x*u_CosB-a_Position.y*u_SinB;\n+gl_Position.y=a_Position.x*u_SinB+a_Position.y*u_CosB;\n+gl_Position.z=a_Position.z;\n+gl_Position.w=1.0;\n+}\n;varFSHADER_SOURCE=voidmain(){\n+gl_FragColor=vec4(1.0,0.0,0.0,1.0);\n+}\n;//旋转角度varANGLE=90.0;functionmain(){varcanvas=document.getElementById(webgl);vargl=getWebGLContext(canvas);if(!gl){console.log(FailedtogettherenderingcontextforWebGL);return;}if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)){console.log(Failedtointializeshaders.);return;}varn=initVertexBuffers(gl);if(n0){console.log(Failedtosetthepositionsofthevertices);return;}//将旋转图形所需的数据传递给顶点着色器varradian=Math.PI*ANGLE/.0;//转换为弧度制varcosB=Math.cos(radian);varsinB=Math.sin(radian);varu_CosB=gl.getUniformLocation(gl.program,u_CosB);varu_SinB=gl.getUniformLocation(gl.program,u_SinB);if(!u_CosB
!u_SinB){console.log(Failedtogetthestoragelocationofu_CosBoru_SinB);return;}gl.uniform1f(u_CosB,cosB);gl.uniform1f(u_SinB,sinB);gl.clearColor(0,0,0,1);gl.clear(gl.COLOR_BUFFER_BIT);gl.drawArrays(gl.TRIANGLES,0,n);}functioninitVertexBuffers(gl){varvertices=newFloat32Array([0,0.5,-0.5,-0.5,0.5,-0.5]);varn=3;varvertexBuffer=gl.createBuffer();if(!vertexBuffer){console.log(Failedtocreatethebufferobject);return-1;}gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW);vara_Position=gl.getAttribLocation(gl.program,a_Position);if(a_Position0){console.log(Failedtogetthestoragelocationofa_Position);return-1;}gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0);gl.enableVertexAttribArray(a_Position);returnn;}
#变换矩阵旋转
我们可以将上面的操作看成是矩阵计算得到的结果:
上面的这个矩阵通常称为旋转矩阵。
#变换矩阵平移
平移时需要加上一个常量,来表示偏移量,可以使用4×4的矩阵以及具有第四个分量的矢量来表示,假设p的坐标为(x,y,z,1),平移之后的p的坐标为(x,y,z,1);如下:
该矩阵的乘法结果如下:
x=ax+by+cz+dy=ex+fy+gz+hz=ix+jy+kz+l1=mx+ny+oz+p根据最后一个式子就可以计算出系数m=0,n=0,o=0,p=1,这些方式都有常数项d、h、l和p,我们跟之前做平移的等式进行比较:
x=x+Txy=y+Tyz=z+Tz比较两个等式,姐可以知道a=1,b=0,c=0,d=Tx,e=0,f=1,g=0,h=Ty,i=0,j=0,k=1,l=Tz。这样就可以表示平移矩阵,如下:
#使用变换矩阵实现旋转平移
如果我们使用最开始计算点坐标在进行旋转平移变化,每次都需要进行一次新的变换。每次重新取一个新的等式,然后实现一个新的着色器,很不科学,我们可以通过变换矩阵来完成。上面我们获取到旋转的矩阵为3×3的矩形,但是平移矩阵是4×4的矩阵,阶数不同不能进行计算,可以处理一下旋转矩阵为4×4的。
实现代码如下:
varVSHADER_SOURCE=attributevec4a_Position;\n+//mat4表示4×4的矩阵uniformmat4u_xformMatrix;\n+voidmain(){\n+//矩阵相乘gl_Position=u_xformMatrix*a_Position;\n+}\n;varFSHADER_SOURCE=voidmain(){\n+gl_FragColor=vec4(1.0,0.0,0.0,1.0);\n+}\n;varANGLE=90.0;functionmain(){varcanvas=document.getElementById(webgl);vargl=getWebGLContext(canvas);if(!gl){console.log(FailedtogettherenderingcontextforWebGL);return;}if(!initShaders(gl,VSHADER_SOURCE,FSHADER_SOURCE)){console.log(Failedtointializeshaders.);return;}varn=initVertexBuffers(gl);if(n0){console.log(Failedtosetthepositionsofthevertices);return;}//创建旋转矩阵varradian=Math.PI*ANGLE/.0;//角度转换为弧度制varcosB=Math.cos(radian),sinB=Math.sin(radian);//注意webgl中矩阵是列主序的varxformMatrix=newFloat32Array([cosB,sinB,0.0,0.0,-sinB,cosB,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0]);//将旋转矩阵传输给顶点着色器varu_xformMatrix=gl.getUniformLocation(gl.program,u_xformMatrix);if(!u_xformMatrix){console.log(Failedtogetthestoragelocationofu_xformMatrix);return;}//将矩阵传递给着色器v表示可以向着色器传输多个数据值//第一个参数为uniform变量的存储位置,第二个参数在webgl中必须指定false,第三个参数是待传输的类型化数组,4×4矩阵按列主序存储在其中gl.uniformMatrix4fv(u_xformMatrix,false,xformMatrix);gl.clearColor(0,0,0,1);gl.clear(gl.COLOR_BUFFER_BIT);gl.drawArrays(gl.TRIANGLES,0,n);}functioninitVertexBuffers(gl){varvertices=newFloat32Array([0,0.5,-0.5,-0.5,0.5,-0.5]);varn=3;varvertexBuffer=gl.createBuffer();if(!vertexBuffer){console.log(Failedtocreatethebufferobject);returnfalse;}gl.bindBuffer(gl.ARRAY_BUFFER,vertexBuffer);gl.bufferData(gl.ARRAY_BUFFER,vertices,gl.STATIC_DRAW);vara_Position=gl.getAttribLocation(gl.program,a_Position);if(a_Position0){console.log(Failedtogetthestoragelocationofa_Position);return-1;}gl.vertexAttribPointer(a_Position,2,gl.FLOAT,false,0,0);gl.enableVertexAttribArray(a_Position);returnn;}
注意这里的矩阵表示法,数组为一维的,其元素排成一行,我们可以按照两种方式在数组中存储矩阵元素:按行主序和按列主序,如图:
webgl跟opengl一样,矩阵元素是按列主组存储在数组中的。
#变换矩阵-缩放
假设p经过缩放操作变成了p:
假设在三个方向X轴,Y轴,Z轴的缩放因子Sx,Sy,Sz不相关,那么有:
x=Sx×xy=Sy×yz=Sz×z写成变换矩阵如下:
我们只需要修改前面代码的矩阵就可以实现缩放效果。