webGL 图像变换


<!-- 图像变换 -->

图元变化

平移

我们知道,在WebGL中,要绘制一个基本的图形,我们只需要指定顶点的位置、大小和颜色,然后调用drawArrays接口进行绘制即可。现在,我们想要实现对三角形进行一个平移,比如,把它移到右上角的地方,那如何实现呢?

其实仔细想想,你会发现,我们要移动一个三角形,只需要移动它的三个顶点即可,然后WebGL将会自动在新的顶点位置把三角形绘制出来。

2019-11-25-09-17-04.png

顶点着色器代码的修改

  1. 将平移量Tx,Ty,Tz的值传入顶点着色器
  2. 将平移量加到各顶点坐标的对应的分量上
  3. 将运算结果赋值给gl_Position

为了进一步说明在程序中是如何实现平移的,我们先来看下顶点着色器代码:

// 顶点着色器代码(决定顶点的位置、大小、颜色)
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。

旋转

旋转看起来比平移要复杂一些,在考虑旋转的时候,我们要考虑三个点:

  1. 绕着哪个轴旋转
  2. 旋转的方向(顺时针还是逆时针)
  3. 旋转的角度

在这里,我们以绕着Z轴逆时针旋转来推导

2019-11-25-09-38-42.png

有了以上旋转后的点的坐标公式,我们便可以编写顶点着色器代码了:

// 顶点着色器代码(决定顶点的位置、大小、颜色)
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_CosBu_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);

如果想要的是顺时针的旋转,把ANGLE设置为负数即可

缩放

![](note_img/2019-11-25-11-29-52.png)

点p(x,y,z) 缩放后,在三个轴的缩放因子为:Sx、Sy、Sz,则缩放后,点p′(x′,y′,z′)的坐标如下:

x′ = x * Sx 
y′ = y * Sy 
z′ = z * Sz

然后我们来看下顶点着色器的代码:

// 顶点着色器代码(决定顶点的位置、大小)
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);

三角形的基础变换是非常重要的知识,还是要花点时间理解一下,目前使用的是简单的数学知识进行推导,其实,在图形学中,真正强大的是用矩阵进行计算。

变化矩阵

当情形逐渐变得复杂时,很快就会发现利用表达式运算实际上相当繁琐,我们可以使用另一个数学工具--变化矩阵,来完成这项工作。

变换矩阵:平移

![](note_img/2019-11-25-09-17-04.png)

![](note_img/2019-11-25-12-01-30.png)

变换矩阵:旋转

![](note_img/2019-11-25-09-38-42.png)

![](note_img/2019-11-25-11-53-16.png)

4 X 4 的旋转矩阵

![](note_img/2019-11-25-11-53-27.png)

变换矩阵:缩放

![](note_img/2019-11-25-12-05-03.png)

mat4 u_transMatrix // 4*4的变换矩阵

//将array 表示的4*4矩阵分配给由location 指定的uniform 变量
gl.uniformMatrix4fv(location, transpose, array)

参数:
location uniform 变量的存储位置
transpose 在WebGL 中必须指定为false
array 待传输的类型化数组,4*4矩阵按列主序存储在其中

返回值 无

变换矩阵三维计算机图形学中应用得非常广泛,以至于着色器本身就实现了矩阵和适量相乘的功能。

// 顶点着色器
var VSHADER_SOURCE = 
    'attribute vec4 a_Position;\n' +
    'uniform mat4 u_xformMatrix;\n' +
    'void main() {\n' +
    '    gl_Position = u_xfromMatrix * a_Position;\n' +
    '}\n';
// 矩阵变换

// 创建一个 Matrix4 对象。
var xformMatrix = new Matrix4();

var Tx = 0.3, Ty = 0.3, Tz = 0.3; // 平移量
var ANGLE = 30.0; // 旋转度数

// 平移
xformMatrix.setTranslate(Tx, Ty, Tz, 0);
// 旋转
xformMatrix.rotate(ANGLE, 1, 1, 1);
// 缩放
xformMatrix.scale(0.7, 0.7, 1);

Matrix4

Matrix4是由<<WebGL编程指南>>作者写的提供WebGL的4*4矩阵操作的方法库,简化我们编写的代码。源代码共享地址,点击链接:Matrix4源代码

下面罗列了Matrix4库的所有方法:

1.setIdentity()

将Matrix4实例初始化为单位阵

2.setTranslate(x, y, z)

将Matrix4实例设置为平移变换矩阵,在x轴上平移的距离为x,在y轴上平移的距离为y,在z轴上平移的距离为z

3.setRotate(angle, x, y, z)

将Matrix4实例设置为旋转变换矩阵,旋转角度为angle,旋转轴为(x, y, z)。旋转轴(x,y,z)无需归一化

4.setScale(x, y, z)

将Matrix4实例设置为缩放变换矩阵,在三个轴上的缩放因子分别为x、y、z

5.translate(x, y, z)

将Matrix4实例生意一个平移变换矩阵(该平移矩阵在x轴上的平移距离为x,在y轴上的平移距离为y,在z轴上的平移距离为z),所得到的结果存储在Matrix4中

6.rotate(angle, x, y, z)

将Matrix4实例乘以一个旋转变换矩阵(该旋转矩阵旋转的角度为angle,旋转轴为(x、y、z)。旋转轴(x、y、z)无须归一化),所得的记过还存储在Matrix4中

7.scale(x, y, z)

将Matrix4实例乘以一个缩放变换矩阵(该缩放矩阵在三个轴上的缩放因子分别为x、y、z。),所得结果还存储在Matrix4中

8.set( m )

将Matrix4实例设置为m,m必须也是一个Matrix4实例

9.elements

类型化数组(Float32Array)包含了Matrix4实例的矩阵元素

说明:单位阵在矩阵乘法中的行为,就像数字1在乘法中的行为一样。将一个矩阵生意单位阵,得到的结果和原矩阵完全相同。在单位阵中,对角线的元素为1.0,其余的元素为0.0。

10.multiply(matrix)

两个矩阵相乘,返回的结果为一个新的matrix4对象,并且值为两个矩阵相乘的结果。例如:

var modelViewMatrix = viewMatrix.multiply(modelMatrix);

11.setOrtho(left, right, bottom, top, near, far)

通过各参数计算正射投影矩阵,将其存储在Matrix4中。注意,left不一定与right相等,bottom不一定与top相等,near与far不相等。参数:

left、right:指定近裁剪面的左边界和右边界

bottom、top:指定近裁剪面的上边界和下边界

near、far:指定近裁剪面和远裁剪面的位置,即可视空间的近边界和远边界

12.setPerspective(fov, aspect, near, far)

通过各参数计算透视投影矩阵,将其存储在Matrix4中。注意,near的值必须小于far。参数:

fov:指定垂直视角,即可视空间顶面和底面键的夹角,必须大于0

aspect:指定近裁剪面的高宽比(宽度/高度)

near、far:指定近裁剪面和远裁剪面的位置,即可视空间的近边界和远边界(near和far必须都大于0)

注意,第二个参数aspect是近裁剪面的宽高比,而不是水平视角(第一个参数是垂直视角)。比如说,如果近裁剪面的高度100和宽度200,那么宽高比为2。

13.setInverseOf (m)

使自身称为矩阵m的逆矩阵。

14.transpose()

对自身进行转置操作,并将自身设为转置后的结果。

原文链接

声明:UX|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - webGL 图像变换


心无旁骛似明镜