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

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

推荐资料

相关推荐
依旧天真无邪25 分钟前
Chrome 优质插件计划
前端·chrome
逝缘~37 分钟前
小白学Pinia状态管理
前端·javascript·vue.js·vscode·es6·pinia
光影少年40 分钟前
vite原理
前端·javascript·vue.js
C MIKE1 小时前
ztree.js前端插件样式文字大小文字背景修改
开发语言·前端·javascript
!win !1 小时前
uni-app项目怎么实现多服务环境切换
前端·uni-app
源猿人1 小时前
文化与代码的交汇:OpenCC 驱动的中文语系兼容性解决方案
前端·vue.js
xw51 小时前
uni-app项目怎么实现多服务环境切换
前端·uni-app
Kjjia1 小时前
到底是 react 性能拉胯?还是吃了机制的亏
前端·react.js
ViceBoy_1 小时前
前端的Promise的方法all,race,any
前端·javascript
飞翔的猪猪1 小时前
GitHub Recovery Codes - 用于 GitHub Two-factor authentication (2FA) 凭据丢失时登录账号
前端·git·github