🔍前言
视觉盛宴 WebGL
WebGL招聘需求
以上两点让我学起了WebGL。
- 我亦无他,惟手熟尔
📣
☕正文
什么是WebGL
一句话概括:在浏览器中开发渲染交互式的3D图形。
知识点
坐标系 :默认为右手坐标系。
正方向最大值为1,负方向最大值为-1。
绘制区域: 用canvas。做2D还是3D,是由上下文对象是2还是3。
2: 用getContext('2d')。
3: 用getContext('webgl')。
类型化数组:
举个例子,下面贴个画三角形的类数组的定义,如下:
这种new Float32Array
去定义几何图形的顶点的,定义如下:
javascript
const vertices = new Float32Array([
0.0, 0.5, // 第一个顶点坐标
-0.5, -0.5, // 第二顶点坐标
0.5, -0.5, // 第三顶点坐标
... // 再有就是以此类推
])
着色器:
类型分两种:1、顶点 ;2、片段。
- 第一种写法:
举例说明:
js
// 顶点
const VSHADER_SOURCE =
`void main() {
gl_Position = vec4(0.0, 0.0, 0.0, 1.0); // 顶点位置
gl_PointSize = 10.0; // 点的大小
}`
// 片段
const FSHADER_SOURCE =
`void main() {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); // 点的颜色
}`
- 第二种写法:
设置自定义类型的script标签。
顶点: vertex-shader
片段: fragment-shader
数据类型:
- 基本:
float
int
bool
- 向量:
- 浮点:
vec2
vec3
vec4
- 整数:
ivec2
ivec3
ivec4
- 布尔:
ivec2
bvec2
bvec2
- 浮点:
- 矩阵:
mat2
mat3
mat4
- 采样器(纹理):
sampler2D
samplerCube
变量修饰符:
顶点:从js中传参给顶点着色器。
- attribute:传输的是 那些与顶点相关的数据。
- uniform:传输的是 那些对于所有顶点都相同(或者与顶点无关)的数据。
内置:
- 顶点:
gl_Position
、gl_PointSize
- 片元:
gl_FragColor
用于顶点和片段传参的。
- varying
例子:
js
// 顶点着色器代码
const VSHADER_SOURCE =
`
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color; // 声明v_Color,用来传颜色给片元的。
void main() {
gl_Position = a_Position;
v_Color = a_Color; // 定义v_Color为a_Color
}
`;
// 片元着色器代码
const FSHADER_SOURCE =
`
precision mediump float;
varying vec4 v_Color; // 声明来接收顶点传过来的颜色的。
void main() {
gl_FragColor = v_Color; // 片元颜色用顶点传过来的这个值。
}
`;
精度:
- precision:默认精度声明。
- high:用于 位置、复杂的物理效果等。
- mediump:用于 纹理坐标等。
- lowp:用于颜色值等。
用例:
precision lowp float;
入口 : main()
。
点
先看效果,再看码。
// index.html文件
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Clear canvas</title>
</head>
<body onload="main()">
// 定义一个canvas 宽高都是400px的。
<canvas id="webgl" width="400" height="400">
Please use the browser supporting "canvas"
</canvas>
<script src="./point.js"></script>
</body>
</html>
// index.js
js
// 顶点着色器
const VSHADER_SOURCE =
`void main() {
gl_Position = vec4(0.0, 0.0, 0.0, 1.0); // 顶点位置
gl_PointSize = 10.0; // 点大小
}`
// 片元着色器
const FSHADER_SOURCE =
`void main() {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); // 图像中的像素(片元)的颜色为绿
}`
// 主函数main
function main() {
// 获取canvas
const canvas = document.getElementById('webgl')
// 获取WebGL上下文
const gl = canvas.getContext('webgl') // 创WebGL上下文gl
if (!gl) {
console.log('Failed to get the rendering context for WebGL')
return
}
const vertexShader = gl.createShader(gl.VERTEX_SHADER) // 创建顶点着色器对象
gl.shaderSource(vertexShader, VSHADER_SOURCE) // 将顶点着色器源码VSHADER_SOURCE分配给顶点着色器对象
gl.compileShader(vertexShader) // 编译顶点着色器
// 片元也是如此步骤:创、分、编译
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fragmentShader, FSHADER_SOURCE)
gl.compileShader(fragmentShader)
const program = gl.createProgram() // 创建WebGL程序对象
gl.attachShader(program, vertexShader) // 将顶点着色器附加到程序对象
gl.attachShader(program, fragmentShader) // 将片元着色器附加到程序对象
gl.linkProgram(program) // 将顶点和片元连接成一个可执行的WebGL程序
gl.useProgram(program) // 告诉WebGL使用特定的程序对象
// 清空canvas = 设置canvas的背景色
gl.clearColor(0.0, 0.0, 0.0, 1.0)
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT)
// 绘制一个点
gl.drawArrays(gl.POINTS, 0, 1)
}
注:可以借助一些封装好的WebGL工具库做。
三角形
先看效果,再看码。
// html部分和上面一样,换js文件就行
// index.js
1、定义着色器
js
// 顶点着色器
// 用到了接收js给的顶点数据进行处理
const VSHADER_SOURCE = `
attribute vec4 a_Position; // 声明一个attribute属性a_Position。用来接收顶点位置信息。
void main() { // 顶点着色器的主函数main
gl_Position = a_Position; // 将传入的顶点位置赋值给内置变量
gl_PointSize = 10.0; // 设置点的大小
}
`
// 片元着色器
const FSHADER_SOURCE = `
void main() { // 片元着色器的主函数main
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 设置片元的颜色为红
}
`
// 主函数
js
function main() {
// 获取canvas元素
const canvas = document.getElementById('webgl')
// 获取WebGL上下文
const gl = canvas.getContext('webgl')
// 初始化着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER) // 顶点着色器对象
gl.shaderSource(vertexShader, VSHADER_SOURCE) // 将顶点着色器源码 `VSHADER_SOURCE` 分配给顶点着色器对象。
gl.compileShader(vertexShader) // 编译顶点着色器。
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fragmentShader, FSHADER_SOURCE)
gl.compileShader(fragmentShader)
// 创建程序对象并链接着色器
const program = gl.createProgram()
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
gl.useProgram(program)
// 设置顶点位置
const n = initVertexBuffers(gl, program);
if (n < 0) {
console.log('Failed to set the positions of the vertices');
return
}
// 设置<canvas>背景色
gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
console.log(n)
// 绘制三个点
gl.drawArrays(gl.TRIANGLES, 0, n) // 绘制三角形,从顶点数组的第一个点开始,绘制 `n` 个顶点。
}
// 初始化顶点缓冲区函数
js
function initVertexBuffers(gl, program) {
// 定义顶点数据 - 3个点,构成三角形
const vertices = new Float32Array([
0.0, 0.5,
-0.5, -0.5,
0.5, -0.5
])
const n = 3;
// 创建WebGL缓冲区对象
const vertexBuffer = gl.createBuffer()
if (!vertexBuffer) {
console.log('Failed to create the buffer object')
return -1
}
// 将缓冲区对象绑定到目标
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向缓冲区对象中写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
// 获取并设置顶点着色器中的属性
const a_Position = gl.getAttribLocation(program, 'a_Position')
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return
}
// 将顶点数据传入顶点着色器
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 启用点点属性函数
gl.enableVertexAttribArray(a_Position);
return n;
}
矩形
先看效果,再看码:
绘制矩形,WebGL不能直接绘制矩形,但可以用两个三角形去组成一个矩形。
两个三角形:
- (v0, v1, v2)
- (v2, v1, v3)
用gl.TRIANGLES
、gl.TRANGLES_STRIP
、gl.TRIANGLES_FAN
去做。
跟上面那个三角形八九差不离。
1、加个点
js
// 共4个顶点
const vertices = new Float32Array([
-0.5, 0.5,
-0.5, -0.5,
0.5, 0.5,
0.5, -0.5
])
顶点数从3改成3 const n = 4
2、改函数
gl.drawArrays(gl.TRIANGLE_STRIP, 0, n)
图形变化
平移
比如说,平移一个三角形。
步骤流程是,需要对顶点坐标的每个分量(x和y),加上三角形在对应轴平移的距离。
概念:将平移距离Tx、Ty、Tz的值传入顶点着色器。然后,分别加在顶点坐标的对应值上。再赋值给gl_Positiion。
旋转
先看效果,再看码:
- 旋转轴(图形将围绕旋转轴)
- 旋转方向(方向:顺时针或者逆时针)
- 旋转角度(图形旋转角度)
用z轴逆时针旋转角度去解释:
// 旋转的顶点着色器代码
js
const VSHADER_SOURCE = `
attribute vec4 a_Position;
uniform float u_CosB, u_SinB;
void main() {
gl_Position.x = a_Position.x * u_CosB - a_Position.y * u_SinB; // 根据传入的旋转角度 `u_CosB` 和 `u_SinB` 对顶点进行 x 轴方向的线性变换。
gl_Position.y = a_Position.x * u_SinB - a_Position.y * u_CosB; // 根据传入的旋转角度 `u_CosB` 和 `u_SinB` 对顶点进行 y 轴方向的线性变换。
gl_Position.z = a_Position.z; // 保持原始的 z 轴坐标不变。
gl_Position.w = 1.0; // 设置顶点的齐次坐标 w 为1.0,表示一个点。
}
`
// 用于计算旋转角度的余弦和正弦值。 // 并将它们作为 uniform 变量传递给 WebGL 程序的顶点着色器。
js
// 将角度值转换为弧度值
const radian = Math.PI * ANGLE / 180
const cosB = Math.cos(radian)
const sinB = Math.sin(radian)
const u_CosB = gl.getUniformLocation(program, 'u_CosB')
const u_SinB = gl.getUniformLocation(program, 'u_SinB')
gl.uniform1f(u_CosB, cosB)
gl.uniform1f(u_SinB, sinB)
缩放
乘以某值,进行缩放:
js
x' = Sx * X
y' = Sy * Y
z' = Sz * Z
比如说,把之前那个三角形在垂直方向拉伸1.5倍,代码如下:
js
const Sx = 1.0, Sy = 1.5, Sz = 1.0
const xformMatrix = 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,
])
图形变化这几个罗列了平移、旋转和缩放。
👉就是在js中把数据(点的处理)处理完成之后传值给顶点着色器。
🥤后记
☎️ 希望对大家有所帮助,本文有些地方可能考虑不够周到,有些纰漏,就当抛砖引玉,还望您海涵,如有错误,望不吝赐教,欢迎评论区留言互相学习。感谢阅读,祝您开发有乐趣。
搞WebGL,还要学WebGpu、Cesium、threejs、babylonjs等等。