WebGL入门教程:实现场景中相机的视角与位置移动

WebGL入门教程:实现场景中相机的视角与位置移动

本文旨在为WebGL初学者提供一个详细的相机实现与移动控制的入门指南。我们将一步步用原生WebGL实现一个可以自由移动视角与位置的三维相机,适用于创建三维场景或小游戏。


项目准备

1. 环境准备

  • 浏览器(推荐 Chrome / Edge)
  • 文本编辑器(推荐 VSCode)
  • 引入辅助库:glMatrix (矩阵运算库)
xml 复制代码
<script src="https://cdn.jsdelivr.net/npm/gl-matrix@2.8.1/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/gl-matrix@2.8.1/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

推荐资料

相关推荐
wearegogog1235 小时前
基于 MATLAB 的卡尔曼滤波器实现,用于消除噪声并估算信号
前端·算法·matlab
Drawing stars5 小时前
JAVA后端 前端 大模型应用 学习路线
java·前端·学习
品克缤5 小时前
Element UI MessageBox 增加第三个按钮(DOM Hack 方案)
前端·javascript·vue.js
小二·5 小时前
Python Web 开发进阶实战:性能压测与调优 —— Locust + Prometheus + Grafana 构建高并发可观测系统
前端·python·prometheus
小沐°5 小时前
vue-设置不同环境的打包和运行
前端·javascript·vue.js
qq_419854056 小时前
CSS动效
前端·javascript·css
烛阴6 小时前
3D字体TextGeometry
前端·webgl·three.js
桜吹雪6 小时前
markstream-vue实战踩坑笔记
前端
C_心欲无痕7 小时前
nginx - 实现域名跳转的几种方式
运维·前端·nginx
花哥码天下7 小时前
恢复网站console.log的脚本
前端·javascript·vue.js