本文参考《WebGL编程指南》
仿射变换:移动,旋转,缩放
在做变换时,通常是使用矩阵。
首先画出一个初始的三角形作为对比:
源码:
test.js
function main() { var gl = Init(); if(!gl) { console.log('Failed to init'); return; } var n = InitVertices(gl); if(n < 0) { console.log('Failed to init vertices'); return; } // draw triangle gl.drawArrays(gl.TRIANGLES, 0, n); // draw coordinate system gl.drawArrays(gl.LINES, n, 4); } //@Func: init the WebGL //@Return: gl(the WebGL context) function Init() { var canvas = document.getElementById('webgl'); var gl = getWebGLContext(canvas); if(!gl) { console.log('Failed to get the rendering context for WebGL'); return null; } if(!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { console.log('Failed to init shader'); return null; } gl.clearColor(0.3, 1.0, 0.3, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); return gl; } //@Func: init vertices //@Return: n(the number of vertex) function InitVertices(gl) { var vertexBuffer = gl.createBuffer(); if(!vertexBuffer) { console.log('Failed to create vertex buffer'); return -1; } gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); var n = 3; var vertices = new Float32Array([ -0.3,0.0, 0.0,0.5, 0.3,0.0, -1.0,0.0, 1.0,0.0, 0.0,-1.0, 0.0,1.0 ]); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); var aPos = gl.getAttribLocation(gl.program, 'aPos'); if(aPos < 0) { console.log('Failed to get aPos'); return -1; } gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(aPos); return n; }
shader.js
// vertical shader var VSHADER_SOURCE = ` attribute vec4 aPos; void main() { gl_Position = aPos; gl_PointSize = 30.0; } ` // fragment shader var FSHADER_SOURCE = ` precision mediump float; //uniform vec4 uColor; void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } `
移动
平移矩阵如下:
(1)在着色器中做矩阵运算
// vertical shader var VSHADER_SOURCE = ` attribute vec4 aPos; uniform mat4 uTransform; void main() { gl_Position = uTransform * aPos; gl_PointSize = 30.0; } `
(2)赋值给矩阵
var transformMatrix = new Float32Array([ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.3, 0.0, 0.0, 1.0 ]); var uTransform = gl.getUniformLocation(gl.program, 'uTransform'); gl.uniformMatrix4fv(uTransform, false, transformMatrix);
注意:矩阵是列主元的,即上面定义的矩阵实际是如下:
效果:
源码:
test.js
function main() { var gl = Init(); if(!gl) { console.log('Failed to init'); return; } var n = InitVertices(gl); if(n < 0) { console.log('Failed to init vertices'); return; } var transformMatrix = new Float32Array([ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.3, 0.0, 0.0, 1.0 ]); var uAffineMat = gl.getUniformLocation(gl.program, 'uAffineMat'); gl.uniformMatrix4fv(uAffineMat, false, transformMatrix); // draw triangle gl.drawArrays(gl.TRIANGLES, 0, n); var normalMatrix = new Float32Array([ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]); gl.uniformMatrix4fv(uAffineMat, false, normalMatrix); // draw coordinate system gl.drawArrays(gl.LINES, n, 4); } //@Func: init the WebGL //@Return: gl(the WebGL context) function Init() { var canvas = document.getElementById('webgl'); var gl = getWebGLContext(canvas); if(!gl) { console.log('Failed to get the rendering context for WebGL'); return null; } if(!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { console.log('Failed to init shader'); return null; } gl.clearColor(0.3, 1.0, 0.3, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); return gl; } //@Func: init vertices //@Return: n(the number of vertex) function InitVertices(gl) { var vertexBuffer = gl.createBuffer(); if(!vertexBuffer) { console.log('Failed to create vertex buffer'); return -1; } gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); var n = 3; var vertices = new Float32Array([ -0.3,0.0, 0.0,0.5, 0.3,0.0, -1.0,0.0, 1.0,0.0, 0.0,-1.0, 0.0,1.0 ]); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); var aPos = gl.getAttribLocation(gl.program, 'aPos'); if(aPos < 0) { console.log('Failed to get aPos'); return -1; } gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(aPos); return n; }
shader.js
// vertical shader var VSHADER_SOURCE = ` attribute vec4 aPos; uniform mat4 uAffineMat; void main() { gl_Position = uAffineMat * aPos; gl_PointSize = 30.0; } ` // fragment shader var FSHADER_SOURCE = ` precision mediump float; //uniform vec4 uColor; void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } `
旋转
旋转矩阵如下:
效果:
源码:
test.js
function main() { var gl = Init(); if(!gl) { console.log('Failed to init'); return; } var n = InitVertices(gl); if(n < 0) { console.log('Failed to init vertices'); return; } var angle = 3.14 / 4; var transformMatrix = new Float32Array([ Math.cos(angle), Math.sin(angle), 0.0, 0.0, -Math.sin(angle), Math.cos(angle), 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]); var uAffineMat = gl.getUniformLocation(gl.program, 'uAffineMat'); gl.uniformMatrix4fv(uAffineMat, false, transformMatrix); // draw triangle gl.drawArrays(gl.TRIANGLES, 0, n); var normalMatrix = new Float32Array([ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]); gl.uniformMatrix4fv(uAffineMat, false, normalMatrix); // draw coordinate system gl.drawArrays(gl.LINES, n, 4); } //@Func: init the WebGL //@Return: gl(the WebGL context) function Init() { var canvas = document.getElementById('webgl'); var gl = getWebGLContext(canvas); if(!gl) { console.log('Failed to get the rendering context for WebGL'); return null; } if(!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { console.log('Failed to init shader'); return null; } gl.clearColor(0.3, 1.0, 0.3, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); return gl; } //@Func: init vertices //@Return: n(the number of vertex) function InitVertices(gl) { var vertexBuffer = gl.createBuffer(); if(!vertexBuffer) { console.log('Failed to create vertex buffer'); return -1; } gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); var n = 3; var vertices = new Float32Array([ -0.3,0.0, 0.0,0.5, 0.3,0.0, -1.0,0.0, 1.0,0.0, 0.0,-1.0, 0.0,1.0 ]); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); var aPos = gl.getAttribLocation(gl.program, 'aPos'); if(aPos < 0) { console.log('Failed to get aPos'); return -1; } gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(aPos); return n; }
shader.js
// vertical shader var VSHADER_SOURCE = ` attribute vec4 aPos; uniform mat4 uAffineMat; void main() { gl_Position = uAffineMat * aPos; gl_PointSize = 30.0; } ` // fragment shader var FSHADER_SOURCE = ` precision mediump float; //uniform vec4 uColor; void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } `
缩放
缩放矩阵如下:
效果:
源码:
test.js
function main() { var gl = Init(); if(!gl) { console.log('Failed to init'); return; } var n = InitVertices(gl); if(n < 0) { console.log('Failed to init vertices'); return; } var angle = 3.14 / 4; var transformMatrix = new Float32Array([ 2, 0, 0.0, 0.0, 0, 0.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]); var uAffineMat = gl.getUniformLocation(gl.program, 'uAffineMat'); gl.uniformMatrix4fv(uAffineMat, false, transformMatrix); // draw triangle gl.drawArrays(gl.TRIANGLES, 0, n); var normalMatrix = new Float32Array([ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]); gl.uniformMatrix4fv(uAffineMat, false, normalMatrix); // draw coordinate system gl.drawArrays(gl.LINES, n, 4); } //@Func: init the WebGL //@Return: gl(the WebGL context) function Init() { var canvas = document.getElementById('webgl'); var gl = getWebGLContext(canvas); if(!gl) { console.log('Failed to get the rendering context for WebGL'); return null; } if(!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { console.log('Failed to init shader'); return null; } gl.clearColor(0.3, 1.0, 0.3, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); return gl; } //@Func: init vertices //@Return: n(the number of vertex) function InitVertices(gl) { var vertexBuffer = gl.createBuffer(); if(!vertexBuffer) { console.log('Failed to create vertex buffer'); return -1; } gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); var n = 3; var vertices = new Float32Array([ -0.3,0.0, 0.0,0.5, 0.3,0.0, -1.0,0.0, 1.0,0.0, 0.0,-1.0, 0.0,1.0 ]); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); var aPos = gl.getAttribLocation(gl.program, 'aPos'); if(aPos < 0) { console.log('Failed to get aPos'); return -1; } gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(aPos); return n; }
shader.js
// vertical shader var VSHADER_SOURCE = ` attribute vec4 aPos; uniform mat4 uAffineMat; void main() { gl_Position = uAffineMat * aPos; gl_PointSize = 30.0; } ` // fragment shader var FSHADER_SOURCE = ` precision mediump float; //uniform vec4 uColor; void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } `
多个变换组合在一起
要将多个仿射变换组合在一起,只需要将对应的变换矩阵相乘。
利用cuon-matrix.js,其中定义了有用的矩阵运算:
var affineMat = new Matrix4(); affineMat.setTranslate(0.3, 0.0, 0.0); affineMat.scale(2, 0.5, 1); var uAffineMat = gl.getUniformLocation(gl.program, 'uAffineMat'); gl.uniformMatrix4fv(uAffineMat, false, affineMat.elements);
效果:
源码:
test.js
function main() { var gl = Init(); if(!gl) { console.log('Failed to init'); return; } var n = InitVertices(gl); if(n < 0) { console.log('Failed to init vertices'); return; } var affineMat = new Matrix4(); affineMat.setTranslate(0.3, 0.0, 0.0); affineMat.scale(2, 0.5, 1); var uAffineMat = gl.getUniformLocation(gl.program, 'uAffineMat'); gl.uniformMatrix4fv(uAffineMat, false, affineMat.elements); // draw triangle gl.drawArrays(gl.TRIANGLES, 0, n); var normalMatrix = new Float32Array([ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]); gl.uniformMatrix4fv(uAffineMat, false, normalMatrix); // draw coordinate system gl.drawArrays(gl.LINES, n, 4); } //@Func: init the WebGL //@Return: gl(the WebGL context) function Init() { var canvas = document.getElementById('webgl'); var gl = getWebGLContext(canvas); if(!gl) { console.log('Failed to get the rendering context for WebGL'); return null; } if(!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) { console.log('Failed to init shader'); return null; } gl.clearColor(0.3, 1.0, 0.3, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); return gl; } //@Func: init vertices //@Return: n(the number of vertex) function InitVertices(gl) { var vertexBuffer = gl.createBuffer(); if(!vertexBuffer) { console.log('Failed to create vertex buffer'); return -1; } gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); var n = 3; var vertices = new Float32Array([ -0.3,0.0, 0.0,0.5, 0.3,0.0, -1.0,0.0, 1.0,0.0, 0.0,-1.0, 0.0,1.0 ]); gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); var aPos = gl.getAttribLocation(gl.program, 'aPos'); if(aPos < 0) { console.log('Failed to get aPos'); return -1; } gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(aPos); return n; }
shader.js
// vertical shader var VSHADER_SOURCE = ` attribute vec4 aPos; uniform mat4 uAffineMat; void main() { gl_Position = uAffineMat * aPos; gl_PointSize = 30.0; } ` // fragment shader var FSHADER_SOURCE = ` precision mediump float; //uniform vec4 uColor; void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } `