webgl 介绍
- webgl 是在传统的html文件的系统上,添加了 GLSLes(主要是编写
着色器
的配置) - webgl通过
js
赋值给着色器数据,着色器
主要是编写绘制图形需要的配置项
webgl 代码的主要部分和一些需要知道的概念介绍
学习新语言,需要了解语法和一些概念,下面是我总结的一些东西
着色器
- 着色器是webgl 中的绘制图形的主要部分,每个着色器都是由
顶点着色器
片元着色器
组成 -
顶点着色器
主要负责设置元素的位置信息,尺寸大小(还可以在着色器中添加一些逻辑判断,本质上着色器语法为 opengl 语法) -
片元着色器
用于设置物体范围内的颜色插值(以设置片元的颜色为标准,设置像素的颜色值)(还可以在着色器中添加一些逻辑判断,本质上着色器语法为 opengl 语法)
// 最简单的代码片段参考
// gl 中 定义变量的方法 attribute 和 uniform
// attribute 定义的是和 顶点 着色器 相关的变量
// uniform 定义的是 对于所有顶点 都相同的数据
// attribute(存储限定符) vec4(类型) a_Position(变量)
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' + // attribute 定义一个 数据类型为 vec4 变量 名称为 a_Position
'void main() {\n' +
' gl_Position = a_Position;\n' + // 设置 位置坐标
' gl_PointSize = 10.0;\n' + // 设置尺寸大小
'}\n';
// Fragment shader program
var FSHADER_SOURCE =
'void main() {\n' +
' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 设置片元着色器 填充颜色
'}\n';
缓冲区对象
个人认为缓冲区对象的主要作用: 一次性提取数据,提高数据的处理速度
- 创建缓冲区对象的具体步骤
1 创建缓冲区对象
2 将缓冲区分配给对应需要的缓冲区类型(有多个缓冲区类型)
3 给缓冲区对象中写入需要的数据
4 把缓冲区和着色器中的变量绑定(可以理解为赋值)
5 开启缓冲区的访问权限
- 代码逻辑
// 将多个顶点的数据 保存在缓存区对象中
// 缓冲区传给 着色器
function initVertexBuffers(gl) {
var vertices = new Float32Array([
0.0, 0.5, -0.5, -0.5, 0.5, -0.5
]); // 定义数据
var n = 3; // 点的个数
// 1 创建缓冲区对象
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create the buffer object');
return -1;
}
// 2 将缓冲区对象绑定到目标
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 3 向缓冲区对象写入数据
// 参数3 的作用是 表示程序将如何使用存储在缓冲区对象的数据 (做优化操作)
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 获取变量
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
/**
* 4 把缓冲区和着色器中的变量绑定(可以理解为赋值)
* 将缓冲区对象 分配给(赋值给) a_Position 变量
* 参数含义
* 参数1 a_Position 赋值的变量
* 参数2 指定缓冲区中每个顶点的分量个数
* 参数3 指定数据类型 gl.FLOAT 是其中一种类型
* 参数4 表明是否将非浮点型数据 归一化成 [0,1] 或 [-1,1]
* 参数5 指定相邻的两个顶点间的字节数
* 参数6 指定从第几个数据位置开始(偏移数)
*/
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 5 开启访问权限
//开启a_Position,为了使着色器能够访问 a_Position 变量
// 连接 a_Position 变量 与分配给他的缓冲区对象
gl.enableVertexAttribArray(a_Position);
return n;
}
纹理映射
纹理贴图的原理就是把把图片中对应的像素值(gaba) 映射到三维物体的表面
- 创建纹理对象的具体步骤
1 创建纹理对象
2 开启纹理对象
3 对纹理单元绑定 纹理对象
4 设置纹理参数
5 配置纹理图像 --> 将纹理图像分配给纹理对象
6 将纹理单元赋值给着色器中的变量
7 着色器变量 调用 texture2D 取出片元对应的颜色值
- 代码具体实现
// 1 创建一个纹理对象
var texture = gl.createTexture();
// 2 开启 0号纹理单元
gl.activeTexture(gl.TEXTURE0);
// 3 向 EXTURE_2D 绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D, texture);
// 4配置纹理参数 (设置样式)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// 5 配置纹理图像 --> 将纹理图像分配给纹理对象
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
// 6 将 0 号纹理传递给着色器
gl.uniform1i(u_Sampler, 0);
// 创建片元着色器
var FSHADER_SOURCE =
// '#ifdef GL_ES\n' +
'precision mediump float;\n' +
// '#endif\n' +
'uniform sampler2D u_Sampler;\n' + // 6 定义取样器 用来接收 纹理图
'varying vec2 v_TexCoord;\n' +
'void main() {\n' +
' gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' + // 6 提取纹理颜色值栅格化片元着色器
'}\n';
矩阵对象
可以看做一系列变化关系的集合的统一表达方式(用于设置图形变换旋转、缩放,设置视图顶点坐标)
- 用于表示 顶点位置、颜色数据、物体变化数据、视图投影矩阵等
- 多个矩阵可以合并
光线
用于模拟现实场景下的光照因素
- 光线分类
1 平行光源(太阳)
2 点光源(灯泡)
3 环境光(物体周围均匀分布的光线)
-
反射类型(光照下的物体表面颜色)
1 漫反射计算: 漫反射 = 入射光线颜色*物体表面颜色*cos(a)
1 环境反射: 环境反射 = 入射光线颜色*物体表面颜色
- 代码参考
// 顶点着色器
var VSHADER_SOURCE =
'attribute vec4 a_Color;\n' + // 定义顶点的 颜色 变量
'attribute vec4 a_Normal;\n' + // 定义法向量的变量、
'uniform vec3 u_LightColor;\n' + // 定义漫反射的光照颜色 变量
'uniform vec3 u_LightDirection;\n' + // 定义漫反射的光照方向 变量
'uniform vec3 u_AmbientLight;\n' + // 定义环境光的变量
'varying vec4 v_Color;\n' + // 要传给片元着色器的变量
'void main() {\n' +
// 计算 cos 值 = 漫反射的方向向量 * 法向量
' float nDotL = max(dot(u_LightDirection, a_Normal), 0.0);\n' +
// 计算漫反射后的物体颜色
' vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;\n' +
// 计算环境光的物体颜色
' vec3 ambient = u_AmbientLight * a_Color.rgb;\n' +
// 计算最终的物体颜色
' v_Color = vec4(diffuse + ambient, a_Color.a);\n' +
'}\n';
// 片元着色器
var FSHADER_SOURCE =
'#ifdef GL_ES\n' +
'precision mediump float;\n' + // 设置数据精度
'#endif\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_FragColor = v_Color;\n' +
'}\n';
视图矩阵
由 观察者位置, 目标位置, 观察者上方向位置组成的 视图矩阵
- 代码
// 包括 观察者(x y z)坐标,目标点(x y z)坐标,观察者上方向(x y z)坐标
viewMatrix.setLookAt(0.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0); // 设置视图矩阵的api
可视空间矩阵
观察者可以看到区域范围,下面两个选择设置一个即可
-
盒状可视空间
// 参数依次为 近裁剪面左边界、近裁剪面右边界、近裁剪面上边界、近裁剪面下边界、近裁剪面位置、远裁剪面位置
projMatrix.setOrtho(-1.0, 1.0, -1.0, 1.0, g_near, g_far); // 设置盒装可视空间
-
四棱锥/金字塔 可视空间(又叫透视投影矩阵)
// 参数依次是 可视空间的夹角(观察者顶面和底面的夹角),近裁剪面的宽高比,近裁剪面位置,远裁剪面的位置
viewProjectionMatrix.setPerspective(30.0, canvas.width / canvas.height, 1.0, 100.0);// 设置透视投影矩阵(更真实,近处大 远处小)
隐藏面消除功能
gl.enable(gl.DEPTH_TEST);//开启隐藏面消除功能
gl.clear( gl.DEPTH_BUFFER_BIT); // 清除深度缓冲区
立体图形绘制
webgl 没有提供绘制三维图形的方法,只能通过二维绘制的api来实现
- 方法一 通过绘制多个三角形来拼成3d图形
立方体6个面,每个面需要两个三角形来组成,所以需要的定点数 =6(6个面) * 3(每个三角形需要3的顶点) * 2(每个面需要两个三角形)
通过拼接三角形需要36个顶点 - 方法二 通过 gl.drawElements() 方式索引顶点坐标来绘制立方体
调用 gl.drawElements() 方法前需要做以下准备
1创建 顶点坐标 并存放到 缓冲区中
2 创建 顶点坐标对应的 索引坐标(通过索引坐标来获取顶点坐标来绘制3d图形)
3 创建缓冲区 并绑定到gl.ELEMENT_ARRAY_BUFFER
缓冲区类型上
4 将 索引坐标 绑定到gl.ELEMENT_ARRAY_BUFFER
缓冲区 上
5 调用 gl.drawElements() 绘制3d图形
下面是简单的代码逻辑
// 1创建 顶点坐标 并存放到 缓冲区中
// 将缓冲区对象 绑定到 gl.ARRAY_BUFFER
// Create a cube
// v6----- v5
// /| /|
// v1------v0|
// | | | |
// | |v7---|-|v4
// |/ |/
// v2------v3
var verticesColors = new Float32Array([
// 8个顶点位置 和 颜色值
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // v0 White
-1.0, 1.0, 1.0, 1.0, 0.0, 1.0, // v1 Magenta
-1.0, -1.0, 1.0, 1.0, 0.0, 0.0, // v2 Red
1.0, -1.0, 1.0, 1.0, 1.0, 0.0, // v3 Yellow
1.0, -1.0, -1.0, 0.0, 1.0, 0.0, // v4 Green
1.0, 1.0, -1.0, 0.0, 1.0, 1.0, // v5 Cyan
-1.0, 1.0, -1.0, 0.0, 0.0, 1.0, // v6 Blue
-1.0, -1.0, -1.0, 0.0, 0.0, 0.0 // v7 Black
]);
var vertexColorBuffer = gl.createBuffer();
var indexBuffer = gl.createBuffer();
if (!vertexColorBuffer || !indexBuffer) {
return -1;
}
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
var FSIZE = verticesColors.BYTES_PER_ELEMENT;
var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); // 获取 变量
if(a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);
gl.enableVertexAttribArray(a_Position);
var a_Color = gl.getAttribLocation(gl.program, 'a_Color'); // 获取 变量
if(a_Color < 0) {
console.log('Failed to get the storage location of a_Color');
return -1;
}
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
gl.enableVertexAttribArray(a_Color);
// 2 创建 顶点坐标对应的 索引坐标(通过索引坐标来获取顶点坐标来绘制3d图形)
// 这个就是索引坐标
var indices = new Uint8Array([
0, 1, 2, 0, 2, 3, // front
0, 3, 4, 0, 4, 5, // right
0, 5, 6, 0, 6, 1, // up
1, 6, 7, 1, 7, 2, // left
7, 4, 3, 7, 3, 2, // down
4, 7, 6, 4, 6, 5 // back
]);
// 3 创建缓冲区 并绑定到 `gl.ELEMENT_ARRAY_BUFFER` 缓冲区类型上
// 将缓冲区对象 绑定到 gl.ELEMENT_ARRAY_BUFFER(gl.ELEMENT_ARRAY_BUFFER 管理具有索引结构的三维数据模型)
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
// 4 将 索引坐标 绑定到 `gl.ELEMENT_ARRAY_BUFFER` 缓冲区 上
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
// 5 绘制 立方体
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_BYTE, 0);
通过 gl.drawElements
方法绘制 需要的是 8个顶点,相比第一种减少了数据的创建
雾化
基本开发流程
- 创建着色器对象
- 创建canvas元素,并获取canvas 的 dom 元素
- 通过canvas 获取 webgl 的绘图上下文对象
- 初始化着色器,包括设置着色器的 定点位置,颜色值,视图投影矩阵,向量矩阵,纹理对象,光线对象值等的设置(是webgl中的最重要的开发部分)
- 设置canvas背景色
- 清除canvas 中的颜色缓冲区和深度缓冲区
- webgl 绘图(包含许多绘图api)
代码案例
- 案例1 通过js控制绘制元素的颜色,代码大致实现原理
主要注意的是 可以通过 js 来动态赋值着色器中的变量
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Hello Point (2)</title>
</head>
<body onload="main()">
<canvas id="webgl" width="400" height="400">
Please use a browser that supports "canvas"
</canvas>
<script src="../../lib/webgl-utils.js"></script>
<script src="../../lib/webgl-debug.js"></script>
<script src="../../lib/cuon-utils.js"></script>
<script>
// 顶点着色器
// gl 中 定义变量的方法 attribute 和 uniform
// attribute 定义的是和 顶点 着色器 相关的变量
// uniform 定义的是 对于所有顶点 都相同的数据
// attribute(存储限定符) vec4(类型) a_Position(变量)
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' + // attribute 定义一个 数据类型为 vec4 变量 名称为 a_Position
'void main() {\n' +
' gl_Position = a_Position;\n' + // 设置 位置坐标
' gl_PointSize = 10.0;\n' + // 设置尺寸大小
'}\n';
// 片元着色器
var FSHADER_SOURCE =
'void main() {\n' +
' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 设置片元着色器 填充颜色
'}\n';
function main() {
// 获取dom元素
var canvas = document.getElementById('webgl');
// 获取gl上下文对象
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 初始化着色器 返回程序对象
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// 拿到 gl 中定义的坐标 a_Position
// 获取 attribute 存储变量的位置
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return;
}
// a_Position 赋值
// 将 位置数据 传给 attribute 变量
gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);
// 设置背景色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 清空颜色缓冲区
gl.clear(gl.COLOR_BUFFER_BIT);
// 画图
gl.drawArrays(gl.POINTS, 0, 1);
}
</script>
</body>
</html>
- 案例2 通过缓冲区对象来设置顶点位置 颜色信息
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Draw Multiple Points</title>
</head>
<body onload="main()">
<canvas id="webgl" width="400" height="400">
Please use a browser that supports "canvas"
</canvas>
<script src="../../lib/webgl-utils.js"></script>
<script src="../../lib/webgl-debug.js"></script>
<script src="../../lib/cuon-utils.js"></script>
<script>
// Vertex shader program
// 缓冲区对象 一次性的向着色器传多个顶点数据
/**
* 创建缓存区对象 步骤
* 1 创建缓存区对象 gl.createBuffer()
* 2 绑定缓冲区对象 gl.bindBuffer()
* 3 将数据写入缓冲区对象 gl.bufferData()
* 4 将缓冲区对象分配给一个attribute变量 gl.vertexAttribPointer()
* 5 开启attribute变量 gl.enableVertexAttribArray()
* */
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' +
' gl_PointSize = 10.0;\n' +
'}\n';
// Fragment shader program
var FSHADER_SOURCE =
'void main() {\n' +
' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
'}\n';
function main() {
// Retrieve <canvas> element
var canvas = document.getElementById('webgl');
// Get the rendering context for WebGL
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// 设置 顶点位置
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the positions of the vertices');
return;
}
// 设置背景色
gl.clearColor(0, 0, 0, 1);
// 清空 颜色缓冲区
gl.clear(gl.COLOR_BUFFER_BIT);
// 画个点
// 参数1: gl.POINTS 代表绘制点 还有其他的绘制方法(后面说)
// 参数2: 0 表示从起始位置开始
// 参数3: 1 绘制几个
gl.drawArrays(gl.POINTS, 0, n);
}
// 创建顶点 缓冲区对象
// 将多个顶点的数据 保存在缓存区对象中
// 缓冲区传给 着色器
function initVertexBuffers(gl) {
var vertices = new Float32Array([
0.0, 0.5, -0.5, -0.5, 0.5, -0.5
]);
var n = 3; // 点的个数
// 创建缓冲区对象
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create the buffer object');
return -1;
}
// 将缓冲区对象绑定到目标
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向缓冲区对象写入数据
// 参数3 的作用是 表示程序将如何使用存储在缓冲区对象的数据 (做优化操作)
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 获取变量
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
/**
* 将缓冲区对象 分配给(赋值给) a_Position 变量
* 参数含义
* 参数1 a_Position 赋值的变量
* 参数2 指定缓冲区中每个顶点的分量个数
* 参数3 指定数据类型 gl.FLOAT 是其中一种类型
* 参数4 表明是否将非浮点型数据 归一化成 [0,1] 或 [-1,1]
* 参数5 指定相邻的两个顶点间的字节数
* 参数6 指定从第几个数据位置开始(偏移数)
*/
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 开启a_Position,为了使着色器能够访问 a_Position 变量
// 连接 a_Position 变量 与分配给他的缓冲区对象
gl.enableVertexAttribArray(a_Position);
return n;
}
</script>
</body>
</html>
- 案例3 模型矩阵 视图矩阵 投影矩阵开启深度缓冲区
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Perspective Projection</title>
</head>
<body onload="main()">
<canvas id="webgl" width="400" height="400">
Please use a browser that supports "canvas"
</canvas>
<script src="../../lib/webgl-utils.js"></script>
<script src="../../lib/webgl-debug.js"></script>
<script src="../../lib/cuon-utils.js"></script>
<script src="../../lib/cuon-matrix.js"></script>
<script>
// 顶点着色器
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' +
'uniform mat4 u_ViewMatrix;\n' +
'uniform mat4 u_ProjMatrix;\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_Position = u_ProjMatrix * u_ViewMatrix * a_Position;\n' +
' v_Color = a_Color;\n' +
'}\n';
// 片元着色器
var FSHADER_SOURCE =
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_FragColor = v_Color;\n' +
'}\n';
function main() {
// 获取dom 元素
var canvas = document.getElementById('webgl');
// 获取 gl 上线文
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// 初始化 着色器 函数调用
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the vertex information');
return;
}
// 设置背景颜色
gl.clearColor(0, 0, 0, 1);
// 获取矩阵变量
var u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');
var u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
if (!u_ViewMatrix || !u_ProjMatrix) {
console.log('Failed to get the storage location of u_ViewMatrix and/or u_ProjMatrix');
return;
}
var viewMatrix = new Matrix4(); // 创建 矩阵对象
var projMatrix = new Matrix4(); // 创建 矩阵对象
// 计算视图矩阵
viewMatrix.setLookAt(0, 0, 5, 0, 0, -100, 0, 1, 0);
projMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100); // 设置 投影矩阵 数据
// 赋值
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
// 清空颜色缓冲区
gl.clear(gl.COLOR_BUFFER_BIT);
// 画图
gl.drawArrays(gl.TRIANGLES, 0, n);
}
function initVertexBuffers(gl) {
var verticesColors = new Float32Array([
// 位置 颜色数据
0.75, 1.0, -4.0, 0.4, 1.0, 0.4, // 后面的
0.25, -1.0, -4.0, 0.4, 1.0, 0.4,
1.25, -1.0, -4.0, 1.0, 0.4, 0.4,
0.75, 1.0, -2.0, 1.0, 1.0, 0.4, // 中间的
0.25, -1.0, -2.0, 1.0, 1.0, 0.4,
1.25, -1.0, -2.0, 1.0, 0.4, 0.4,
0.75, 1.0, 0.0, 0.4, 0.4, 1.0, // 前面的
0.25, -1.0, 0.0, 0.4, 0.4, 1.0,
1.25, -1.0, 0.0, 1.0, 0.4, 0.4,
// Three triangles on the left side
-0.75, 1.0, -4.0, 0.4, 1.0, 0.4, // 后面的
-1.25, -1.0, -4.0, 0.4, 1.0, 0.4,
-0.25, -1.0, -4.0, 1.0, 0.4, 0.4,
-0.75, 1.0, -2.0, 1.0, 1.0, 0.4, // 中间的
-1.25, -1.0, -2.0, 1.0, 1.0, 0.4,
-0.25, -1.0, -2.0, 1.0, 0.4, 0.4,
-0.75, 1.0, 0.0, 0.4, 0.4, 1.0, // 前面的
-1.25, -1.0, 0.0, 0.4, 0.4, 1.0,
-0.25, -1.0, 0.0, 1.0, 0.4, 0.4,
]);
var n = 18; // Three vertices per triangle * 6
// 创建 缓冲区对象
var vertexColorbuffer = gl.createBuffer();
if (!vertexColorbuffer) {
console.log('Failed to create the buffer object');
return -1;
}
// 将缓冲区对象 绑定到 gl.ARRAY_BUFFER
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorbuffer);
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
var FSIZE = verticesColors.BYTES_PER_ELEMENT;
// 获取 变量
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if(a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);
gl.enableVertexAttribArray(a_Position);
// 获取 变量
var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
if(a_Color < 0) {
console.log('Failed to get the storage location of a_Color');
return -1;
}
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
gl.enableVertexAttribArray(a_Color);
return n;
}
</script>
</body>
</html>
- 案例4 添加纹理贴图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Draw quad with texture</title>
</head>
<body onload="main()">
<canvas id="webgl" width="400" height="400">
Please use a browser that supports "canvas"
</canvas>
<script src="../../lib/webgl-utils.js"></script>
<script src="../../lib/webgl-debug.js"></script>
<script src="../../lib/cuon-utils.js"></script>
<script src="../../lib/cuon-matrix.js"></script>
<script>
// TexturedQuad.js (c) 2012 matsuda and kanda
// 创建顶点着色器
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec2 a_TexCoord;\n' +
'varying vec2 v_TexCoord;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' +
' v_TexCoord = a_TexCoord;\n' +
'}\n';
// 创建片元着色器
var FSHADER_SOURCE =
// '#ifdef GL_ES\n' +
'precision mediump float;\n' +
// '#endif\n' +
'uniform sampler2D u_Sampler;\n' + // 定义取样器 用来接收 纹理图
'varying vec2 v_TexCoord;\n' +
'void main() {\n' +
' gl_FragColor = texture2D(u_Sampler, v_TexCoord);\n' +
'}\n';
function main() {
// 获取dom 元素
var canvas = document.getElementById('webgl');
// 获取 gl 上下文对象
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 初始化对象
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// 函数调用
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the vertex information');
return;
}
// 设置颜色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 设置纹理
if (!initTextures(gl, n)) {
console.log('Failed to intialize the texture.');
return;
}
}
function initVertexBuffers(gl) { // 将纹理坐标和顶点坐标对应上
var verticesTexCoords = new Float32Array([
// 顶点坐标, 纹理坐标
-0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, 0.0, 0.0,
0.5, 0.5, 1.0, 1.0,
0.5, -0.5, 1.0, 0.0,
]);
var n = 4; // The number of vertices
// 创建 缓冲区对象
var vertexTexCoordBuffer = gl.createBuffer();
if (!vertexTexCoordBuffer) {
console.log('Failed to create the buffer object');
return -1;
}
// 将缓冲区对象 分配给 gl.ARRAY_BUFFER
gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);
var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;// 每个数据所占的字节
//获取着色器 对象 变量
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
gl.enableVertexAttribArray(a_Position); // 开启缓冲区对象访问权限 使 着色器能访问到变量的值
// 获取着色器 对象 变量
var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
if (a_TexCoord < 0) {
console.log('Failed to get the storage location of a_TexCoord');
return -1;
}
// 赋值
gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
gl.enableVertexAttribArray(a_TexCoord); // 开启缓冲区对象访问权限 使 着色器能访问到变量的值
return n;
}
function initTextures(gl, n) { // 配置和加载纹理
var texture = gl.createTexture(); // 创建一个纹理对象
if (!texture) {
console.log('Failed to create the texture object');
return false;
}
// 获取着色器 对象 变量(取样器)
var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
if (!u_Sampler) {
console.log('Failed to get the storage location of u_Sampler');
return false;
}
var image = new Image(); // 创建 图片对象
if (!image) {
console.log('Failed to create the image object');
return false;
}
// 注册图像加载事件响应函数
image.onload = function(){ loadTexture(gl, n, texture, u_Sampler, image); };
// 图片路径(赋值后 浏览器开始加载)
image.src = '../../resources/sky.JPG';
return true;
}
function loadTexture(gl, n, texture, u_Sampler, image) { // 对纹理开始处理
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // 对纹理图像进行y轴反转
// 开启 0号纹理单元
gl.activeTexture(gl.TEXTURE0);
// 向 EXTURE_2D 绑定纹理对象
gl.bindTexture(gl.TEXTURE_2D, texture);
// 配置纹理参数 (设置样式)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// 配置纹理图像 --> 将纹理图像分配给纹理对象
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
// 将 0 号纹理传递给着色器
gl.uniform1i(u_Sampler, 0);
gl.clear(gl.COLOR_BUFFER_BIT); // 清空颜色缓冲区
gl.drawArrays(gl.TRIANGLE_STRIP, 0, n); // 绘制矩形
}
</script>
</body>
</html>
- 案例5 立方体绘制
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Hello cube</title>
</head>
<body onload="main()">
<canvas id="webgl" width="400" height="400">
Please use a browser that supports "canvas"
</canvas>
<script src="../../lib/webgl-utils.js"></script>
<script src="../../lib/webgl-debug.js"></script>
<script src="../../lib/cuon-utils.js"></script>
<script src="../../lib/cuon-matrix.js"></script>
<script>
// 顶点着色器
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'attribute vec4 a_Color;\n' +
'uniform mat4 u_MvpMatrix;\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_Position = u_MvpMatrix * a_Position;\n' +
' v_Color = a_Color;\n' +
'}\n';
// 片元着色器
var FSHADER_SOURCE =
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_FragColor = v_Color;\n' +
'}\n';
function main() {
// 获取dom 元素
var canvas = document.getElementById('webgl');
// 获取 gl 上线文
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// 初始化 着色器 函数调用
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the vertex information');
return;
}
// 设置背景颜色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST); // 开启隐藏面消除功能
// 获取着色器 变量
var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
if (!u_MvpMatrix) {
console.log('Failed to get the storage location of u_MvpMatrix');
return;
}
// 创建矩阵对象
var mvpMatrix = new Matrix4();
mvpMatrix.setPerspective(30, 1, 1, 100); // 设置 投影矩阵
mvpMatrix.lookAt(3, 3, 7, 0, 0, 0, 0, 1, 0);// 视图模型矩阵
// 赋值
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
// 清空颜色缓冲区 和 深度缓冲区
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 绘制 立方体
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}
function initVertexBuffers(gl) {
// Create a cube
// v6----- v5
// /| /|
// v1------v0|
// | | | |
// | |v7---|-|v4
// |/ |/
// v2------v3
var verticesColors = new Float32Array([
// 8个顶点位置 和 颜色值
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // v0 White
-1.0, 1.0, 1.0, 1.0, 0.0, 1.0, // v1 Magenta
-1.0, -1.0, 1.0, 1.0, 0.0, 0.0, // v2 Red
1.0, -1.0, 1.0, 1.0, 1.0, 0.0, // v3 Yellow
1.0, -1.0, -1.0, 0.0, 1.0, 0.0, // v4 Green
1.0, 1.0, -1.0, 0.0, 1.0, 1.0, // v5 Cyan
-1.0, 1.0, -1.0, 0.0, 0.0, 1.0, // v6 Blue
-1.0, -1.0, -1.0, 0.0, 0.0, 0.0 // v7 Black
]);
// 6个面
var indices = new Uint8Array([
0, 1, 2, 0, 2, 3, // front
0, 3, 4, 0, 4, 5, // right
0, 5, 6, 0, 6, 1, // up
1, 6, 7, 1, 7, 2, // left
7, 4, 3, 7, 3, 2, // down
4, 7, 6, 4, 6, 5 // back
]);
// 创建缓冲区对象
var vertexColorBuffer = gl.createBuffer();
var indexBuffer = gl.createBuffer();
if (!vertexColorBuffer || !indexBuffer) {
return -1;
}
// 将缓冲区对象 绑定到 gl.ARRAY_BUFFER
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);
var FSIZE = verticesColors.BYTES_PER_ELEMENT;
// 获取 变量
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if(a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);
gl.enableVertexAttribArray(a_Position);
// 获取 变量
var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
if(a_Color < 0) {
console.log('Failed to get the storage location of a_Color');
return -1;
}
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
gl.enableVertexAttribArray(a_Color);
// 将缓冲区对象 绑定到 gl.ELEMENT_ARRAY_BUFFER(gl.ELEMENT_ARRAY_BUFFER 管理具有索引结构的三维数据模型)
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
return indices.length;
}
</script>
</body>
</html>
- 案例6 光照
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Point lighted cube (with animation)</title>
</head>
<body onload="main()">
<canvas id="webgl" width="400" height="400">
Please use a browser that supports "canvas"
</canvas>
<script src="../../lib/webgl-utils.js"></script>
<script src="../../lib/webgl-debug.js"></script>
<script src="../../lib/cuon-utils.js"></script>
<script src="../../lib/cuon-matrix.js"></script>
<script>
// 顶点着色器
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' + // 顶点位置
'attribute vec4 a_Color;\n' + // 顶点颜色
'attribute vec4 a_Normal;\n' + // 法向量
'uniform mat4 u_MvpMatrix;\n' + // 视图 投影矩阵
'uniform mat4 u_ModelMatrix;\n' + // 模型矩阵
'uniform mat4 u_NormalMatrix;\n' + // 用来变换 法向量的矩阵
'uniform vec3 u_LightColor;\n' + // 光线颜色
'uniform vec3 u_LightPosition;\n' + // 光线方向
'uniform vec3 u_AmbientLight;\n' + // 环境光
'varying vec4 v_Color;\n' + // 传给片元着色器的
'void main() {\n' +
' gl_Position = u_MvpMatrix * a_Position;\n' +
// 计算位置变换后的 方向量 并 归一化
' vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +
// 计算顶点坐标
' vec4 vertexPosition = u_ModelMatrix * a_Position;\n' +
// 计算光线方向并且归一化 (点光源的光线方向 = 点光线坐标 - 顶点坐标)
' vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));\n' +
// 计算 cos 值 = 漫反射的方向向量 * 法向量
' float nDotL = max(dot(normal, lightDirection), 0.0);\n' +
// 计算漫反射后的物体颜色
' vec3 diffuse = u_LightColor * a_Color.rgb * nDotL;\n' +
// 计算环境光的物体颜色
' vec3 ambient = u_AmbientLight * a_Color.rgb;\n' +
// 计算最终的物体颜色
' v_Color = vec4(diffuse + ambient, a_Color.a);\n' +
'}\n';
// Fragment shader program
var FSHADER_SOURCE =
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_FragColor = v_Color;\n' +
'}\n';
function main() {
// 获取dom元素
var canvas = document.getElementById('webgl');
// 获取gl 上下文
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// 着色器赋值
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the vertex information');
return;
}
// 设置背景颜色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST); // 开启隐藏面消除功能
// 获取着色器 变量
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');
var u_LightColor = gl.getUniformLocation(gl.program, 'u_LightColor');
var u_LightPosition = gl.getUniformLocation(gl.program, 'u_LightPosition');
var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');
if (!u_MvpMatrix || !u_NormalMatrix || !u_LightColor || !u_LightPosition || !u_AmbientLight) {
console.log('Failed to get the storage location');
return;
}
var vpMatrix = new Matrix4(); // 视图 投影矩阵
vpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);// 设置设置透视投影矩阵
vpMatrix.lookAt(6, 6, 14, 0, 0, 0, 0, 1, 0);// 设置视图模型矩阵
// 设置光颜色
gl.uniform3f(u_LightColor, 1.0, 1.0, 1.0);
// 设置光方向
gl.uniform3f(u_LightPosition, 2.3, 4.0, 3.5);
// 设置环境光
gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);
var currentAngle = 0.0; // 当前角度
var modelMatrix = new Matrix4(); // 模型变换矩阵
var mvpMatrix = new Matrix4(); // 视图 投影矩阵
var normalMatrix = new Matrix4(); // 计算法向量的矩阵
var tick = function() {
currentAngle = animate(currentAngle); // 更新旋转角度
// 设置旋转角度
modelMatrix.setRotate(currentAngle, 0, 1, 0); // Rotate around the y-axis
// 更新到 着色器 上
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
// 视图 投影矩阵 更新 并 赋值到 着色器上
mvpMatrix.set(vpMatrix).multiply(modelMatrix);
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
// 根据模型矩阵 计算出用来转化法向量的矩阵
// 将 normalMatrix 设置为 modelMatrix 的 逆转置矩阵,(用于计算 物体 旋转或平移变换后的 法向量)
normalMatrix.setInverseOf(modelMatrix);
normalMatrix.transpose();// 对自身进行转置操作,并将自身设为转置后的结果(用于计算 物体 旋转或平移变换后的 法向量)
gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);
// 清空颜色缓冲区 和 深度缓冲区
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 画图
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
requestAnimationFrame(tick, canvas); // 循环调用
};
tick();
}
function initVertexBuffers(gl) {
// Create a cube
// v6----- v5
// /| /|
// v1------v0|
// | | | |
// | |v7---|-|v4
// |/ |/
// v2------v3
// 顶点
var vertices = new Float32Array([
2.0, 2.0, 2.0, -2.0, 2.0, 2.0, -2.0,-2.0, 2.0, 2.0,-2.0, 2.0, // v0-v1-v2-v3 front
2.0, 2.0, 2.0, 2.0,-2.0, 2.0, 2.0,-2.0,-2.0, 2.0, 2.0,-2.0, // v0-v3-v4-v5 right
2.0, 2.0, 2.0, 2.0, 2.0,-2.0, -2.0, 2.0,-2.0, -2.0, 2.0, 2.0, // v0-v5-v6-v1 up
-2.0, 2.0, 2.0, -2.0, 2.0,-2.0, -2.0,-2.0,-2.0, -2.0,-2.0, 2.0, // v1-v6-v7-v2 left
-2.0,-2.0,-2.0, 2.0,-2.0,-2.0, 2.0,-2.0, 2.0, -2.0,-2.0, 2.0, // v7-v4-v3-v2 down
2.0,-2.0,-2.0, -2.0,-2.0,-2.0, -2.0, 2.0,-2.0, 2.0, 2.0,-2.0 // v4-v7-v6-v5 back
]);
// 颜色
var colors = new Float32Array([
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v1-v2-v3 front
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v3-v4-v5 right
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v0-v5-v6-v1 up
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v1-v6-v7-v2 left
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // v7-v4-v3-v2 down
1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 // v4-v7-v6-v5 back
]);
// 法向量
var normals = new Float32Array([
0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front
1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right
0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up
-1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left
0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, // v7-v4-v3-v2 down
0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0, 0.0, 0.0,-1.0 // v4-v7-v6-v5 back
]);
// 索引值
var indices = new Uint8Array([
0, 1, 2, 0, 2, 3, // front
4, 5, 6, 4, 6, 7, // right
8, 9,10, 8,10,11, // up
12,13,14, 12,14,15, // left
16,17,18, 16,18,19, // down
20,21,22, 20,22,23 // back
]);
// 给着色器变量 赋值
if (!initArrayBuffer(gl, 'a_Position', vertices, 3, gl.FLOAT)) return -1;
if (!initArrayBuffer(gl, 'a_Color', colors, 3, gl.FLOAT)) return -1;
if (!initArrayBuffer(gl, 'a_Normal', normals, 3, gl.FLOAT)) return -1;
// 缓冲区对象置空
gl.bindBuffer(gl.ARRAY_BUFFER, null);
// 创建缓冲区对象
var indexBuffer = gl.createBuffer();
if (!indexBuffer) {
console.log('Failed to create the buffer object');
return false;
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);// 将缓冲区对象 绑定到 gl.ELEMENT_ARRAY_BUFFER(gl.ELEMENT_ARRAY_BUFFER 管理具有索引结构的三维数据模型)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
return indices.length;
}
function initArrayBuffer(gl, attribute, data, num, type) {
// 创建一个 缓冲区对象
var buffer = gl.createBuffer();
if (!buffer) {
console.log('Failed to create the buffer object');
return false;
}
// 将缓冲区对象分配给 gl.ARRAY_BUFFER
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
// 获取变量
var a_attribute = gl.getAttribLocation(gl.program, attribute);
if (a_attribute < 0) {
console.log('Failed to get the storage location of ' + attribute);
return false;
}
gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
// 开启访问权限
gl.enableVertexAttribArray(a_attribute);
return true;
}
// 每次旋转的角度值
var ANGLE_STEP = 30.0;
// 上次时间值
var g_last = Date.now();
function animate(angle) {
// 获取当前时间
var now = Date.now();
var elapsed = now - g_last;
g_last = now;
// 计算应该旋转的角度
var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
return newAngle %= 360;
}
</script>
</body>
</html>
- 雾化效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Fog</title>
</head>
<body onload="main()">
<canvas id="webgl" width="400" height="400">
Please use a browser that supports "canvas"
</canvas>
<p>↑↓: Increase/decrease the fog distance</p></p>
<script src="../../lib/webgl-utils.js"></script>
<script src="../../lib/webgl-debug.js"></script>
<script src="../../lib/cuon-utils.js"></script>
<script src="../../lib/cuon-matrix.js"></script>
<script>
// 顶点着色器
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' + // 设置顶点
'attribute vec4 a_Color;\n' + // 颜色变量
'uniform mat4 u_MvpMatrix;\n' + // 视图投影矩阵
'uniform mat4 u_ModelMatrix;\n' + //
'uniform vec4 u_Eye;\n' + // 观察者位置 视点
'varying vec4 v_Color;\n' + // 颜色
'varying float v_Dist;\n' + // 顶点与 视点的距离
'void main() {\n' +
' gl_Position = u_MvpMatrix * a_Position;\n' + // 计算物体顶点位置
' v_Color = a_Color;\n' +
// 计算顶点与 视点的距离
' v_Dist = distance(u_ModelMatrix * a_Position, u_Eye);\n' +
'}\n';
// 片元着色器
var FSHADER_SOURCE =
'#ifdef GL_ES\n' +
'precision mediump float;\n' + // 设置精度
'#endif\n' +
'uniform vec3 u_FogColor;\n' + // 雾的颜色
'uniform vec2 u_FogDist;\n' + // 雾化的起点和终点
'varying vec4 v_Color;\n' + // 传过来的变量
'varying float v_Dist;\n' + // 传过来的变量
'void main() {\n' +
// 计算雾化因子 u_FogDist.y:雾化终点 v_Dist:顶点与 视点的距离 u_FogDist.x:雾化起点 clamp api:把第一个参数的值 限制在第二三的区间中(0,1)
' float fogFactor = clamp((u_FogDist.y - v_Dist) / (u_FogDist.y - u_FogDist.x), 0.0, 1.0);\n' +
// api 吧 混合 雾的颜色 物体颜色 雾化因子
' vec3 color = mix(u_FogColor, vec3(v_Color), fogFactor);\n' +
' gl_FragColor = vec4(color, v_Color.a);\n' +
'}\n';
function main() {
// 获取dom对象
var canvas = document.getElementById('webgl');
// 获取gl 上下文对象
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 初始化着色器 返回程序对象
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// 着色器变量赋值
var n = initVertexBuffers(gl);
if (n < 1) {
console.log('Failed to set the vertex information');
return;
}
// 雾化颜色
var fogColor = new Float32Array([0.137, 0.231, 0.423]); // 设置雾的颜色
// 雾化的起点和终点
var fogDist = new Float32Array([55, 80]);
// 设置视点坐标
var eye = new Float32Array([25, 65, 35, 1.0]);
// 获取着色器的变量
var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
var u_Eye = gl.getUniformLocation(gl.program, 'u_Eye');
var u_FogColor = gl.getUniformLocation(gl.program, 'u_FogColor');
var u_FogDist = gl.getUniformLocation(gl.program, 'u_FogDist');
if (!u_MvpMatrix || !u_ModelMatrix || !u_Eye || !u_FogColor || !u_FogDist) {
console.log('Failed to get the storage location');
return;
}
// 给 着色器变量赋值
gl.uniform3fv(u_FogColor, fogColor); // Colors
gl.uniform2fv(u_FogDist, fogDist); // Starting point and end point
gl.uniform4fv(u_Eye, eye); // Eye point
// 设置背景色
gl.clearColor(fogColor[0], fogColor[1], fogColor[2], 1.0); // Color of Fog
gl.enable(gl.DEPTH_TEST); // 开启隐藏面消除功能
// 创建矩阵
var modelMatrix = new Matrix4();
modelMatrix.setScale(10, 10, 10); // 设置 收缩比例
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements); // 赋值
// 创建矩阵
var mvpMatrix = new Matrix4();
mvpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 1000); // // 定义透视 投影矩阵
mvpMatrix.lookAt(eye[0], eye[1], eye[2], 0, 2, 0, 0, 1, 0);// 创建视图矩阵
mvpMatrix.multiply(modelMatrix);
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
document.onkeydown = function(ev){ keydown(ev, gl, n, u_FogDist, fogDist); }; // 注册事件
// 清空颜色缓冲器 和 深度缓冲区
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 画图
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
var modelViewMatrix = new Matrix4(); // ??? 干什么的 没看懂
modelViewMatrix.setLookAt(eye[0], eye[1], eye[2], 0, 2, 0, 0, 1, 0);
modelViewMatrix.multiply(modelMatrix);
modelViewMatrix.multiplyVector4(new Vector4([1, 1, 1, 1]));
mvpMatrix.multiplyVector4(new Vector4([1, 1, 1, 1]));
modelViewMatrix.multiplyVector4(new Vector4([-1, 1, 1, 1]));
mvpMatrix.multiplyVector4(new Vector4([-1, 1, 1, 1]));
}
function keydown(ev, gl, n, u_FogDist, fogDist) {
switch (ev.keyCode) {
case 38: // 方向键 上键 改变 fogDist的值
fogDist[1] += 1;
break;
case 40: // 方向键 下键 改变 fogDist的值
if (fogDist[1] > fogDist[0]) fogDist[1] -= 1;
break;
default: return;
}
gl.uniform2fv(u_FogDist, fogDist); // 赋值
// 清空颜色缓冲器 和 深度缓冲区
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 画图
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}
function initVertexBuffers(gl) {
// Create a cube
// v6----- v5
// /| /|
// v1------v0|
// | | | |
// | |v7---|-|v4
// |/ |/
// v2------v3
var vertices = new Float32Array([ // 顶点坐标
1, 1, 1, -1, 1, 1, -1,-1, 1, 1,-1, 1, // v0-v1-v2-v3 front
1, 1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, // v0-v3-v4-v5 right
1, 1, 1, 1, 1,-1, -1, 1,-1, -1, 1, 1, // v0-v5-v6-v1 up
-1, 1, 1, -1, 1,-1, -1,-1,-1, -1,-1, 1, // v1-v6-v7-v2 left
-1,-1,-1, 1,-1,-1, 1,-1, 1, -1,-1, 1, // v7-v4-v3-v2 down
1,-1,-1, -1,-1,-1, -1, 1,-1, 1, 1,-1 // v4-v7-v6-v5 back
]);
var colors = new Float32Array([ // 颜色坐标
0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, // v0-v1-v2-v3 front
0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, // v0-v3-v4-v5 right
1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, 1.0, 0.4, 0.4, // v0-v5-v6-v1 up
1.0, 1.0, 0.4, 1.0, 1.0, 0.4, 1.0, 1.0, 0.4, 1.0, 1.0, 0.4, // v1-v6-v7-v2 left
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // v7-v4-v3-v2 down
0.4, 1.0, 1.0, 0.4, 1.0, 1.0, 0.4, 1.0, 1.0, 0.4, 1.0, 1.0 // v4-v7-v6-v5 back
]);
var indices = new Uint8Array([ // 3d 物体 索引值坐标
0, 1, 2, 0, 2, 3, // front
4, 5, 6, 4, 6, 7, // right
8, 9,10, 8,10,11, // up
12,13,14, 12,14,15, // left
16,17,18, 16,18,19, // down
20,21,22, 20,22,23 // back
]);
// 创建 缓冲区对象
var indexBuffer = gl.createBuffer();
if (!indexBuffer)
return -1;
// 着色器 变量赋值
if (!initArrayBuffer(gl, vertices, 3, gl.FLOAT, 'a_Position')) return -1;
if (!initArrayBuffer(gl, colors, 3, gl.FLOAT, 'a_Color')) return -1;
// 将缓冲区对象 绑定到 gl.ELEMENT_ARRAY_BUFFER(gl.ELEMENT_ARRAY_BUFFER 管理具有索引结构的三维数据模型)
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
return indices.length;
}
function initArrayBuffer (gl, data, num, type, attribute) {
// 创建 缓冲区对象
var buffer = gl.createBuffer();
if (!buffer) {
console.log('Failed to create the buffer object');
return false;
}
// 将缓冲区对象分配给 ARRAY_BUFFER
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW); // 赋值
// 获取着色器 变量
var a_attribute = gl.getAttribLocation(gl.program, attribute);
if (a_attribute < 0) {
console.log('Failed to get the storage location of ' + attribute);
return false;
}
gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
// 开启访问权限
gl.enableVertexAttribArray(a_attribute);
// 缓冲区对象置空
gl.bindBuffer(gl.ARRAY_BUFFER, null);
return true;
}
</script>
</body>
</html>