在上一篇中咱们已经实现了使用webgl绘制图形这个小目标《前端图形学从入门到放弃》001 画一个三角形
今天咱们来探讨一个新的话题矩阵
咱们都知道空间中的点咱们能够用向量表示,例如二维平面中的点(1,1)就表示第一象限的点:
而多个点就能组成图形,这也是上一篇文章中咱们说过的。
实际生产中这些图形每每并不会固定在画面中不懂,例如咱们能够对图形进行旋转,缩放,移动。
实际上这个过程就是将图形的顶点组进行了旋转,缩放,移动,成为了新的顶点组,再由新的顶点组绘制成新的图形。html
例如咱们要将由点A(0,0),B(1,0),C(0,1)组成的三角形放大一倍,那么咱们很容易知道放大后的点ÂḂĆ的坐标前端
Âx = Ax 2 = 02 = 0
Ây = Ay 2 = 02 = 0
Ḃx = Bx 2 = 12 = 2
Ḃy = By 2 = 02 = 0
Ćx = Cx 2 = 02 = 0
Ćy = Cy 2 = 12 = 2
数学家嫌这一番操做太过麻烦,而点又是能够写成向量形式的,要是能把操做简化成Â = M*A的形式就再好不过了,因而web
⎡ 2 0 ⎤ Â = ⎪ ⎪ * A ⎣ 0 2 ⎦
真是一顿操做猛如虎,一句不懂二百五canvas
举证表明了一种计算,如上咱们使用了一个二维矩阵segmentfault
⎡ A B ⎤ ⎣ C D ⎦
与一个二维向量相乘,会获得一个新的二维向量,计算公式以下函数
⎡ A B ⎤ ⎡x⎤ = ⎡ A*x + B*y ⎤ ⎣ C D ⎦ ⎣y⎦ ⎣ C*x + D*y ⎦
固然矩阵也不单单能够和向量相乘也能够和举证相乘,矩阵也不单单能够是22,也能够是33,更能够是n*m(n表明行数,m表明列数)。
两个矩阵能够相乘只须要,前一个矩阵的列数和后一个矩阵的函数相等便可。
例如nm的举证能够和ml的矩阵相乘,获得n*l的矩阵。
至于计算方法不是本文讨论的内容,推荐观看3blue1brown的视频。oop
而上文咱们看到的矩阵webgl
⎡ 2 0 ⎤ ⎣ 0 2 ⎦
就是一个把任意点放大两倍的矩阵,更通常的,若是能够写出缩放矩阵(n≠0)spa
⎡ n 0 ⎤ ⎡x⎤ = ⎡ n*x ⎤ ⎣ 0 n ⎦ ⎣y⎦ ⎣ n*y ⎦
相比于缩放还有一种操做也很高频,那就是旋转。前面没有提到,矩阵的变换是线性的。什么叫作线性?也是是说一样的操做(放大2倍)对A点产生的效果,和对B点产生的效果(放大2倍)是同样的。
因此对于旋转矩阵咱们也能够找到特殊的点进行求解,从而获得广泛适用的矩阵
对于x轴上的点a,旋转ø角后,能够用下图描述
咱们就获得了二维平面上的旋转矩阵code
⎡ cosø -sinø ⎤ ⎣ sinø cosø ⎦
下面咱们把矩阵和webgl结合起来,让《前端图形学从入门到放弃》001 画一个三角形中咱们实现的三角形能够旋转与缩放
首先咱们在页面上添加两个滑块分辨实现旋转与缩放
<canvas id="canvas" width="1000" height="1000"></canvas> <div id="control"> <input type="range" value=".75" min=".5" max="1" step="0.01" id="scale" value="0"> <input type="range" value="0" min="0" max="360" step="0.01" id="rotate" value="0"> </div>
因为旋转和缩放操做仅仅影响顶点位置,下面咱们之须要修改顶点着色器便可:
<script id="vertex-shader-2d" type="notjs"> attribute vec2 vertPosition; attribute vec3 vertColor; varying vec3 fragColor; // 额外申明两个矩阵用于旋转和缩放 uniform mat2 scaleMatrix; uniform mat2 rotateMatrix; void main() { fragColor = vertColor; // 把顶点坐标与矩阵相乘,获得旋转和缩放后的新顶点,传给gl gl_Position = vec4(scaleMatrix*rotateMatrix*vertPosition,0.0,1.0); } </script>
这两个申明的变量也要在js中取出
... var positionAttribLocation = gl.getAttribLocation(program, 'vertPosition'); var colorAttribLocation = gl.getAttribLocation(program, 'vertColor'); var scaleMatrix = gl.getUniformLocation(program, 'scaleMatrix'); var rotateMatrix = gl.getUniformLocation(program, 'rotateMatrix'); ...
因为咱们指望在滑动滑块时,页面实时变化,所以须要一个loop函数来完成这一切:
.... gl.useProgram(program); gl.drawArrays(gl.TRIANGLES, 0, 6); loop(gl, rotateMatrix, scaleMatrix); } ...
loop函数:
var scaleNode = document.querySelector("#scale"); var rotateNode = document.querySelector("#rotate"); function loop(gl, rotateMatrix, scaleMatrix) { var angle = rotateNode.value/180*Math.PI; var scale = scaleNode.value; var sin = Math.sin(angle);//旋转角度正弦值 var cos = Math.cos(angle);//旋转角度余弦值 var myArr = new Float32Array([cos, -sin, sin, cos,]); var scaleArr = new Float32Array([scale, 0, 0, scale,]); gl.uniformMatrix2fv(rotateMatrix, false, myArr); gl.uniformMatrix2fv(scaleMatrix, false, scaleArr); gl.clearColor(0.75, 0.85, 0.8, 1.0); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); requestAnimationFrame(function () { loop(gl, rotateMatrix, scaleMatrix); }); gl.drawArrays(gl.TRIANGLES, 0, 3); }
大功告成:
不知道各位看官有没有发现,在矩阵这套线性变化下,咱们没办法作平移操做。由于做为原点的o(0,0)不论乘以什么矩阵,结果都仍是本身。可是平移操做是平常工做中极其常见的操做,不能平移甚至没法实现拖拽!
难道图形学之路就此gg?
但天无绝人之路,只要零点不是零点我就能够移动它,对于二维平面,我能够把它看做三维世界中一个不过原点的平面,本来的(x,y)变为(x,y,1)
此时就能够实现平移
根据上文,咱们已经了解的矩阵知识,不难写出
而这种经过n+1维实现了n维线性变换外加移动操做的变换,就被称为齐次变换
。
下面咱们继续改造原有的webgl代码!
首先咱们还须要加入两个滑块分别控制,图形上下和左右运动
<div id="control"> ... <input type="range" value="0" min="-0.5" max="0.5" step="0.01" id="tranX"> <input type="range" value="0" min="-.5" max=".5" step="0.01" id="tranY"> </div>
因为齐次变换将全部的矩阵都升维了,咱们须要改造定点着色器。
<script id="vertex-shader-2d" type="notjs"> ... // 将本来二维矩阵定义为三维 uniform mat3 scaleMatrix; uniform mat3 rotateMatrix; uniform mat3 transformMatrix; void main() { fragColor = vertColor; vec3 v = rotateMatrix*scaleMatrix*transformMatrix*vec3(vertPosition,1.0); // 因为咱们之须要x,y把他们取出便可 gl_Position = vec4(v.xy,0.0,1.0); } </script>
因为矩阵从二维变为三维,取出的变量也须要从新定义为三维:
... var trMatrix = gl.getUniformLocation(program,'transformMatrix'); var scaleMatrix = gl.getUniformLocation(program, 'scaleMatrix'); var rotateMatrix = gl.getUniformLocation(program, 'rotateMatrix'); ... // 获取滑块 var tranXNode = document.querySelector("#tranX"); var tranYNode = document.querySelector("#tranY"); // 修改loop函数 ... loop(gl, rotateMatrix, scaleMatrix,trMatrix); } function loop(gl, rotateMatrix, scaleMatrix,trMatrix) { ... var myArr = new Float32Array([cos, -sin, 0 , sin, cos, 0,0,0,1]); var scaleArr = new Float32Array([scale, 0, 0,0, scale,0,0,0,1]); var tranArr = new Float32Array([1,0,0,0,1,0,tranXNode.value,tranYNode.value,1]); // console.log(tranXNode.value); gl.uniformMatrix3fv(rotateMatrix, false, myArr); gl.uniformMatrix3fv(scaleMatrix, false, scaleArr); gl.uniformMatrix3fv(trMatrix, false, tranArr); ....
大功告成:
我想二维的世界,你们也腻了,下篇咱们将进入三维世界,并说说光线是如何影响物体的