基本概念
先平移后旋转
先平移 ---》得到平移后的坐标(通过矩阵乘积)
再旋转 ---》旋转矩阵 * 平移后的坐标
变换矩阵------旋转
左边矩阵的列数要是右边矩阵的行数才可以相乘
绕着 Z 轴旋转

绕着 X 轴旋转

绕着 Y 轴旋转

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

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 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>
使用模型矩阵来实现复合变换
将 平移、旋转、缩放 的计算放到外部
传递到顶点着色器的矩阵应该是 (旋转 * 平移)的转置= 平移 的转置 * 旋转的转置
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>
平面内容复习
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_SHADER
gl.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.POINTS
gl.LINES LINES_STRIP LINES_LOOP
gl.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); 传递纹理数据, 传递纹理单元索引