WebGL入门教程:实现场景中相机的视角与位置移动
本文旨在为WebGL初学者提供一个详细的相机实现与移动控制的入门指南。我们将一步步用原生WebGL实现一个可以自由移动视角与位置的三维相机,适用于创建三维场景或小游戏。
项目准备
1. 环境准备
- 浏览器(推荐 Chrome / Edge)
- 文本编辑器(推荐 VSCode)
- 引入辅助库:glMatrix (矩阵运算库)
xml
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gl-matrix-min.js"></script>
2. 项目结构
css
webgl-camera/
│
├── index.html
└── main.js
教程目标
- 创建一个基础3D WebGL场景
- 实现相机的位置控制(前后左右移动)
- 实现视角控制(鼠标旋转视角)
一步步构建你的相机系统
🔹 Step 1:创建基本WebGL场景
index.html
xml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebGL 相机控制</title>
<style>
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
</style>
</head>
<body>
<canvas id="glcanvas"></canvas>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gl-matrix-min.js"></script>
<script src="main.js"></script>
</body>
</html>
Step 2:初始化WebGL上下文
main.js(前半部分)
ini
const canvas = document.getElementById("glcanvas");
const gl = canvas.getContext("webgl");
if (!gl) {
alert("你的浏览器不支持 WebGL");
}
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gl.viewport(0, 0, canvas.width, canvas.height);
Step 3:设置基础着色器 & 绘制一个立方体
此处省略立方体的完整创建(可另行模块化封装),重点放在相机处理上。如果你需要,可以加我提醒,我提供完整立方体绘制代码。
Step 4:定义相机参数与矩阵
ini
let cameraPosition = vec3.fromValues(0, 0, 5); // 初始位置
let cameraFront = vec3.fromValues(0, 0, -1); // 朝向
let cameraUp = vec3.fromValues(0, 1, 0); // 上方向
let yaw = -90; // 左右旋转
let pitch = 0; // 上下旋转
let lastX = canvas.width / 2;
let lastY = canvas.height / 2;
let firstMouse = true;
let deltaTime = 0;
let lastFrame = 0;
Step 5:计算视图矩阵(view matrix)
ini
function getViewMatrix() {
let center = vec3.create();
vec3.add(center, cameraPosition, cameraFront);
let view = mat4.create();
mat4.lookAt(view, cameraPosition, center, cameraUp);
return view;
}
Step 6:添加鼠标控制视角旋转
ini
canvas.addEventListener('mousemove', (e) => {
if (firstMouse) {
lastX = e.clientX;
lastY = e.clientY;
firstMouse = false;
}
let xoffset = e.clientX - lastX;
let yoffset = lastY - e.clientY;
lastX = e.clientX;
lastY = e.clientY;
const sensitivity = 0.1;
xoffset *= sensitivity;
yoffset *= sensitivity;
yaw += xoffset;
pitch += yoffset;
if (pitch > 89.0) pitch = 89.0;
if (pitch < -89.0) pitch = -89.0;
const front = vec3.create();
front[0] = Math.cos(glMatrix.toRadian(yaw)) * Math.cos(glMatrix.toRadian(pitch));
front[1] = Math.sin(glMatrix.toRadian(pitch));
front[2] = Math.sin(glMatrix.toRadian(yaw)) * Math.cos(glMatrix.toRadian(pitch));
vec3.normalize(cameraFront, front);
});
Step 7:添加键盘控制移动位置
ini
let keys = {};
window.addEventListener("keydown", e => keys[e.key] = true);
window.addEventListener("keyup", e => keys[e.key] = false);
function processInput() {
const cameraSpeed = 2.5 * deltaTime;
let temp = vec3.create();
if (keys["w"]) {
vec3.scale(temp, cameraFront, cameraSpeed);
vec3.add(cameraPosition, cameraPosition, temp);
}
if (keys["s"]) {
vec3.scale(temp, cameraFront, cameraSpeed);
vec3.sub(cameraPosition, cameraPosition, temp);
}
if (keys["a"]) {
let cross = vec3.create();
vec3.cross(cross, cameraFront, cameraUp);
vec3.normalize(cross, cross);
vec3.scale(cross, cross, cameraSpeed);
vec3.sub(cameraPosition, cameraPosition, cross);
}
if (keys["d"]) {
let cross = vec3.create();
vec3.cross(cross, cameraFront, cameraUp);
vec3.normalize(cross, cross);
vec3.scale(cross, cross, cameraSpeed);
vec3.add(cameraPosition, cameraPosition, cross);
}
}
Step 8:主循环绘制并更新相机
ini
function render(now) {
now *= 0.001; // ms 转 s
deltaTime = now - lastFrame;
lastFrame = now;
processInput();
gl.clearColor(0.1, 0.1, 0.1, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const view = getViewMatrix();
// 使用 view 矩阵传入着色器
// gl.uniformMatrix4fv(viewLoc, false, view);
// drawCube(); // 你可以在此绘制立方体或其他物体
requestAnimationFrame(render);
}
requestAnimationFrame(render);
效果演示
通过鼠标移动视角、键盘 WASD
控制相机移动,可以在WebGL场景中自由游走,非常适合用于游戏、建模、可视化等。
总结与拓展
本文要点:
- WebGL中没有"相机对象",但我们通过构造 view 矩阵来模拟相机视角;
- 使用
mat4.lookAt()
实现视角观察; - 使用 glMatrix 库进行矩阵运算;
- 鼠标 + 键盘组合控制相机视角与移动。
可以拓展的内容:
- 添加透视投影矩阵(
mat4.perspective
) - 限制相机移动范围
- 引入第三人称/第一人称切换
- 封装相机类 CameraController