6.5 WebGL 简介
6.5 WebGL 简介
WebGL(Web Graphics Library)是基于 OpenGL ES 的 JavaScript API,允许在浏览器中实现高性能的 3D 图形渲染。作为 Canvas API 的扩展,WebGL 为 Web 开发者提供了直接访问 GPU 硬件加速的能力,开启了浏览器中复杂 3D 可视化、游戏开发和特效制作的新纪元。
WebGL 核心概念
1. 渲染管线基础
WebGL 采用图形处理的标准管线架构:
-
顶点处理:
- 顶点着色器处理每个顶点的位置和属性
- 进行模型变换、视图变换和投影变换
-
图元装配:
- 将顶点连接成三角形、线条等基本图元
-
光栅化:
- 将图元转换为像素片段
-
片段处理:
- 片段着色器计算每个像素的最终颜色
- 执行纹理采样、光照计算等操作
-
输出合并:
- 深度测试、模板测试
- 颜色混合(Blending)
2. 基本WebGL程序结构
// 1. 获取WebGL上下文
const canvas = document.getElementById('glCanvas');
const gl = canvas.getContext('webgl');
// 2. 初始化着色器程序
const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
// 3. 创建缓冲区并填充数据
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
// 4. 渲染场景
function render() {
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// 设置着色器程序
gl.useProgram(shaderProgram);
// 绑定顶点数据
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(programInfo.attribLocations.vertexPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
// 绘制
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);
requestAnimationFrame(render);
}
render();
着色器编程基础
1. 顶点着色器示例
// 顶点着色器代码 (GLSL)
attribute vec3 aVertexPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main() {
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aVertexPosition, 1.0);
}
2. 片段着色器示例
// 片段着色器代码 (GLSL)
precision mediump float;
uniform vec4 uColor;
void main() {
gl_FragColor = uColor;
}
3. 着色器初始化函数
function initShaderProgram(gl, vsSource, fsSource) {
// 编译着色器
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
// 创建着色器程序
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
console.error('Shader program link error:', gl.getProgramInfoLog(shaderProgram));
return null;
}
return shaderProgram;
}
function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('Shader compile error:', gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
3D图形基础
1. 矩阵变换
// 创建模型视图矩阵
const modelViewMatrix = mat4.create();
mat4.translate(modelViewMatrix, modelViewMatrix, [0.0, 0.0, -6.0]);
mat4.rotate(modelViewMatrix, modelViewMatrix, rotation, [0, 1, 0]);
// 创建投影矩阵
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, 45 * Math.PI / 180, canvas.width / canvas.height, 0.1, 100.0);
// 传递给着色器
gl.uniformMatrix4fv(
programInfo.uniformLocations.projectionMatrix,
false,
projectionMatrix
);
gl.uniformMatrix4fv(
programInfo.uniformLocations.modelViewMatrix,
false,
modelViewMatrix
);
2. 绘制3D立方体
// 立方体顶点数据(36个顶点,6个面 × 2个三角形 × 3个顶点)
const positions = [
// 前面
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
// 后面
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
// 其他面...
];
// 索引缓冲区
const indices = [
0, 1, 2, 0, 2, 3, // 前面
4, 5, 6, 4, 6, 7, // 后面
// 其他面索引...
];
// 创建并绑定索引缓冲区
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
// 绘制时使用
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
纹理与光照
1. 纹理加载与应用
// 创建纹理
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// 填充默认像素
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([255, 0, 0, 255])); // 红色
// 加载图像
const image = new Image();
image.onload = function() {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
// 非2的幂次方图像处理
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
gl.generateMipmap(gl.TEXTURE_2D);
} else {
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);
}
};
image.src = 'texture.jpg';
// 在着色器中使用
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(programInfo.uniformLocations.uSampler, 0);
2. 基础光照实现
// 顶点着色器添加法线和光照计算
attribute vec3 aVertexNormal;
uniform mat3 uNormalMatrix;
varying vec3 vNormal;
void main() {
vNormal = uNormalMatrix * aVertexNormal;
// ...原有位置计算
}
// 片段着色器实现Phong光照模型
uniform vec3 uLightDirection;
uniform vec3 uAmbientLight;
uniform vec3 uDiffuseLight;
varying vec3 vNormal;
void main() {
// 环境光
vec3 ambient = uAmbientLight;
// 漫反射
float nDotL = max(dot(normalize(vNormal), normalize(-uLightDirection)), 0.0);
vec3 diffuse = uDiffuseLight * nDotL;
gl_FragColor = vec4(ambient + diffuse, 1.0) * texture2D(uSampler, vTextureCoord);
}
性能优化技巧
-
批处理绘制调用:
- 合并相似对象的绘制
- 使用实例化渲染(ANGLE_instanced_arrays)
-
缓冲区管理:
- 重用缓冲区对象
- 使用gl.STATIC_DRAW、gl.DYNAMIC_DRAW等适当用法提示
-
着色器优化:
- 减少分支语句
- 最小化精度声明(mediump代替highp)
- 使用内置函数
-
纹理优化:
- 使用Mipmap
- 压缩纹理格式(WEBGL_compressed_texture)
- 合理设置过滤参数
现代WebGL开发工具
-
Three.js:
const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer(); const geometry = new THREE.BoxGeometry(); const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const cube = new THREE.Mesh(geometry, material); scene.add(cube); camera.position.z = 5; function animate() { requestAnimationFrame(animate); cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render(scene, camera); } animate(); -
Babylon.js:
const canvas = document.getElementById("renderCanvas"); const engine = new BABYLON.Engine(canvas, true); const createScene = function() { const scene = new BABYLON.Scene(engine); const camera = new BABYLON.ArcRotateCamera("Camera", 0, 0, 10, BABYLON.Vector3.Zero(), scene); camera.attachControl(canvas, false); const light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene); const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter:2}, scene); return scene; }; const scene = createScene(); engine.runRenderLoop(function() { scene.render(); }); -
调试工具:
- WebGL Inspector(浏览器扩展)
- Spector.js(实时WebGL捕获分析)
// 使用Spector.js捕获帧 const spector = new SPECTOR.Spector(); spector.displayUI(); spector.captureCanvas(canvas);
WebGL 2.0新特性
-
核心增强:
- 3D纹理和2D纹理数组
- 统一缓冲区对象(UBO)
- 变换反馈(Transform Feedback)
-
着色器改进:
- 非方阵矩阵支持
- 更多内置函数
- 增强的循环控制
-
性能提升:
- 实例化渲染原生支持
- 多重采样渲染缓冲
- 更高效的纹理压缩
// 检测WebGL 2.0支持
const gl = canvas.getContext('webgl2');
if (!gl) {
console.warn('WebGL 2 not supported, falling back to WebGL 1');
gl = canvas.getContext('webgl') ||
canvas.getContext('experimental-webgl');
}
// WebGL 2.0示例:使用统一缓冲区
const ubo = gl.createBuffer();
gl.bindBuffer(gl.UNIFORM_BUFFER, ubo);
gl.bufferData(gl.UNIFORM_BUFFER, new Float32Array(matrixData), gl.STATIC_DRAW);
gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, ubo);
实战案例:粒子系统
// 初始化粒子数据
const PARTICLE_COUNT = 10000;
const particles = new Float32Array(PARTICLE_COUNT * 3);
for(let i = 0; i < PARTICLE_COUNT; i++) {
particles[i*3] = Math.random() * 2 - 1; // x
particles[i*3+1] = Math.random() * 2 - 1; // y
particles[i*3+2] = Math.random() * 2 - 1; // z
}
// 创建粒子缓冲区
const particleBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, particleBuffer);
gl.bufferData(gl.ARRAY_BUFFER, particles, gl.STATIC_DRAW);
// 顶点着色器(使用点精灵)
const vsSource = `
attribute vec3 aPosition;
uniform mat4 uMVP;
uniform float uPointSize;
void main() {
gl_Position = uMVP * vec4(aPosition, 1.0);
gl_PointSize = uPointSize;
}
`;
// 片段着色器(圆形点)
const fsSource = `
precision highp float;
void main() {
vec2 coord = gl_PointCoord - vec2(0.5);
if(length(coord) > 0.5) discard;
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
`;
// 渲染循环
function render() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 更新粒子位置(CPU端简单示例)
for(let i = 0; i < PARTICLE_COUNT; i++) {
particles[i*3+1] += 0.01;
if(particles[i*3+1] > 1.0) particles[i*3+1] = -1.0;
}
gl.bufferSubData(gl.ARRAY_BUFFER, 0, particles);
// 绘制
gl.drawArrays(gl.POINTS, 0, PARTICLE_COUNT);
requestAnimationFrame(render);
}
学习资源推荐
-
官方文档:
-
进阶教程:
- WebGL 2 Samples
- Learn OpenGL(概念通用)
-
工具库:
- Three.js
- Babylon.js
- PixiJS(2D渲染)
WebGL为Web带来了前所未有的图形处理能力,虽然学习曲线较陡峭,但掌握后可以创建出令人惊艳的视觉体验。建议从基础概念入手,逐步过渡到高级特效,最终结合现代框架开发复杂的3D应用。
#前端开发
分享于 2025-05-20