缩放 Scale

△ABC是怎样放大到△A'B'C'的呢? 这两个三角形相似,只要把每个点都乘以一个缩放系数s就可以了。

x' = x * s
y' = y * s

把缩放系数设定为一个 uniform 变量,它分别与 attribute 变量的x、y坐标相乘,直接在顶点着色器中进行运算。顶点着色器里的 gl_Position 变量的 x、y、z、w 坐标都需单独写出来,其中 gl_Position.z 不变。这是在 xOy 平面上的缩放,如果对 z 坐标也进行相同的缩放,可以 gl_Position = a_Position * u_scale。

<script id="vertexShader" type="x-shader/x-vertex"> attribute vec4 a_Position; uniform float u_scale; void main(){ gl_Position.x = a_Position.x * u_scale; gl_Position.y = a_Position.y * u_scale; gl_Position.z = a_Position.z; gl_Position.w = 1.0; } </script> <script id="fragmentShader" type="x-shader/x-fragment"> void main(){ gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0); } </script> function init(){ var canvas = document.getElementById('canvas'), gl = canvas.getContext('webgl'); if(!gl){ console.log('Failed to get WebGL.'); return; } if(!initShaders(gl, "vertexShader", "fragmentShader")){ console.log('Failed to init Shaders.'); return; } // 获取 attribute, uniform 变量的存储地址 var a_Position = gl.getAttribLocation(gl.program, 'a_Position'), u_scale = gl.getUniformLocation(gl.program, 'u_scale'); if(a_Position < 0 || !u_scale){ console.log('Failed to get the location'); console.log(a_Position, u_scale); return; } var vertics = new Float32Array([0.0, 0.3, -0.3, -0.3, 0.3, -0.3]); var s = 2; // 创建缓冲区对象,对其写入数据 initVertexBuffer(gl, vertics, a_Position, 2); // 对 uniform 变量赋值 gl.uniform1f(u_scale, s); // 设置背景色, 清空颜色缓冲区 gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); // 画三角形 gl.drawArrays(gl.TRIANGLES, 0, 3); } function initVertexBuffer(gl, data, location, size){ var vertexBuffer = gl.createBuffer(); if(!vertexBuffer){ console.log('Failed to create the buffer object.'); return ; } gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.bufferData( gl.ARRAY_BUFFER, data, gl.STATIC_DRAW ); gl.vertexAttribPointer( location, size, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray( location ); }

缩放矩阵

下面分别是三角形在z轴、x轴、y轴上缩放的效果,以及对应的矩阵。但在 WebGL 里矩阵要按列主序重写。这时,GLSL ES 里的 Vertex Shader 中声明一个类型是 mat4 的 uniform 变量,它是一个 4×4 矩阵。在 main() 函数里进行矩阵运算 u_scaleMatrix * a_Position 再赋值给 gl_Position,不用对 gl_Position 变量的 x、y、z、w 坐标单独运算了。 而在 JavaScript 里对 uniform 传 mat4 的值就用到 gl.uniformMatrix4fv( location, false, array )。

<script id="vertexShader" type="x-shader/x-vertex"> attribute vec4 a_Position; attribute vec4 a_Color; uniform mat4 u_scaleMatrix; varying vec4 v_Color; void main(){ gl_Position = u_scaleMatrix * a_Position; v_Color = a_Color; } </script> <script id="fragmentShader" type="x-shader/x-fragment"> precision mediump float; varying vec4 v_Color; void main(){ gl_FragColor = v_Color; } </script> function init(){ ...... // 获取 attribute, uniform 变量的存储地址 var a_Position = gl.getAttribLocation(gl.program, 'a_Position'), a_Color = gl.getAttribLocation(gl.program, 'a_Color'), u_scaleMatrix = gl.getUniformLocation(gl.program, 'u_scaleMatrix'); if(a_Position < 0 || a_Color < 0 || !u_scaleMatrix){ console.log('Failed to get the location'); console.log(a_Position, a_Color, u_scaleMatrix); return; } var verticsColor = new Float32Array([ 0.0, 0.5, 1.0, 0.0, 0.0, 1.0, -0.5, -0.4, 0.0, 1.0, 0.0, 1.0, 0.5, -0.4, 0.0, 0.0, 1.0, 1.0]); var element_size = verticsColor.BYTES_PER_ELEMENT; var sx = 1.5, sy = 0.6, sz = 1; var scaleMatrix = new Float32Array([ sx, 0.0, 0.0, 0.0, 0.0, sy, 0.0, 0.0, 0.0, 0.0, sz, 0.0, 0.0, 0.0, 0.0, 1.0 ]); // 创建缓冲区对象,对其写入数据 initVertexBuffer(gl, verticsColor, a_Position, a_Color, element_size); // 对 uniform 变量赋值 gl.uniformMatrix4fv(u_scaleMatrix, false, scaleMatrix); // 设置背景色, 清空颜色缓冲区 gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); // 画三角形 gl.drawArrays(gl.TRIANGLES, 0, 3); } function initVertexBuffer(gl, data, a_Position, a_Color, size){ var vertexBuffer = gl.createBuffer(); if(!vertexBuffer){ console.log('Failed to create the buffer object.'); return ; } gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.bufferData( gl.ARRAY_BUFFER, data, gl.STATIC_DRAW ); // 位置 gl.vertexAttribPointer( a_Position, 2, gl.FLOAT, false, size * 6, 0); gl.enableVertexAttribArray( a_Position ); // 颜色 gl.vertexAttribPointer( a_Color, 4, gl.FLOAT, false, size * 6, size * 2 ); gl.enableVertexAttribArray( a_Color ); }

缩放动画

逻辑和原理跟 rotation 一致。

function init(){ ... ... // 设置背景色 gl.clearColor(0.0, 0.0, 0.0, 1.0); // 旋转动画 animate(); function animate(){ changeAngle( gl, u_rotateMatrix ); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.TRIANGLES, 0, 3); requestAnimationFrame( animate ); } } var sx = 0.3, sy = 0.3, sz = 1; var step = 0.01, flag; function changeScale( gl, u_scaleMatrix ){ if(!flag){ sx += step; sy += step; if(sx > 3) flag = true; } else if(flag){ sx -= step; sy -= step; if(sx < 0.3) flag = false; } var scaleMatrix = new Float32Array([ sx, 0.0, 0.0, 0.0, 0.0, sy, 0.0, 0.0, 0.0, 0.0, sz, 0.0, 0.0, 0.0, 0.0, 1.0 ]); // 对 uniform 变量赋值 gl.uniformMatrix4fv(u_scaleMatrix, false, scaleMatrix); } }