WebGL 基础API详解:掌握3D图形编程的核心工具
WebGL API总览
WebGL提供了丰富的API来控制GPU进行3D图形渲染。这些API可以按功能分为几大类,每一类都承担着不同的职责,共同协作完成从数据输入到图像输出的整个渲染流程。
上下文和状态管理API
在开始绘制之前,我们首先需要获取WebGL的绘图上下文,并管理绘图状态。
获取WebGL上下文
// 从canvas元素获取WebGL绘图环境
const canvas = document.getElementById('myCanvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
视口设置
// 设置渲染区域的大小和位置(通常是整个canvas)
gl.viewport(0, 0, canvas.width, canvas.height);
清空缓冲区
// 设置清空颜色(这里是黑色)
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 清空颜色缓冲区
gl.clear(gl.COLOR_BUFFER_BIT);
// 如果启用了深度测试,还需清空深度缓冲区
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
功能开关
// 启用深度测试
gl.enable(gl.DEPTH_TEST);
// 启用面剔除
gl.enable(gl.CULL_FACE);
// 启用混合(透明度处理)
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
// 禁用功能
gl.disable(gl.DEPTH_TEST);
着色器相关API
着色器是WebGL的灵魂,这些API负责创建、编译和管理着色器程序。
创建和编译顶点着色器
// 创建顶点着色器对象
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
// 设置着色器源代码
const vertexShaderSource = `
attribute vec4 a_position;
void main() {
gl_Position = a_position;
}
`;
gl.shaderSource(vertexShader, vertexShaderSource);
// 编译着色器
gl.compileShader(vertexShader);
// 检查编译是否成功
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
console.error('顶点着色器编译失败:', gl.getShaderInfoLog(vertexShader));
}
创建和编译片元着色器
// 创建片元着色器对象
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 设置着色器源代码
const fragmentShaderSource = `
precision mediump float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
`;
gl.shaderSource(fragmentShader, fragmentShaderSource);
// 编译着色器
gl.compileShader(fragmentShader);
// 检查编译是否成功
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
console.error('片元着色器编译失败:', gl.getShaderInfoLog(fragmentShader));
}
程序相关API
程序对象是顶点着色器和片元着色器的组合,这些API负责管理和链接着色器。
创建和链接程序
// 创建程序对象
const program = gl.createProgram();
// 将着色器附加到程序
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 链接程序
gl.linkProgram(program);
// 检查链接是否成功
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('程序链接失败:', gl.getProgramInfoLog(program));
}
// 使用程序
gl.useProgram(program);
变量定位和赋值API
这些API用于在JavaScript和着色器之间传递数据,是实现动态渲染的关键。
获取变量位置
// 获取attribute变量位置(顶点着色器中的变量)
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
const colorUniformLocation = gl.getUniformLocation(program, 'u_color');
给attribute变量赋值
// 给attribute变量赋值(1-4个浮点数)
gl.vertexAttrib1f(location, value); // 1个浮点数
gl.vertexAttrib2f(location, x, y); // 2个浮点数
gl.vertexAttrib3f(location, x, y, z); // 3个浮点数
gl.vertexAttrib4f(location, x, y, z, w); // 4个浮点数
给uniform变量赋值
// 给uniform变量赋值(浮点数)
gl.uniform1f(colorUniformLocation, value); // 1个浮点数
gl.uniform2f(colorUniformLocation, x, y); // 2个浮点数
gl.uniform3f(colorUniformLocation, r, g, b); // 3个浮点数
gl.uniform4f(colorUniformLocation, r, g, b, a); // 4个浮点数
// 给uniform变量赋值(整数)
gl.uniform1i(textureUniformLocation, textureUnit); // 1个整数
// 给矩阵uniform变量赋值
const matrix = new Float32Array([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]);
gl.uniformMatrix4fv(matrixUniformLocation, false, matrix);
缓冲区相关API
缓冲区用于高效地向GPU传输大量顶点数据,是高性能渲染的基础。
创建和绑定缓冲区
// 创建缓冲区对象
const buffer = gl.createBuffer();
// 绑定缓冲区(指定缓冲区类型)
gl.bindBuffer(gl.ARRAY_BUFFER, buffer); // 顶点数据
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); // 索引数据
向缓冲区写入数据
// 创建顶点数据
const vertices = new Float32Array([
-0.5, -0.5, // 第一个点
0.5, -0.5, // 第二个点
0.0, 0.5 // 第三个点
]);
// 向当前绑定的缓冲区写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// STATIC_DRAW: 数据一次性写入,多次使用
// DYNAMIC_DRAW: 数据频繁更改
// STREAM_DRAW: 数据偶尔更改
配置顶点属性指针
// 启用顶点属性数组
gl.enableVertexAttribArray(positionAttributeLocation);
// 配置顶点属性
gl.vertexAttribPointer(
positionAttributeLocation, // attribute变量位置
2, // 每个顶点包含2个分量(x, y)
gl.FLOAT, // 数据类型为浮点数
false, // 不标准化
0, // 步长(0表示紧密排列)
0 // 偏移量
);
绘制相关API
这些API触发GPU执行渲染操作。
绘制图元
// 使用顶点数组绘制
gl.drawArrays(
gl.TRIANGLES, // 图元类型
0, // 起始顶点索引
3 // 顶点数量
);
// 使用索引数组绘制
const indices = new Uint16Array([0, 1, 2]);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
gl.drawElements(
gl.TRIANGLES, // 图元类型
3, // 要绘制的索引数量
gl.UNSIGNED_SHORT, // 索引数据类型
0 // 偏移量
);
纹理相关API
纹理用于为3D模型添加细节和真实感。
创建和配置纹理
// 创建纹理对象
const texture = gl.createTexture();
// 绑定纹理
gl.bindTexture(gl.TEXTURE_2D, texture);
// 设置纹理图像
gl.texImage2D(
gl.TEXTURE_2D, // 纹理目标
0, // 纹理级别
gl.RGBA, // 内部格式
gl.RGBA, // 源格式
gl.UNSIGNED_BYTE, // 源数据类型
imageData // 图像数据
);
// 设置纹理参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
// 激活纹理单元
gl.activeTexture(gl.TEXTURE0); // 激活纹理单元0
混合和深度测试API
这些API控制像素的混合方式和深度测试行为。
混合设置
// 启用混合
gl.enable(gl.BLEND);
// 设置混合函数
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); // 标准alpha混合
深度测试设置
// 启用深度测试
gl.enable(gl.DEPTH_TEST);
// 设置深度测试函数
gl.depthFunc(gl.LEQUAL); // 小于等于时通过测试
// 设置深度缓冲写入掩码
gl.depthMask(true); // 允许写入深度缓冲
常用常量参考
着色器类型
-
gl.VERTEX_SHADER- 顶点着色器 -
gl.FRAGMENT_SHADER- 片元着色器
缓冲区类型
-
gl.ARRAY_BUFFER- 顶点数据缓冲区 -
gl.ELEMENT_ARRAY_BUFFER- 索引数据缓冲区
缓冲区使用方式
-
gl.STATIC_DRAW- 静态数据,一次性写入 -
gl.DYNAMIC_DRAW- 动态数据,频繁更改 -
gl.STREAM_DRAW- 流数据,偶尔更改
绘制模式
-
gl.POINTS- 点 -
gl.LINES- 线段 -
gl.TRIANGLES- 三角形
数据类型
-
gl.FLOAT- 浮点数 -
gl.UNSIGNED_BYTE- 无符号字节
纹理类型
-
gl.TEXTURE_2D- 2D纹理 -
gl.TEXTURE_CUBE_MAP- 立方体贴图
缓冲区位
-
gl.COLOR_BUFFER_BIT- 颜色缓冲区 -
gl.DEPTH_BUFFER_BIT- 深度缓冲区
实际应用示例
回顾我们在前一节中使用的API:
// 使用着色器程序
gl.useProgram(program);
// 获取变量位置
const positionLocation = gl.getAttribLocation(program, 'a_Position');
const colorLocation = gl.getUniformLocation(program, 'u_Color');
// 给attribute变量赋值
gl.vertexAttrib2f(sizeLocation, canvas.width, canvas.height);
// 给uniform变量赋值
gl.uniform4f(colorLocation, r, g, b, a);
// 执行绘制
gl.drawArrays(gl.POINTS, 0, 1);
// 设置清空颜色
gl.clearColor(r, g, b, a);
// 清空缓冲区
gl.clear(gl.COLOR_BUFFER_BIT);
掌握了这些基础API,你就能构建各种复杂的3D图形应用了。这些API看似繁多,但它们都有明确的职责分工,一旦熟悉了它们的作用,WebGL编程就会变得清晰明了。