9、webgl 基本概念 + 复合变换 + 平面内容复习
基本概念
先平移后旋转
先平移 —》得到平移后的坐标(通过矩阵乘积)
再旋转 —》旋转矩阵 * 平移后的坐标
变换矩阵——旋转
左边矩阵的列数要是右边矩阵的行数才可以相乘
绕着 Z 轴旋转

绕着 X 轴旋转

绕着 Y 轴旋转

复合变换
对四边形进行平移+旋转+缩放操作
需要将矩阵传入顶点着色器中,然后在顶点着色器中进行位置计算

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><canvas width="500" height="500" id="myCanvas"></canvas><script type="vertex" id="vertex">attribute vec4 a_position;attribute vec4 a_color;uniform mat4 a_Translate;uniform mat4 rotateMatrix;uniform mat4 scaleMatrix;varying vec4 v_color;void main() {// 先平移,再旋转,最后缩放gl_Position = a_Translate * rotateMatrix * scaleMatrix * a_position;gl_PointSize = 10.0;v_color = a_color;}</script><script type="fragment" id="fragment">precision mediump float;varying vec4 v_color;void main() {gl_FragColor = v_color;}</script><script>let myCanvas = document.getElementById("myCanvas");let gl = myCanvas.getContext("webgl");// IE8 之前的浏览器不兼容if(!gl) {alert("浏览器不支持 webgl!")}// 创建着色器function createShader(gl, type, source) {const shader = gl.createShader(type);gl.shaderSource(shader, source);gl.compileShader(shader);let isSuccess = gl.getShaderParameter(shader, gl.COMPILE_STATUS);return isSuccess ? shader : console.log(gl.getShaderInfoLog(shader))}// 获取着色器中的文本内容字符串function getInnerText(id) {return document.getElementById(id).innerText;}const vertexStr = getInnerText("vertex");const fragmentStr = getInnerText("fragment");const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexStr)const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentStr);// 创建程序function createProgram(gl, vertexShader, fragmentShader) {let program = gl.createProgram();gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);let isSuccess = gl.getProgramParameter(program, gl.LINK_STATUS);return isSuccess ? program : console.log(gl.getProgramInfoLog(program));}let program = createProgram(gl, vertexShader, fragmentShader);gl.useProgram(program);// 获取顶点着色器中变量let a_position = gl.getAttribLocation(program, 'a_position');let screenSize = gl.getUniformLocation(program, 'screenSize');let a_Translate = gl.getUniformLocation(program, 'a_Translate');let rotateMatrix = gl.getUniformLocation(program, 'rotateMatrix');let scaleMatrix = gl.getUniformLocation(program, 'scaleMatrix');let a_color = gl.getAttribLocation(program, 'a_color');let deg = 45;let Tx = 0.5, Ty = -0.5, Tz = 0;// 平移let Sx = 0.5, Sy = 0.5, Sz = 0; // 缩放gl.uniformMatrix4fv(a_Translate, false, new Float32Array([1, 0, 0, 0,0, 1, 0, 0,0, 0, 1, 0,Tx, Ty, Tz, 1]));gl.uniformMatrix4fv(scaleMatrix, false, new Float32Array([Sx, 0, 0, 0,0, Sy, 0, 0,0, 0, Sz, 0,0, 0, 0, 1]));let positionBuffer = gl.createBuffer();let indexBuffer = gl.createBuffer();gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); // 绑定索引缓冲区gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);function rotate() {deg += 5;let cosb = Math.cos(deg * Math.PI /180);let sinb = Math.sin(deg * Math.PI /180);// 四个分量的时候矩阵传值gl.uniformMatrix4fv(rotateMatrix, false, new Float32Array([cosb, sinb, 0, 0,-sinb, cosb, 0, 0,0, 0, 1, 0,0, 0, 0, 1]));draw()}rotate()setInterval(rotate, 30)function draw() {// 矩形的正面,要逆时针的顶点进行写, 矩形就是两个三角形,也就是需要写 6个 顶点, gl.ARRAY_BUFFER 这个缓冲区需要把所有的顶点都写上,但其实例如矩形的时候有些顶点是重复的,当面越来越多就会造成严重的性能问题,此时我们可以使用 gl.ELEMENT_ARRAY_BUFFER(也就是顶点索引)缓冲区来进行优化-只需要传不重复的点gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 0, 0, 1, 0, -0.5, 0.5, 0, 1, 0, 0, 0.5, 0.5, 0, 0, 1, 0, 0.5, -0.5, 0, 0, 0, 1, -0.5, -0.5, 0, 1, 1, 0,-0.5, 0.5, 1, 0, 0, 0]), gl.STATIC_DRAW);// gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([// 0, 1, 2,// 2, 3, 0// ]), gl.STATIC_DRAW);gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, 4 * 6, 0);gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, 4 * 6, 4* 3);// 启用这个位置数据gl.enableVertexAttribArray(a_position);gl.enableVertexAttribArray(a_color);// 绘制三角形 drawArrays 直接从数据的缓冲区取数据// gl.drawElements(mode, count, type, offset) // mode: 制定绘制图元的类型, gl.POINTS, gl.TRIANGLES, gl.LINES...// count: 指定绘制图形的顶点个数// type:指定索引缓冲区的值的类型,常用的值有两个 gl.UNSINGED_TYPE(无符号8位整数值) 和 gl.UNSINGED_SHORT(无符号短整型16位)// offset:指定索引数组中开始绘制的位置,以字节为单位// gl.drawElements(gl.TRIANGLES_FAN, 6, gl.UNSIGNED_SHORT, 0);gl.drawArrays(gl.TRIANGLE_FAN, 0, 6);}</script>
</body>
</html>
使用模型矩阵来实现复合变换
将 平移、旋转、缩放 的计算放到外部
传递到顶点着色器的矩阵应该是 (旋转 * 平移)的转置= 平移 的转置 * 旋转的转置
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><canvas width="500" height="500" id="myCanvas"></canvas><script type="vertex" id="vertex">attribute vec4 a_position;attribute vec4 a_color;uniform mat4 moduleMatrix;varying vec4 v_color;void main() {// 先平移,再旋转,最后缩放gl_Position = moduleMatrix * a_position;gl_PointSize = 10.0;v_color = a_color;}</script><script type="fragment" id="fragment">precision mediump float;varying vec4 v_color;void main() {gl_FragColor = v_color;}</script><script>let myCanvas = document.getElementById("myCanvas");let gl = myCanvas.getContext("webgl");// IE8 之前的浏览器不兼容if(!gl) {alert("浏览器不支持 webgl!")}// 创建着色器function createShader(gl, type, source) {const shader = gl.createShader(type);gl.shaderSource(shader, source);gl.compileShader(shader);let isSuccess = gl.getShaderParameter(shader, gl.COMPILE_STATUS);return isSuccess ? shader : console.log(gl.getShaderInfoLog(shader))}// 获取着色器中的文本内容字符串function getInnerText(id) {return document.getElementById(id).innerText;}const vertexStr = getInnerText("vertex");const fragmentStr = getInnerText("fragment");const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexStr)const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentStr);// 创建程序function createProgram(gl, vertexShader, fragmentShader) {let program = gl.createProgram();gl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);gl.linkProgram(program);let isSuccess = gl.getProgramParameter(program, gl.LINK_STATUS);return isSuccess ? program : console.log(gl.getProgramInfoLog(program));}let program = createProgram(gl, vertexShader, fragmentShader);gl.useProgram(program);// 获取顶点着色器中变量let a_position = gl.getAttribLocation(program, 'a_position');let screenSize = gl.getUniformLocation(program, 'screenSize');let moduleMatrix = gl.getUniformLocation(program, 'moduleMatrix');let a_color = gl.getAttribLocation(program, 'a_color');let deg = 35;let Tx = 0.5, Ty = -0.5, Tz = 0;// 平移let Sx = 0.5, Sy = 0.5, Sz = 0; // 缩放let matrix = {tranlate: (Tx, Ty, Tz) => {return [1, 0, 0, 0,0, 1, 0, 0,0, 0, 1, 0,Tx, Ty, Tz, 1]},rotateZ: (deg) => {let cosb = Math.cos(deg * Math.PI /180);let sinb = Math.sin(deg * Math.PI /180);return [cosb, sinb, 0, 0,-sinb, cosb, 0, 0,0, 0, 1, 0,0, 0, 0, 1]},mutiply: (a, b) => {let a00 = a[0 * 4 + 0]let a01 = a[0 * 4 + 1]let a02 = a[0 * 4 + 2]let a03 = a[0 * 4 + 3]let a10 = a[1 * 4 + 0]let a11 = a[1 * 4 + 1]let a12 = a[1 * 4 + 2]let a13 = a[1 * 4 + 3]let a20 = a[2 * 4 + 0]let a21 = a[2 * 4 + 1]let a22 = a[2 * 4 + 2]let a23 = a[2 * 4 + 3]let a30 = a[3 * 4 + 0]let a31 = a[3 * 4 + 1]let a32 = a[3 * 4 + 2]let a33 = a[3 * 4 + 3]let b00 = a[0 * 4 + 0]let b01 = a[0 * 4 + 1]let b02 = a[0 * 4 + 2]let b03 = a[0 * 4 + 3]let b10 = a[1 * 4 + 0]let b11 = a[1 * 4 + 1]let b12 = a[1 * 4 + 2]let b13 = a[1 * 4 + 3]let b20 = a[2 * 4 + 0]let b21 = a[2 * 4 + 1]let b22 = a[2 * 4 + 2]let b23 = a[2 * 4 + 3]let b30 = a[3 * 4 + 0]let b31 = a[3 * 4 + 1]let b32 = a[3 * 4 + 2]let b33 = a[3 * 4 + 3]return [a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30,a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31,a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32,a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33,a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30,a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31,a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32,a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33,a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30,a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31,a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32,a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33,a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30,a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31,a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32,a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33,]}}// 传入的是转置gl.uniformMatrix4fv(moduleMatrix, false, new Float32Array(matrix.mutiply(matrix.rotateZ(deg), matrix.tranlate(Tx, Ty, Tz))));let positionBuffer = gl.createBuffer();let indexBuffer = gl.createBuffer();gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); // 绑定索引缓冲区gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);draw()function draw() {// 矩形的正面,要逆时针的顶点进行写, 矩形就是两个三角形,也就是需要写 6个 顶点, gl.ARRAY_BUFFER 这个缓冲区需要把所有的顶点都写上,但其实例如矩形的时候有些顶点是重复的,当面越来越多就会造成严重的性能问题,此时我们可以使用 gl.ELEMENT_ARRAY_BUFFER(也就是顶点索引)缓冲区来进行优化-只需要传不重复的点gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 0, 0, 1, 0, -0.5, 0.5, 0, 1, 0, 0, 0.5, 0.5, 0, 0, 1, 0, 0.5, -0.5, 0, 0, 0, 1, -0.5, -0.5, 0, 1, 1, 0,-0.5, 0.5, 1, 0, 0, 0]), gl.STATIC_DRAW);// gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([// 0, 1, 2,// 2, 3, 0// ]), gl.STATIC_DRAW);gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, 4 * 6, 0);gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, 4 * 6, 4* 3);// 启用这个位置数据gl.enableVertexAttribArray(a_position);gl.enableVertexAttribArray(a_color);// 绘制三角形 drawArrays 直接从数据的缓冲区取数据// gl.drawElements(mode, count, type, offset) // mode: 制定绘制图元的类型, gl.POINTS, gl.TRIANGLES, gl.LINES...// count: 指定绘制图形的顶点个数// type:指定索引缓冲区的值的类型,常用的值有两个 gl.UNSINGED_TYPE(无符号8位整数值) 和 gl.UNSINGED_SHORT(无符号短整型16位)// offset:指定索引数组中开始绘制的位置,以字节为单位// gl.drawElements(gl.TRIANGLES_FAN, 6, gl.UNSIGNED_SHORT, 0);gl.drawArrays(gl.TRIANGLE_FAN, 0, 6);}</script>
</body>
</html>
平面内容复习
1、GLSL :着色器语言
(1)变量声明:1)存储变量类型(修饰符):attribute(属性修饰符)uniform(全局变量)所有顶点都用到这个varying (可变变量修饰符)---》顶点着色器和片元着色器共用的变量2)数据类型:vec2 ---> 两个分量的浮点型数据vec3 ---> 三个分量vec4 ---> 四个分量,第四位代表齐次坐标,前面的 数不能比最后一位大,会将前面的点除以最后一位3)precision 声明精准度highp:高精准度mediump:中精准度lowp: 低精准度
(2)顶点着色器中的内置变量gl_Position : 必须赋值,四个分量进行赋值gl_PointSize : 默认是1.0 ,点的大小(3)片元着色器中的内置变量gl_fragColor: 片元颜色(4)坐标转换通过 canvas 的坐标计算 webgl 的坐标
(5)坐标轴webg坐标系 原点在canvas 的中心点 水平方向(从左到右)-1 --- 1 竖直方向(从下到上) -1 ---- 1 垂直于页面向里的为 z 轴canvas坐标系 原点在 canvas的左上角 水平向右为 x 正轴 竖直向下为 y 轴正轴 垂直页面向上为 z轴纹理坐标系 原点在图片的左下角 水平向右为正轴 图片宽度为1 竖直向上为 y 轴 图片高度为1
(6)Webgl api着色器 shader:gl.createShader(type); 创建着色器对象, type 是着色器类型, type: gl.VERTEX_SHADER gl.FRAGMENT_SHADERgl.shaderSource(shader, source); 为着色器添加数据源 shader 是着色器对象, source 是数据源即我们写的着色器代码gl.compileShader(shader);编译着色器,将字符串编译成着色器语言gl.getShaderParameter(shader, gl.COMPILE_STATUS) 返回着色器编译的成功状态 gl.getShaderInfoLog(shader) 打印着色器编译失败的日志创建程序 program:gl.createProgram(); 创建程序gl.attachShader(program, shader); 添加着色器 program 程序,shader要往程序里面添加着色器对象gl.linkProgram(program)连接着色器gl.useProgram(program); 使用程序attribute 属性:gl.getAttribLocation(program, 'a_position') 获取attribute 变量位置gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, 4 * 6, 0); 为变量添加数据uniform 属性:gl.getUniformLocation(program, 'screenSize');gl.uniformMatrix4fv(moduleMatrix, false, new Float32Array(matrix.mutiply(matrix缓冲区对象:let positionBuffer = gl.createBuffer(); 创建缓冲区对象gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); // 绑定索引缓冲区gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 0, 0, 1, 0, -0.5, 0.5, 0, 1, 0, 0, 0.5, 0.5, 0, 0, 1, 0, 0.5, -0.5, 0, 0, 0, 1, -0.5, -0.5, 0, 1, 1, 0,-0.5, 0.5, 1, 0, 0, 0]), gl.STATIC_DRAW);gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, 4 * 6, 0);gl.enableVertexAttribArray(a_position); 启用这个位置数据绘制:gl.drawElements(gl.TRIANGLES_FAN, 6, gl.UNSIGNED_SHORT, 0);gl.drawArrays(gl.TRIANGLE_FAN, 0, 6);图元:gl.POINTSgl.LINES LINES_STRIP LINES_LOOPgl.TRANGLES TRANGLE_FAN TRANGLE_STRIP纹理:gl.createTexture(); 创建纹理对象gl.pixelStorei(gl.UNPACK_FILP_Y_WEBGL, 1)转换坐标系gl.activeTexture(gl.TEXTURE0);激活纹理单元gl.bindTexture(gl.TEXTURE_2D, texture); 绑定纹理对象gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) 设置纹理参数gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); 配置纹理图像gl.uniform1i(location, 0); 传递纹理数据, 传递纹理单元索引
