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

推荐资料

相关推荐
伍哥的传说24 分钟前
鸿蒙系统(HarmonyOS)应用开发之手势锁屏密码锁(PatternLock)
前端·华为·前端框架·harmonyos·鸿蒙
yugi98783826 分钟前
前端跨域问题解决Access to XMLHttpRequest at xxx from has been blocked by CORS policy
前端
浪裡遊37 分钟前
Sass详解:功能特性、常用方法与最佳实践
开发语言·前端·javascript·css·vue.js·rust·sass
旧曲重听11 小时前
最快实现的前端灰度方案
前端·程序人生·状态模式
默默coding的程序猿2 小时前
3.前端和后端参数不一致,后端接不到数据的解决方案
java·前端·spring·ssm·springboot·idea·springcloud
夏梦春蝉2 小时前
ES6从入门到精通:常用知识点
前端·javascript·es6
归于尽2 小时前
useEffect玩转React Hooks生命周期
前端·react.js
G等你下课2 小时前
React useEffect 详解与运用
前端·react.js
我想说一句2 小时前
当饼干遇上代码:一场HTTP与Cookie的奇幻漂流 🍪🌊
前端·javascript
funnycoffee1232 小时前
Huawei 6730 Switch software upgrade example版本升级
java·前端·华为