前言
经过前面的学习,我们已经可以绘制基本的图形了。本小节将介绍基本的图形变换,介绍在WebGL中,如何对基本的图形进行平移、旋转和缩放。
平移
在前面的小节中,我们已经绘制过一个三角形,那时候,它看起来是这样的:
我们知道,在WebGL中,要绘制一个基本的图形,我们只需要指定顶点的位置、大小和颜色,然后调用drawArrays接口进行绘制即可。现在,我们想要实现对三角形进行一个平移,比如,把它移到右上角的地方,那如何实现呢?
其实仔细想想,你会发现,我们要移动一个三角形,只需要移动它的三个顶点即可,然后WebGL将会自动在新的顶点位置把三角形绘制出来。
其原理示意图如下:
为了进一步说明在程序中是如何实现平移的,我们先来看下顶点着色器代码:
// 顶点着色器代码(决定顶点的位置、大小、颜色)
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'uniform vec4 u_Translation;\n' +
'void main() {\n' +
' gl_Position = a_Position + u_Translation;\n' + // 设置顶点的位置
' gl_PointSize = 10.0;\n' + // 设置顶点的大小
'}\n';
主要看到两个地方有所变动 :
1、添加了 uniform vec4 u_Translation 定义
2、顶点的位置使用两个 vec4 变量进行相加, gl_Position = a_Position + u_Translation
我们以前曾经使用过 attribute变量,用于直接表示顶点的位置,那时我们知道每个顶点的位置都不一样,而现在,为了对三角形进行平移,实际上是对三个顶点都进行了相同的平移,也就是说,平移的量对所有顶点是一致的,这样所有顶点共享的变量在WebGL中使用uniform 关键字进行表示。
既然定义了 uniform 变量,那么就需要对它进行赋值,对它的赋值跟对 attribute变量进行赋值的流程是完全一样的。
// 三角形顶点的平移量
var Tx = 0.5, Ty = 0.5, Tz = 0.0;
var u_Translation = context.getUniformLocation(context.program,'u_Translation');
context.uniform4f(u_Translation, Tx, Ty, Tz, 0.0);
我们定义了 Tx,Ty,Tz 表示顶点在x、y、z 轴上的位移,最后使用 uniform4f 方法对 u_Translation变量进行了赋值,这里有个地方要注意,方法 uniform4f 最后一个参数我们设置为了0.0,因为我们要确保两个vec4变量相加后,第四个参数为1。a_Position 变量第四个参数已经设置为1,所以u_Translation第四个参数只能设置为0。
最后,我们来看下平移后的效果图:
旋转
旋转看起来比平移要复杂一些,在考虑旋转的时候,我们要考虑三个点:
- 绕着哪个轴旋转
- 旋转的方向(顺时针还是逆时针)
- 旋转的角度
在这里,我们以绕着Z轴逆时针旋转来推导
由上图可以看到,当一个点
p(x,y,z)
沿着Z轴逆时针旋转到
p′(x′,y′,z′)
时,可以得到p点满足如下等式:
同理, p′ 也满足如下等式:
根据
p
和
有了以上旋转后的点的坐标公式,我们便可以编写顶点着色器代码了:
// 顶点着色器代码(决定顶点的位置、大小、颜色)
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'uniform float u_CosB, u_SinB;\n' +
'void main() {\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' +
'gl_PointSize = 10.0;\n' + // 设置顶点的大小
'}\n';
注意,我们可以通过 . 符号来访问一个vec4变量的x、y、z、w 四个分量值 ,绕着Z轴旋转,只需要计算x和y坐标值即可。
对于u_CosB和u_SinB,由于对三角形三个顶点而言,旋转的角度是一样的,所以变量应该是所有顶点共享,使用uniform进行声明,其赋值如下:
// 旋转的角度
var ANGLE = 30;
var radian = Math.PI * ANGLE / 180.0; //转为弧度
var cosB = Math.cos(radian);
var sinB = Math.sin(radian);
var u_CosB = context.getUniformLocation(context.program, 'u_CosB');
var u_SinB = context.getUniformLocation(context.program, 'u_SinB');
context.uniform1f(u_CosB, cosB);
context.uniform1f(u_SinB, sinB);
这段代码很简单,相信都应该明白了。
绕着Z轴逆序旋转30度的效果图:
如果想要的是顺时针的旋转,把ANGLE设置为负数即可,大家可以拿源码改改试试,这就是三角函数的精妙之处。
以上推导的是绕着Z轴进行旋转,其实绕着x轴或者y轴也可以用类似的方式进行推导,大家有兴趣可以试着推导一下。
缩放
缩放其实是比较简单的,我们来看缩放示意图:
点 p(x,y,z) 缩放后,在三个轴的缩放因子为: Sx 、 Sy 、 Sz ,则缩放后,点 p′(x′,y′,z′) 的坐标如下:
然后我们来看下顶点着色器的代码:
// 顶点着色器代码(决定顶点的位置、大小、颜色)
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'uniform float a_Scale;\n' +
'void main() {\n' +
' gl_Position.x = a_Position.x * a_Scale;\n' +
' gl_Position.y = a_Position.y * a_Scale;\n' +
' gl_Position.z = a_Position.z * a_Scale;\n' +
' gl_Position.w = 1.0;\n' +
' gl_PointSize = 10.0;\n' + // 设置顶点的大小
'}\n';
我们定义了一个uniform变量a_Scale,所有顶点都使用相同的缩放,然后对顶点的x、y、z值分别进行坐标变换。a_Scale的初始化也非常的简单。
// 三角形顶点的缩放
var scale = 0.5;
var u_Scale = context.getUniformLocation(context.program, 'a_Scale');
context.uniform1f(u_Scale, scale);
缩放后的三角形效果图:
小结
三角形的基础变换是非常重要的知识,还是要花点时间理解一下,目前使用的是简单的数学知识进行推导,其实,在图形学中,真正强大的是用矩阵进行计算,后面将会详细解说。
源码
参考
<<WebGL编程指南>>