WebGL入门:从零开始学习3D图形渲染
前言
各位前端小伙伴,不知道你们有没有想过在浏览器中实现炫酷的3D效果?WebGL可以让你在浏览器中实现高性能的3D图形渲染!
我曾经开发过一个3D数据可视化应用,使用WebGL实现了实时的3D图表渲染,性能比Canvas 2D提升了10倍以上!
什么是WebGL?
WebGL是一种基于OpenGL ES 2.0的3D图形API,可以在浏览器中实现硬件加速的3D图形渲染。
WebGL工作原理
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ JavaScript │ │ WebGL API │ │ GPU │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
│ 1. 创建着色器 │ │
│───────────────────────>│ │
│ │ │
│ 2. 编译着色器 │ │
│<───────────────────────│ │
│ │ │
│ 3. 创建缓冲区 │ │
│───────────────────────>│ │
│ │ │
│ 4. 绑定数据 │ │
│───────────────────────>│ │
│ │ │
│ │ 5. 发送到GPU │
│ │────────────────────────>│
│ │ │
│ │ 6. 渲染 │
│ │<────────────────────────│
│ │ │
│ 7. 显示结果 │ │
│<───────────────────────│ │
第一个WebGL程序
1. 创建Canvas元素
<canvas id="gl-canvas" width="800" height="600"></canvas>
2. 获取WebGL上下文
const canvas = document.getElementById('gl-canvas')
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl')
if (!gl) {
console.error('WebGL not supported')
}
3. 创建顶点着色器
const vertexShaderSource = `
attribute vec4 aVertexPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main() {
gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
}
`
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vertexShader, vertexShaderSource)
gl.compileShader(vertexShader)
4. 创建片段着色器
const fragmentShaderSource = `
precision mediump float;
void main() {
gl_FragColor = vec4(1.0, 0.5, 0.2, 1.0);
}
`
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fragmentShader, fragmentShaderSource)
gl.compileShader(fragmentShader)
5. 创建着色器程序
const program = gl.createProgram()
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Program link error:', gl.getProgramInfoLog(program))
}
gl.useProgram(program)
6. 创建顶点缓冲区
const vertices = [
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
0.0, 1.0, 0.0
]
const vertexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
7. 设置顶点属性
const positionLocation = gl.getAttribLocation(program, 'aVertexPosition')
gl.enableVertexAttribArray(positionLocation)
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0)
8. 设置矩阵
function createMat4() {
return new Float32Array([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
])
}
const projectionMatrix = createMat4()
const modelViewMatrix = createMat4()
const projectionLocation = gl.getUniformLocation(program, 'uProjectionMatrix')
const modelViewLocation = gl.getUniformLocation(program, 'uModelViewMatrix')
gl.uniformMatrix4fv(projectionLocation, false, projectionMatrix)
gl.uniformMatrix4fv(modelViewLocation, false, modelViewMatrix)
9. 渲染
gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.TRIANGLES, 0, 3)
WebGL核心概念
着色器(Shaders)
// 顶点着色器 - 处理顶点位置
const vertexShader = `
attribute vec3 aPosition;
void main() {
gl_Position = vec4(aPosition, 1.0);
}
`
// 片段着色器 - 处理像素颜色
const fragmentShader = `
precision mediump float;
uniform vec4 uColor;
void main() {
gl_FragColor = uColor;
}
`
缓冲区(Buffers)
// 创建顶点缓冲区
const positionBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW)
// 创建索引缓冲区
const indexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW)
矩阵(Matrices)
// 透视投影矩阵
function createPerspectiveMatrix(fov, aspect, near, far) {
const f = 1.0 / Math.tan(fov / 2)
const nf = 1 / (near - far)
return new Float32Array([
f / aspect, 0, 0, 0,
0, f, 0, 0,
0, 0, (far + near) * nf, -1,
0, 0, 2 * far * near * nf, 0
])
}
高级WebGL技术
纹理映射
async function loadTexture(url) {
const texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
const level = 0
const internalFormat = gl.RGBA
const width = 1
const height = 1
const border = 0
const srcFormat = gl.RGBA
const srcType = gl.UNSIGNED_BYTE
const pixel = new Uint8Array([0, 0, 255, 255])
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border, srcFormat, srcType, pixel)
const image = new Image()
image.crossOrigin = 'anonymous'
return new Promise((resolve) => {
image.onload = () => {
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, srcFormat, srcType, image)
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)
}
resolve(texture)
}
image.src = url
})
}
光照效果
const vertexShader = `
attribute vec3 aPosition;
attribute vec3 aNormal;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
varying highp vec3 vNormal;
void main() {
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0);
vNormal = mat3(uModelViewMatrix) * aNormal;
}
`
const fragmentShader = `
precision mediump float;
varying highp vec3 vNormal;
void main() {
highp vec3 lightDirection = vec3(0.0, 0.0, 1.0);
highp float dotProduct = dot(normalize(vNormal), normalize(lightDirection));
highp float intensity = max(dotProduct, 0.0);
gl_FragColor = vec4(intensity, intensity, intensity, 1.0);
}
`
WebGL库对比
| 库 | 特点 | 适用场景 |
|---|---|---|
| Three.js | 功能丰富,抽象程度高 | 快速开发3D应用 |
| Babylon.js | 游戏引擎,功能强大 | 游戏开发 |
| PlayCanvas | 在线编辑器,云端渲染 | 协作开发 |
使用Three.js
import * as THREE from 'three'
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
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()
常见问题
问题1:WebGL上下文获取失败
解决方案:
- 确保浏览器支持WebGL
- 检查canvas元素是否存在
- 使用experimental-webgl作为备选
问题2:着色器编译失败
解决方案:
- 检查着色器语法
- 使用gl.getShaderInfoLog获取错误信息
- 确保precision声明正确
问题3:渲染结果不正确
解决方案:
- 检查矩阵设置
- 检查顶点数据
- 使用WebGL Inspector调试
总结
WebGL是前端3D图形渲染的核心技术。通过学习WebGL,我们可以:
- 实现高性能3D渲染:利用GPU加速
- 创建复杂视觉效果:光影、纹理、动画
- 开发交互式3D应用:游戏、数据可视化
现在,开始学习WebGL吧!你的用户会感谢你的!
最后一句忠告:从简单的三角形开始,逐步学习复杂的3D概念!

1万+

被折叠的 条评论
为什么被折叠?



