当前位置: 首页 > news >正文

3.前置知识学习

此文章用于详细拆分讲解内容。

1.JavaScript 进阶

1.1 ES6+(箭头函数、解构、模块import/exportPromise/async/await

箭头函数:

  • 语法简洁:(参数) => 表达式 或 (参数) => { 语句 }
  • 没有自己的 this:继承自外层作用域的 this(解决了传统函数中 this 指向混乱的问题)
  • 不能用作构造函数(不能用 new 调用)
  • 没有 arguments 对象(可使用剩余参数 ...args 替代)
const add = (a, b) => a + b;const greet = (name) => {console.log(`Hello, ${name}!`);
};

解构:

允许从数组或对象中提取值,并赋值给变

数组:

const [a, b, c] = [1, 2, 3];
console.log(a); // 1
console.log(b); // 2// 跳过元素
const [x, , y] = [10, 20, 30];
console.log(x); // 10
console.log(y); // 30// 剩余参数
const [first, ...rest] = [1, 2, 3, 4];
console.log(rest); // [2, 3, 4]// 默认值
const [p = 0, q = 0] = [5];
console.log(q); // 0

 对象:

const user = { name: "Alice", age: 30, city: "Beijing" };// 基本用法
const { name, age } = user;
console.log(name); // "Alice"// 重命名变量
const { name: userName, age: userAge } = user;
console.log(userName); // "Alice"// 默认值
const { gender = "female" } = user;
console.log(gender); // "female"// 嵌套解构
const obj = { a: 1, b: { c: 2 } };
const { b: { c } } = obj;
console.log(c); // 2

模块(import/export)

将代码分割为独立的文件(模块),通过 import 和 export 共享功能。

导出(export)

// module.js
// 命名导出
export const PI = 3.14;
export function add(a, b) { return a + b; }// 默认导出
export default class Person {constructor(name) {this.name = name;}
}

导入(import)

// main.js
// 导入命名导出(需用大括号)
import { PI, add } from './module.js';
console.log(PI); // 3.14
console.log(add(2, 3)); // 5// 导入默认导出(无需大括号,可自定义名称)
import MyPerson from './module.js';
const person = new MyPerson("Bob");
console.log(person.name); // "Bob"// 重命名导入
import { add as sum } from './module.js';
console.log(sum(2, 3)); // 5// 导入所有(命名空间导入)
import * as MyModule from './module.js';
console.log(MyModule.PI); // 3.14

 Promise 与 async/await

用于处理异步操作,解决了传统回调函数嵌套("回调地狱")的问题。

Promise 是一个对象,表示异步操作的最终完成或失败,有三种状态:

 
  • pending(进行中)
  • fulfilled(已成功)
  • rejected(已失败)

async/await

是 Promise 的语法糖,让异步代码看起来像同步代码

  • async 关键字修饰函数,使其返回一个 Promise
  • await 关键字只能在 async 函数中使用,用于等待 Promise 完成
// 定义异步函数
async function handleData() {try {// 等待Promise完成const data = await fetchData; console.log(data); // "数据获取成功"const processedData = data + ",已处理";console.log(processedData);return processedData;} catch (error) {console.error(error); // 捕获失败} finally {console.log("操作完成");}
}// 调用异步函数(返回Promise)
handleData().then(result => {console.log("最终结果:", result);
});

1.2 异步编程(事件循环、回调地狱解决)

异步编程是 JavaScript 处理非阻塞操作的核心机制,尤其适用于网络请求、文件读写等耗时操作.

事件循环(Event Loop):JavaScript 异步的底层原理

JavaScript 是单线程语言(同一时间只能只能执行一个任务),但浏览器和 Node.js 通过事件循环实现了非阻塞异步操作。

执行流程

  1. 同步代码直接进入调用栈执行,执行完后出栈。
  2. 遇到异步操作(如 setTimeout),交给 Web API 处理,继续执行后续同步代码。
  3. 异步操作完成后,回调函数被放入任务队列(宏任务或微任务队列)。
  4. 当调用栈为空时,事件循环优先处理所有微任务,再处理一个宏任务,重复此过程。

任务类型

  • 宏任务(Macro Task)setTimeoutsetIntervalDOM事件fetch(网络请求完成后回调)、script 整体代码等。
  • 微任务(Micro Task)Promise.then/catch/finallyasync/await(本质是 Promise 语法糖)、queueMicrotask 等。
console.log("1"); // 同步代码,直接执行setTimeout(() => {console.log("2"); // 宏任务,放入宏任务队列
}, 0);Promise.resolve().then(() => {console.log("3"); // 微任务,放入微任务队列
});console.log("4"); // 同步代码,直接执行// 输出顺序:1 → 4 → 3 → 2

回调地狱如何解决可以看看第一条Promise 链式调用和async/await

1.3 DOM/BOM 操作(canvas 元素控制、窗口 resize 监听)

DOM 用于操作网页内容,BOM 用于控制浏览器窗口

3.1 DOM 操作核心:canvas 元素控制

<canvas> 是 HTML5 新增的绘图元素,通过 JavaScript 可在其上绘制图形、动画、图像等,广泛用于游戏、数据可视化等场景。

学习参考中文网站:https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API

基础使用步骤

<canvas id="myCanvas" width="400" height="300"></canvas>
// 1. 获取 canvas 元素
const canvas = document.getElementById('myCanvas');// 2. 获取绘图上下文(2D 绘图核心)
const ctx = canvas.getContext('2d');// 3. 绘制基本图形
// 绘制矩形(填充)
ctx.fillStyle = 'blue'; // 填充颜色
ctx.fillRect(50, 50, 100, 80); // x, y, 宽, 高// 绘制矩形(描边)
ctx.strokeStyle = 'red'; // 描边颜色
ctx.lineWidth = 3; // 线条宽度
ctx.strokeRect(200, 50, 100, 80);// 绘制圆形
ctx.beginPath(); // 开始路径
ctx.arc(150, 200, 40, 0, Math.PI * 2); // 圆心(x,y), 半径, 起始角, 结束角
ctx.fillStyle = 'green';
ctx.fill();
ctx.stroke();

高级应用:动画与图像处理

  • 动画原理:通过 requestAnimationFrame 循环重绘
let x = 0;
function animate() {// 清空画布ctx.clearRect(0, 0, canvas.width, canvas.height);// 绘制移动的圆ctx.beginPath();ctx.arc(x, 150, 30, 0, Math.PI * 2);ctx.fillStyle = 'purple';ctx.fill();x += 2;if (x > canvas.width) x = 0;requestAnimationFrame(animate); // 下一帧继续
}
animate();
  • 图像绘制:加载并绘制图片
const img = new Image();
img.src = 'image.jpg';
img.onload = () => {// 绘制完整图像ctx.drawImage(img, 0, 0);// 裁剪并绘制部分图像ctx.drawImage(img, 50, 50, 100, 100, 200, 200, 100, 100);// 参数:图像, 源图x, 源图y, 源图宽, 源图高, 目标x, 目标y, 目标宽, 目标高
};

扩展:canvas 实用场景

  • 数据可视化(绘制图表)
  • 网页截图(配合 html2canvas 库)
  • 像素级图像处理(滤镜效果)
  • 小游戏开发(如贪吃蛇、五子棋)

3.2 BOM 操作核心:窗口 resize 监听

窗口大小变化(resize)是常见的浏览器事件,通过监听该事件可实现响应式布局调整、画布自适应等功能。

基础监听方法

// 监听窗口大小变化
window.addEventListener('resize', handleResize);// 处理函数
function handleResize() {// 获取当前窗口尺寸const width = window.innerWidth;const height = window.innerHeight;console.log(`窗口尺寸:${width}x${height}`);// 示例:让 canvas 自适应窗口大小canvas.width = width * 0.8; // 画布宽度为窗口的80%canvas.height = height * 0.6;
}// 初始加载时执行一次
handleResize();

 优化:防抖处理

窗口 resize 事件会频繁触发(如拖拽窗口边缘时),可能导致性能问题,需用防抖(debounce) 优化。

function debounce(func, delay = 100) {let timer;return (...args) => {clearTimeout(timer);timer = setTimeout(() => func.apply(this, args), delay);};
}// 使用防抖后的处理函数
window.addEventListener('resize', debounce(handleResize));

扩展:相关 BOM 事件与属性

  • 窗口滚动(scroll):监听页面滚动位置
window.addEventListener('scroll', () => {const scrollTop = window.pageYOffset; // 滚动距离顶部的像素console.log('滚动位置:', scrollTop);
});
  • 窗口加载(load):页面资源全部加载完成后触发
window.addEventListener('load', () => {console.log('页面所有资源加载完成');
});
  • 视口尺寸:除 innerWidth/innerHeight 外
    • document.documentElement.clientWidth:可视区域宽度(不含滚动条)
    • screen.width:屏幕分辨率宽度

如何进行Canvas 性能优化?

优化方向具体手段核心原理适用场景注意事项
减少重绘频率1. 使用 requestAnimationFrame 代替 setInterval/setTimeout
2. 避免无意义的 clearRect + draw 循环
3. 批量执行绘制操作(如合并多帧绘制为一帧)
1. 与浏览器刷新频率(通常 60Hz,约 16.6ms / 帧)同步,避免丢帧
2. 减少不必要的画布清空和重复绘制,降低 GPU/CPU 负载
3. 减少绘制函数调用次数,降低函数执行开销
1. 所有需要动态刷新的 Canvas 场景(如动画、实时图表)
2. 绘制内容变化频率低的场景(如静态图表仅数据更新)
3. 多元素同时更新的场景(如粒子效果、多个图形同步移动)
1. requestAnimationFrame 在页面隐藏时会暂停,需处理页面切换逻辑
2. 批量绘制需控制单次绘制量,避免单帧耗时超过 16.6ms
3. 不要为了减少频率忽略用户交互的实时性(如鼠标跟随)
优化绘制内容1. 避免绘制不可见元素(如超出 Canvas 边界的内容)
2. 用 drawImage 绘制图片代替手动用图形 API 画复杂图案
3. 简化路径复杂度(如减少曲线控制点、合并重复路径)
1. 减少 GPU 无效渲染计算(不可见内容仍会消耗绘制资源)
2. 图片绘制由浏览器底层优化(比 Canvas API 手动绘制更高效)
3. 降低路径解析和渲染的计算量,减少 CPU 耗时
1. 元素频繁移动且可能超出画布的场景(如游戏角色、滚动图表)
2. 复杂图形 / 图标绘制(如自定义图标、装饰性图案)
3. 矢量图形绘制(如 SVG 转 Canvas、复杂折线图)
1. 判定元素可见性时,用简单边界检测(如矩形碰撞)代替精确检测
2. 图片绘制需提前加载,避免绘制时阻塞
3. 路径简化需平衡性能与视觉精度,避免图形失真
合理使用画布分层1. 将静态内容(如背景、固定图标)放在独立 Canvas 层
2. 动态内容(如动画、交互元素)单独分层
3. 按更新频率拆分多层 Canvas(高频层在上,低频层在下)
1. 静态层仅绘制一次,避免随动态内容重复重绘
2. 动态层重绘时仅影响自身,不干扰其他层
3. 减少单次重绘的画布面积,降低渲染开销
1. 混合静态与动态内容的场景(如带背景的实时仪表盘、游戏场景)
2. 多元素更新频率差异大的场景(如静态表格 + 动态数据标签)
3. 复杂交互场景(如鼠标 hover 高亮、拖拽元素)
1. 分层 Canvas 需通过 CSS 绝对定位叠加,确保坐标对齐
2. 避免过度分层(超过 3-5 层可能增加浏览器合成开销)
3. 低频层可设置 will-change: transform 提示浏览器优化
减少状态切换与 API 调用1. 批量设置样式(如统一设置 fillStyle 后绘制所有同色元素)
2. 复用路径(如用 save()/restore() 保存状态,避免重复设置)
3. 避免频繁切换 globalAlpha/globalCompositeOperation
1. 减少 Canvas 状态切换的内部开销(样式切换需重新初始化渲染参数)
2. 减少重复的状态配置代码,降低函数调用次数
3. 复杂混合模式会增加 GPU 计算量,频繁切换耗时更高
1. 多元素同样式的场景(如批量绘制同色矩形、同字体文本)
2. 需频繁切换状态的场景(如部分元素半透明、部分不透明)
3. 复杂图形组合场景(如多层叠加、裁剪绘制)
1. 批量绘制前先排序元素(按样式 / 状态分组),减少跨组切换
2. save()/restore() 需成对使用,避免状态污染
3. 非必要时避免使用 globalCompositeOperation,优先用分层替代
硬件加速与资源控制1. 避免 Canvas 尺寸过大(建议单画布像素不超过 4096*4096)
2. 用 transform 代替 offsetLeft/offsetTop 移动画布
3. 避免频繁读取 Canvas 像素数据(如 getImageData/toDataURL
1. 过大画布会超出 GPU 纹理缓存限制,触发 CPU 渲染(性能骤降)
2. transform 由 GPU 加速,而样式修改会触发重排重绘
3. 像素数据读取需从 GPU 显存拷贝到 CPU 内存,属于高开销操作
1. 大尺寸 Canvas 场景(如全景图绘制、高分辨率图表)
2. 画布整体移动的场景(如滚动视图、地图平移)
3. 需像素级操作的场景(如图片滤镜、颜色检测)
1. 大画布可拆分为多个小画布拼接(如地图瓦片)
2. 使用 transform 时,避免同时修改 width/height(会重置变换矩阵)
3. 像素读取需批量执行,避免每帧多次调用,可缓存结果复用

2.数学基础

2.1  线性代数(向量运算:加减 / 点积 / 叉积;矩阵变换:平移 / 旋转 / 缩放;坐标系转换)

2.1.1向量运算:从 “箭头” 开始理解

向量(Vector)是线性代数的 “基本单位”,可以理解为 “有方向和长度的箭头”(比如物理中的 “力”“速度”)。在坐标系中,向量用一组数字表示(如 2D 向量(x, y)、3D 向量(x, y, z)),这组数字对应箭头在各坐标轴上的 “投影长度”。

2.1.1.1 向量的表示:先搞懂 “位置” 与 “方向”
  • 向量 vs 点

(Point):表示位置(在坐标系中 “在哪里”);向量(Vector):表示方向和长度(“从哪里到哪里” 的变化),:点P(2,3)是坐标系中一个固定位置;向量v=(2,3)可以理解为 “从原点出发,向右走 2,向上走 3” 的箭头,也可以平移到任意位置(比如从点(1,1)出发,指向(3,4))。

  • 向量的写法:

通常用加粗字母(如v)或带箭头的字母(如$\vec{v}$)表示,2D 向量记为v = (v₁, v₂),3D 向量记为v = (v₁, v₂, v₃),其中v₁, v₂, v₃是向量在 x、y、z 轴上的 “分量”

2.1.1.2 向量加减:“箭头拼接” 或 “分量对应算”

向量加减的核心是 “分量对应相加 / 减”,也可以用 “箭头拼接” 的直观方式理解(适合 2D/3D 场景)

  • 向量加法:v + w

计算规则:两个向量的 “对应分量相加”

v = (x₁, y₁)w = (x₂, y₂),则v + w = (x₁+x₂, y₁+y₂)

3D 向量同理:v = (x₁,y₁,z₁)w = (x₂,y₂,z₂),则v + w = (x₁+x₂, y₁+y₂, z₁+z₂)

直观理解(三角形法则):把向量w的起点平移到向量v的终点,从v的起点指向w的终点的箭头,就是v + w

例:你先向右走 2、向上走 3(向量v=(2,3)),再向右走 1、向上走 2(向量w=(1,2)),最终位置变化就是(2+1, 3+2)=(3,5),对应向量v+w=(3,5)

  • 向量减法:v - w

计算规则:两个向量的 “对应分量相减”,本质是v + (-w)-ww的 “反方向向量”,分量全取负)。

v=(2,3)w=(1,2),则v - w = (2-1, 3-2)=(1,1)-w=(-1,-2)v + (-w)=(2-1,3-2)=(1,1),结果一致。

直观理解v - w表示 “从w的终点指向v的终点” 的向量,也可以理解为 “两个向量起点重合时,从w指向v的箭头”。

例:若v是 “从原点到 (2,3)”,w是 “从原点到 (1,2)”,则v - w是 “从 (1,2) 到 (2,3)” 的向量,分量为(1,1)

2.1.1.3 向量点积:判断 “方向相似度”

点积是向量的 “重要乘法”,结果不是向量,而是一个数字(标量),核心作用是:判断两个向量的 “方向是否相近”、计算向量长度、求夹角。

  • 计算规则:分量相乘再求和

    • 2D 向量:若v=(x₁,y₁)w=(x₂,y₂),则点积v·w = x₁x₂ + y₁y₂
    • 3D 向量:若v=(x₁,y₁,z₁)w=(x₂,y₂,z₂),则点积v·w = x₁x₂ + y₁y₂ + z₁z₂
    • 例:v=(2,3)w=(1,2),则v·w = 2×1 + 3×2 = 2 + 6 = 8
  • 核心意义:方向与长度的关联

    • 点积的公式还可以表示为:v·w = |v| × |w| × cosθ
    • v|是向量v的 “长度”(模长),计算方式是√(x₁² + y₁²)(2D)或√(x₁² + y₁² + z₁²)(3D);
    • θvw之间的夹角(起点重合时)
    • 结论
      • 判断方向是否相近

        • v·w > 0cosθ > 0θ < 90°,两个向量 “大致同向”(比如v=(1,0)w=(1,1),点积 = 1>0,夹角 45°);
        • v·w = 0cosθ = 0θ = 90°,两个向量 “垂直”(比如 x 轴和 y 轴方向向量,点积 = 0);
        • v·w < 0cosθ < 0θ > 90°,两个向量 “大致反向”(比如v=(1,0)w=(-1,1),点积 =-1<0,夹角 135°)。
      • 计算向量长度:当w = v时,v·v = |v|² × cos0° = |v|²,因此|v| = √(v·v)(这就是模长的计算公式)。

      • 计算夹角:已知两个向量的点积和模长,可反求夹角θ = arccos( (v·w) / (|v|×|w|) )(常用于图形学中 “光照计算”,判断光线与物体表面的夹角)。

2.1.1.4 向量叉积:判断 “左右方向” 或 “法向量”

叉积仅在3D 向量中定义(2D 叉积可理解为 “3D 叉积的 z 分量”),结果是一个向量,核心作用是:判断两个向量的 “左右位置关系”、生成 “垂直于平面的法向量”(用于 3D 图形的面朝向判断)。

  • 计算规则:行列式展开(记结论更简单)
    • 对于 3D 向量v=(x₁,y₁,z₁)w=(x₂,y₂,z₂),叉积v×w的结果是一个新向量,计算公式为:
      v×w = ( y₁z₂ - y₂z₁ , z₁x₂ - z₂x₁ , x₁y₂ - x₂y₁ )

    • 例:v=(1,0,0)(x 轴正方向),w=(0,1,0)(y 轴正方向),则v×w = (0×0 - 1×0, 0×0 - 0×1, 1×1 - 0×0) = (0, 0, 1)(z 轴正方向);
      w=(0,1,0)v=(1,0,0),则w×v = (1×0 - 0×0, 0×1 - 0×0, 0×1 - 1×0) = (0, 0, -1)(z 轴负方向)

核心意义:方向与面积

叉积的向量方向遵循 “右手定则”,且模长等于 “以两个向量为邻边的平行四边形面积”:

  1. 方向判断(右手定则)

    • 伸出右手,让四指从v的方向弯曲到w的方向(弯曲角度小于 180°),此时大拇指指向的方向就是v×w的方向。
    • 例:x 轴向量 ×y 轴向量,四指从 x 弯向 y,大拇指指向 z 轴正方向;y 轴向量 ×x 轴向量,四指从 y 弯向 x,大拇指指向 z 轴负方向(因此叉积不满足 “交换律”,v×w = -w×v)。
  2. 2D 场景的 “左右判断”
    2D 向量可看作 “z 分量为 0 的 3D 向量”,此时叉积的结果只有 z 分量(x、y 分量为 0),可通过 z 分量的正负判断两个向量的左右关系:

    • 无序列表若v×w的 z 分量 > 0:wv的 “左侧”;
    • v×w的 z 分量 < 0:wv的 “右侧”;
    • 若 z 分量 = 0:vw“共线”(方向相同或相反)。
    • 例:v=(1,0)(向右),w=(0,1)(向上),叉积 z 分量 = 1×1 - 0×0=1>0,因此wv左侧;w=(0,-1)(向下),叉积 z 分量 = 1×(-1) - 0×0=-1<0,因此wv右侧。
  3. 模长与面积|v×w| = |v| × |w| × sinθθvw的夹角),这个值恰好等于 “以vw为邻边的平行四边形的面积”(3D 图形中 “三角形面积” 是其一半)。

2.1.2 矩阵变换:用 “表格” 操作向量

矩阵(Matrix)是一个 “数字表格”,比如 2×2 矩阵(2 行 2 列)、3×3 矩阵、4×4 矩阵。在 linear algebra 中,矩阵的核心作用是 “变换向量”—— 把一个向量输入矩阵,输出一个 “被拉伸、旋转、平移后的新向量”(常用于图形学中 “物体变形”“视角移动”)。

  •  矩阵与向量的乘法:核心运算

要让矩阵变换向量,必须先做 “矩阵 × 向量” 的运算(注意:向量要写成 “列向量” 形式,即 1 列多行,比如 2D 向量写成$\begin{pmatrix}x \\ y\end{pmatrix}$)。

(1)2×2 矩阵 × 2D 向量(无平移,仅旋转 / 缩放)

假设矩阵M = $\begin{pmatrix} a & b \\ c & d \end{pmatrix}$,向量v = $\begin{pmatrix} x \\ y \end{pmatrix}$,则乘法结果Mv是一个新向量:
Mv = $\begin{pmatrix} a×x + b×y \\ c×x + d×y \end{pmatrix}$

  • 计算规则:矩阵的 “行” 与向量的 “列” 对应相乘再求和,结果作为新向量的 “行”。

  • 例:M = $\begin{pmatrix} 2 & 0 \\ 0 & 3 \end{pmatrix}$v = $\begin{pmatrix} 1 \\ 2 \end{pmatrix}$,则Mv = $\begin{pmatrix} 2×1 + 0×2 \\ 0×1 + 3×2 \end{pmatrix}$ = $\begin{pmatrix} 2 \\ 6 \end{pmatrix}$(向量在 x 轴拉伸 2 倍,y 轴拉伸 3 倍)。

(2)4×4 矩阵 × 3D 向量(含平移,完整变换)

2×2/3×3 矩阵无法实现 “平移”(因为向量乘法是 “线性运算”,M(0) = 0,无法把原点平移到其他点),因此在 3D 图形学中,通常用4×4 矩阵(齐次坐标)来包含 “平移、旋转、缩放” 三种变换,此时 3D 向量要写成 “4 维齐次向量”(x, y, z, 1)(最后一位固定为 1,代表 “点”;若为 0,则代表 “方向向量”,平移对其无效)。

4×4 矩阵的标准变换形式为:
M = $\begin{pmatrix} a & b & c & t_x \\ d & e & f & t_y \\ g & h & i & t_z \\ 0 & 0 & 0 & 1 \end{pmatrix}$
其中:

  • 左上角 3×3 矩阵$\begin{pmatrix} a & b & c \\ d & e & f \\ g & h & i \end{pmatrix}$:负责旋转和缩放

  • 第 4 列前 3 个元素(t_x, t_y, t_z):负责平移(把点沿 x 轴移t_x,y 轴移t_y,z 轴移t_z);

  • 最后一行(0,0,0,1):保持齐次坐标的 “点 / 方向” 属性(固定不变)。

4×4 矩阵 × 4D 齐次向量的计算规则:
若向量v = (x, y, z, 1),则Mv = (a x + b y + c z + t_x, d x + e y + f z + t_y, g x + h y + i z + t_z, 1)

  • 例:平移矩阵M = $\begin{pmatrix} 1 & 0 & 0 & 5 \\ 0 & 1 & 0 & 3 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}$(仅平移,无旋转缩放),向量v=(2,4,1,1),则Mv = (1×2+0×4+0×1+5, 0×2+1×4+0×1+3, 0×2+0×4+1×1+0, 1) = (7,7,1,1)(点从 (2,4,1) 平移到 (7,7,1))。

  • 三种核心矩阵变换:平移、旋转、缩放

(1)缩放变换(Scale):拉伸或压缩向量

  • 作用:让向量在 x/y/z 轴上按比例放大或缩小(比如把物体 “变胖”“变高”)。

  • 4×4 缩放矩阵形式(s_x=x 轴缩放比例,s_y=y 轴,s_z=z 轴):
    M_scale = $\begin{pmatrix} s_x & 0 & 0 & 0 \\ 0 & s_y & 0 & 0 \\ 0 & 0 & s_z & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}$

  • 例:s_x=2(x 轴拉伸 2 倍),s_y=0.5(y 轴压缩一半),s_z=1(z 轴不变),则矩阵为$\begin{pmatrix} 2 & 0 & 0 & 0 \\ 0 & 0.5 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}$
    输入向量(1, 4, 3, 1),输出为(2×1, 0.5×4, 1×3, 1) = (2, 2, 3, 1)(x 从 1→2,y 从 4→2,z 不变)。

(2)旋转变换(Rotate):绕坐标轴转动向量

  • 作用:让向量绕 x/y/z 轴旋转指定角度θ(右手定则:四指沿旋转方向,大拇指指向坐标轴正方向)。

  • 常见旋转矩阵(4×4 形式,仅列出关键部分,其他元素同标准变换矩阵):

    1. 绕 x 轴旋转θ:y/z 轴分量变化,x 轴不变
      M_rot_x = $\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & cosθ & -sinθ & 0 \\ 0 & sinθ & cosθ & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}$

    2. 绕 y 轴旋转θ:x/z 轴分量变化,y 轴不变
      M_rot_y = $\begin{pmatrix} cosθ & 0 & sinθ & 0 \\ 0 & 1 & 0 & 0 \\ -sinθ & 0 & cosθ & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}$

    3. 绕 z 轴旋转θ:x/y 轴分量变化,z 轴不变
      M_rot_z = $\begin{pmatrix} cosθ & -sinθ & 0 & 0 \\ sinθ & cosθ & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}$

  • 例:绕 z 轴旋转 90°(θ=90°cos90°=0sin90°=1),矩阵为$\begin{pmatrix} 0 & -1 & 0 & 0 \\ 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}$
    输入向量(1, 0, 0, 1)(x 轴正方向),输出为(0×1 + (-1)×0, 1×1 + 0×0, 0, 1) = (0, 1, 0, 1)(旋转后指向 y 轴正方向,符合 “绕 z 轴转 90°” 的直观效果)。

(3)平移变换(Translate):移动向量位置

  • 作用:把向量(点)沿 x/y/z 轴移动指定距离(比如把物体从左边移到右边)。

  • 4×4 平移矩阵形式(t_x=x 轴平移距离,t_y=y 轴,t_z=z 轴):
    M_trans = $\begin{pmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \end{pmatrix}$

  • 例:t_x=3(向右移 3),t_y=-2(向下移 2),t_z=1(向前移 1),矩阵为$\begin{pmatrix} 1 & 0 & 0 & 3 \\ 0 & 1 & 0 & -2 \\ 0 & 0 & 1 & 1 \\ 0 & 0 & 0 & 1 \end{pmatrix}$
    输入向量(2, 5, 0, 1),输出为(2+3, 5-2, 0+1, 1) = (5, 3, 1, 1)(点从 (2,5,0) 移到 (5,3,1))。

  •  变换的组合:矩阵乘法

如果要对一个向量做 “先缩放→再旋转→最后平移”,不需要单独计算三次,而是把三个变换矩阵相乘,得到一个 “组合矩阵”,再用组合矩阵 × 向量即可(一次计算完成所有变换)。

(1)矩阵乘法规则(以 4×4 矩阵为例)

假设矩阵AB都是 4×4 矩阵,组合矩阵C = A × B(注意顺序:先应用B,再应用A,因为向量是 “右乘” 矩阵,Cv = A(Bv)),则C的每个元素C[i][j]等于A的第i行与B的第j列对应元素相乘再求和。

(2)变换顺序的重要性

矩阵乘法不满足交换律,即A×B ≠ B×A,因此变换顺序会直接影响结果:

  • 例:“先平移再旋转” vs “先旋转再平移”:

    1. 先平移(M_trans)再旋转(M_rot):组合矩阵M_rot × M_trans

    2. 先旋转(M_rot)再平移(M_trans):组合矩阵M_trans × M_rot

    这两个组合矩阵完全不同,比如:先把点 (1,0,0) 平移到 (1,0,0),再绕 z 轴转 90°,结果是 (0,1,0);而先绕 z 轴转 90°(点 (1,0,0)→(0,1,0)),再平移到 (1,0,0),结果是 (1,1,0),显然不同。

2.1.3 坐标系转换:从 “你的视角” 到 “我的视角”

在 3D 场景中,我们会遇到多个坐标系(比如 “物体自己的坐标系”“世界坐标系”“相机视角坐标系”),坐标系转换的核心是 “找到两个坐标系之间的变换矩阵”,把向量从一个坐标系转换到另一个坐标系。

先搞懂三个常用坐标系


  • 1. 局部坐标系(Local Space):物体 “自己的坐标系”,原点通常在物体中心(比如一个立方体的中心),x 轴向右、y 轴向上、z 轴向前(方便描述物体自身的顶点位置,比如 “立方体的右上角顶点在 (1,1,1)”)。

  • 2. 世界坐标系(World Space):整个场景的 “公共坐标系”,原点在场景的某个固定位置(比如场景中心),所有物体的位置都相对于这个坐标系(比如 “立方体在世界坐标系中的位置是 (5,0,3)”)。

  • 3. 相机坐标系(View Space):相机 “视角的坐标系”,原点在相机位置,z 轴指向相机 “看的方向”,y 轴指向相机 “上方”(方便计算 “物体在相机视野中的位置”,比如 “物体在相机前方 5 米处”)。

坐标系转换的核心:“原点偏移”+“轴方向对齐”


任何两个坐标系之间的转换,都可以拆成两步:

  1. 平移:把原坐标系的原点,移动到目标坐标系的原点(抵消原点偏移);

  2. 旋转 / 缩放:把原坐标系的 x/y/z 轴,旋转 / 缩放到与目标坐标系的 x/y/z 轴对齐(统一轴方向和单位)。

(1)局部坐标系 → 世界坐标系(模型变换)

  • 目的:把物体局部坐标系中的顶点,转换到世界坐标系中(确定物体在场景中的位置和姿态)。

  • 转换过程:局部顶点先经过 “缩放→旋转”(调整物体大小和姿态),再经过 “平移”(把物体放到世界坐标系的指定位置),对应组合矩阵M_model = M_trans × M_rot × M_scale(顺序:先缩放,再旋转,最后平移)。

  • 例:一个立方体的局部顶点v_local=(1,1,1,1)(右上角顶点),若:

    • 缩放:s_x=s_y=s_z=2(放大 2 倍),M_scale

    • 旋转:绕 y 轴转 90°,M_rot

    • 平移:t_x=5, t_y=0, t_z=3(移到世界坐标系 (5,0,3) 处),M_trans
      则世界坐标系中的顶点v_world = M_model × v_local = M_trans × M_rot × M_scale × v_local

(2)世界坐标系 → 相机坐标系(视图变换)

  • 目的:把世界坐标系中的物体,转换到相机坐标系中(确定物体相对于相机的位置,比如 “物体在相机左边还是右边”)。

  • 转换逻辑:相机坐标系可以看作 “世界坐标系中一个特殊的物体”—— 因此,把世界坐标转换到相机坐标,等价于 “把相机坐标系移动到世界坐标系的原点,并让相机的轴与世界轴对齐”,对应矩阵M_view(视图矩阵)。

  • M_view的计算步骤:

    1. 平移:把世界坐标系的原点,平移到相机的位置(即 “减去相机的世界坐标”,矩阵M_trans_camt_x=-cam_x, t_y=-cam_y, t_z=-cam_z);

    2. 旋转:把相机的 x/y/z 轴,旋转到与世界坐标系的 x/y/z 轴对齐(比如相机朝 z 轴负方向看,需要旋转 180°,矩阵M_rot_cam);

    3. 组合矩阵:M_view = M_rot_cam × M_trans_cam(先平移,再旋转)。

总结:


  • 先玩工具,再记公式:用可视化工具(如GeoGebra)手动拖动向量、矩阵,观察变换效果。

  • 如果是为了学图形学 / 游戏开发,优先掌握 “4×4 矩阵变换” 和 “坐标系转换”

  • 如果是为了机器学习,优先掌握 “向量点积”(用于计算相似度、线性回归)

  • 零基础阶段不用深究 “线性空间”“秩” 等抽象概念,先会用 “向量运算”“矩阵变换” 解决具体问题(比如用点积判断光照强度,用矩阵旋转一个正方形),后续再回头补理论。

2.2  三角函数(正弦 / 余弦:控制旋转、角度计算)

三角函数是数学中描述 “角” 与 “边” 关系的核心工具,尤其正弦(sin)和余弦(cos),在图形旋转、角度计算等场景中应用极广.

2.2.1 先搞懂两个核心前提:直角三角形与单位圆

直角三角形


  • 直角:90° 的角(用 “⊥” 标记),是三角形中最大的角;

  • 锐角:除直角外的两个角(都小于 90°),我们研究的 “角” 就是指其中一个锐角(记为 θ,读作 “西塔”);

  • 三边:根据与锐角 θ 的位置关系,分为 3 类(见下图):

    • 对边:只与 θ 相对,不包含 θ 的那条边(记为 a);

    • 邻边:包含 θ 且与直角相邻的边(记为 b);

    • 斜边:最长的边,始终与直角相对(记为 c,斜边长度>对边 / 邻边)。

正弦(sin)和余弦(cos)的定义(直角三角形版)


正弦和余弦本质是 “直角三角形中,某条边与斜边的比值”,只和 “角的大小” 有关,和三角形的 “实际大小” 无关(比如同样 30° 角,小三角形和大三角形的 sin30° 比值完全相同)。

函数定义(与锐角 θ 的关系)公式记忆口诀
正弦 sin对边长度 / 斜边长度sinθ = 对边 / 斜边 = a/c对正(对边对应正弦)
余弦 cos邻边长度 / 斜边长度cosθ = 邻边 / 斜边 = b/c余邻(邻边对应余弦)

单位圆:突破直角三角形,走向旋转应用


什么是单位圆?

  • 圆心在坐标原点 (0,0),半径 = 1 的圆(“单位” 就是指半径为 1);

  • 圆上任意一点的坐标记为 (x,y),根据圆的方程,满足x² + y² = 1(因为半径 = 1)。

正弦 / 余弦的定义(单位圆版):核心!核心!核心!

将 “角 θ” 与单位圆结合:让角的顶点在原点,角的始边(起始边)与 x 轴正方向重合,角的终边(结束边)与单位圆交于点 P (x,y)
此时,正弦和余弦的定义直接与点 P 的坐标挂钩:

  • sinθ = 点 P 的纵坐标 = y

  • cosθ = 点 P 的横坐标 = x

这个定义彻底打破了 “直角三角形” 的限制,能描述任意角度(0°~360°、负角、大于 360° 的角),也是 “三角函数控制旋转” 的理论基础。

不同象限角的 sinθ、cosθ 正负(快速判断)

象限角的范围(0°~360°)点 P 坐标 (x,y)sinθ(y)cosθ(x)
第一象限0°~90°正,正
第二象限90°~180°负,正
第三象限180°~270°负,负
第四象限270°~360°正,负

记忆口诀:“一全正,二正弦,三正切,四余弦”(只看正的情况:第一象限全正,第二象限只有正弦正,第四象限只有余弦正).

必背的特殊角度值:不用计算,直接用

角度 θ30°45°60°90°180°270°360°
sinθ01/2√2/2≈0.707√3/2≈0.86610-10
cosθ1√3/2≈0.866√2/2≈0.7071/20-101

规律总结:

  • sinθ:0°→90° 递增(0→1),90°→180° 递减(1→0),180°→270° 递减(0→-1),270°→360° 递增(-1→0);

  • cosθ:0°→90° 递减(1→0),90°→180° 递减(0→-1),180°→270° 递增(-1→0),270°→360° 递增(0→1)。

用正弦 / 余弦控制旋转(图形学基础)


这是正弦 / 余弦最经典的应用之一(比如游戏角色旋转、机器人关节转动、图像旋转),核心原理是 “通过角度计算旋转后的坐标”。

旋转的基本场景:点绕原点旋转

假设平面上有一个点 A (x₀,y₀),现在要让它绕坐标原点 (0,0) 顺时针旋转 θ 角,得到新点 A'(x',y'),如何计算 x' 和 y'?
答案就是用余弦和正弦推导的 “旋转公式”(这个公式不用推导,直接记应用即可):

  • 新横坐标:x' = x₀×cosθ + y₀×sinθ

  • 新纵坐标:y' = -x₀×sinθ + y₀×cosθ

如果是逆时针旋转,公式只需调整符号:

  • x' = x₀×cosθ - y₀×sinθ

  • y' = x₀×sinθ + y₀×cosθ

图形旋转的本质:

一个图形由无数个点组成,只要对图形上每一个点都用旋转公式计算出新坐标,再把这些新点连接起来,就能得到旋转后的图形 —— 这就是电脑、手机里 “图形旋转功能” 的底层数学逻辑,而正弦和余弦就是其中的 “核心工具”。

2.3 坐标系(2D→3D 的右手坐标系、屏幕坐标系)

坐标系是描述 “位置” 的数学工具,就像生活中用 “街道 + 门牌号” 定位住址一样,在数学、图形学(游戏、动画)、计算机视觉里,所有点的位置都要靠坐标系定义。下面从 0 基础出发,先讲简单的 2D 坐标系,再过渡到 3D 右手坐标系,最后讲和我们屏幕直接相关的屏幕坐标系,每个概念都配 “生活类比” 和 “画图指引”,保证能懂。

2.3.1 基础:2D 坐标系(平面坐标系)

2D 坐标系是 “在平面上定位点” 的工具,比如在纸上画点、手机屏幕上点图标,本质都是用 2D 坐标描述位置。最常用的是笛卡尔 2D 坐标系(也叫直角坐标系)

3 个核心要素

  • 原点(Origin):坐标的 “起点”,记为 (0, 0)。可以理解为 “小区大门”—— 所有位置都从这里开始算。

  • 坐标轴(Axes):2 条互相垂直的直线,用来确定 “方向”:

    • 水平方向的轴叫 x 轴(x-axis),通常向右为 “正方向”(可以类比 “东方向”);

    • 垂直方向的轴叫 y 轴(y-axis),通常向上为 “正方向”(可以类比 “北方向”);

  • 单位长度(Unit Length):衡量 “距离” 的标准,比如 1 单位 = 1 厘米(纸上)或 1 像素(屏幕上)。

怎么用 2D 坐标表示一个点?


每个点的位置用 (x, y) 两个数字表示,规则很简单:

  • x 值:表示这个点到 “y 轴” 的水平距离(平行于 x 轴)——x 正,在 y 轴右边;x 负,在 y 轴左边;

  • y 值:表示这个点到 “x 轴” 的垂直距离(平行于 y 轴)——y 正,在 x 轴上方;y 负,在 x 轴下方。

2.3.2 进阶到 3D:右手坐标系(3D 的 “标准语言”)

2D 是 “平面”,3D 是 “空间”(比如房间、游戏里的 3D 场景)—— 要描述空间中一个点的位置,需要在 2D 的 x、y 轴基础上,加一条z 轴,形成 3 个互相垂直的轴,这就是 3D 坐标系。

而 “右手坐标系” 是 3D 领域的通用规则(游戏、CAD、3D 建模都用它),核心是 “确定 z 轴的正方向”—— 用右手就能快速记住,不用死记硬背。

 右手坐标系的 “手型判断法”(关键!

拿出你的右手,按照下面 3 步做,就能直观理解 x、y、z 轴的方向关系:

  1. 让右手手掌摊平,拇指指向右方(这是 x 轴的正方向,和 2D 的 x 轴一致);

  2. 让食指向上伸直(和拇指垂直),这是 y 轴的正方向(也和 2D 的 y 轴一致);

  3. 此时,你的中指会自然向 “自己的方向” 弯曲 / 伸直(和拇指、食指都垂直),这就是 z 轴的正方向!

简单总结:右手拇指 = x 正,食指 = y 正,中指 = z 正,三个轴两两垂直 —— 这就是右手坐标系的核心定义。

3D 坐标怎么表示?——(x, y, z)


和 2D 类似,3D 空间中每个点的位置用 (x, y, z) 三个数字表示,分别对应三个轴的 “距离”:

  • x:平行于 x 轴,到 y-z 平面的距离(x 正 = 右,x 负 = 左);

  • y:平行于 y 轴,到 x-z 平面的距离(y 正 = 上,y 负 = 下);

  • z:平行于 z 轴,到 x-y 平面的距离(z 正 = 靠近自己,z 负 = 远离自己)

为什么必须是 “右手” 而不是 “左手”?


因为如果用左手(左手拇指 = x 正,食指 = y 正,中指 = z 正),z 轴的方向会和右手系相反(左手的中指会指向 “远离自己” 的方向)—— 如果大家不统一用 “右手”,同样一个 3D 模型,你看是 “向前”,别人看就会是 “向后”,会乱套。

比如游戏里的角色 “向前走”,在右手系里是 z 轴正方向;如果用左手系,角色就会 “向后退”—— 所以行业必须统一用右手坐标系。

2.3.3 和屏幕相关:屏幕坐标系(2D,但方向特殊)

前面讲的 2D 坐标系是 “数学标准”,但屏幕坐标系是 “设备标准” —— 它是专门用来描述 “屏幕上像素位置” 的坐标系,和我们平时用手机、电脑直接相关,有个关键特点:y 轴方向和数学 2D 坐标系相反。

屏幕坐标系的核心特点(和数学 2D 的区别)


对比项数学 2D 坐标系(笛卡尔)屏幕坐标系(设备)
原点 (0,0)通常在平面中心固定在屏幕左上角
x 轴正方向向右向右(和数学一致)
y 轴正方向向上向下(和数学相反!)
单位长度厘米、米等像素(Pixel)

为什么屏幕坐标系的 y 轴向下?


这是历史原因:早期计算机屏幕(如 CRT 显示器)的 “扫描方式” 是从 “左上角” 开始,先向右扫一行(x 轴),扫完一行后 “向下移一行”(y 轴),再扫下一行 —— 为了和硬件扫描逻辑一致,屏幕坐标系就把 y 轴正方向设为 “向下”,一直沿用至今。

生活中的屏幕坐标系例子


  • 手机屏幕(分辨率 1080×2340):

    • 左上角是 (0, 0);

    • 右上角是 (1079, 0)(因为 x 轴最大是 “分辨率宽度 - 1”,像素从 0 开始计数);

    • 左下角是 (0, 2339)(y 轴最大是 “分辨率高度 - 1”);

    • 如果你点击屏幕 “中间位置”,坐标大概是 (540, 1170)。

  • 电脑游戏:比如《我的世界》里,你点击屏幕上的 “方块”,游戏就是通过 “屏幕坐标系” 获取你点击的 (x, y) 像素位置,再换算成 3D 场景中的方块位置。

2.3.4 总结:3 个坐标系的核心区别

坐标系类型维度原点位置轴方向(正方向)单位核心用途
数学 2D 坐标系2D通常在平面中心x = 右,y = 上厘米、米等数学计算、画图(如几何题)
3D 右手坐标系3D可自定义(如场景中心)x = 右,y = 上,z = 靠近自己米、厘米等3D 建模、游戏、CAD 设计
屏幕坐标系2D屏幕左上角x = 右,y = 下像素屏幕定位(图标、点击位置)

3.图形学基础

3.1 计算机图形学核心概念(像素、纹理、着色器、渲染管线、光照模型)

计算机图形学本质是 “把数字变成图像” 的技术,比如游戏画面、电影特效、3D 建模都依赖它。

3.1.1 像素(Pixel):图像的 “最小积木”

像素是计算机图形的最基本单位,就像乐高积木 —— 再复杂的图像(照片、游戏画面、UI 界面),放大到极致后都是由无数个 “小方块” 组成的,每个小方块就是一个像素。

核心特点

  • 有固定位置:每个像素在屏幕上有唯一的 “坐标”(比如手机屏幕的 “第 100 行、第 200 列”),对应现实中 “第几排、第几列” 的位置。

  • 有固定颜色:每个像素只能显示一种颜色,颜色由 “红(R)、绿(G)、蓝(B)” 三种基础色混合而成(比如纯红色是 R=255、G=0、B=0),有些像素还会加 “透明度(A)”(比如半透明的按钮)。

  • 数量决定清晰度:屏幕 / 图像的 “分辨率” 本质就是像素总数。比如 “1920×1080” 表示画面横向有 1920 个像素、纵向有 1080 个像素,总像素数约 200 万;像素越多,画面越清晰(比如 4K 屏幕比 1080P 屏幕清晰,就是因为像素更多)。

3.1.2 纹理(Texture):给 3D 模型 “贴皮肤”

你可以把纹理理解为3D 模型的 “贴纸” 或 “皮肤”。比如游戏里的 “木质桌子”,3D 模型本身只是一个 “光秃秃的长方体”,要让它看起来像木头,就需要把一张 “木纹图片” 贴在长方体表面 —— 这张 “木纹图片” 就是纹理。

核心作用:

  • 让 3D 模型更真实:没有纹理的模型是 “纯色块”(比如纯灰色的桌子),贴了纹理后能呈现细节(木纹、划痕、图案)。

  • 降低模型复杂度:如果不用纹理,要做出木纹的凹凸感,需要给模型加无数个小面(增加计算量);用纹理只需一张图,既省资源又逼真。

常见类型:

  • 2D 纹理:最常用的 “图片贴纸”,比如墙面的壁纸、人物的衣服图案。

  • 法线纹理(Normal Map):特殊的 “凹凸贴纸”—— 看起来像模型有凸起(比如砖墙的凹陷),但实际模型还是平的,靠纹理欺骗眼睛,常用于游戏(既真实又不耗性能)。

3.1.3 着色器(Shader):图像的 “化妆师”

着色器是一段控制 “如何显示像素 / 模型” 的小程序,相当于给图像 / 3D 模型做 “化妆” 的工具:比如决定某个像素该显示什么颜色、模型表面该亮还是暗、要不要加光影效果。

核心分类(2 个最关键的着色器)


类型作用类比
顶点着色器(Vertex Shader)处理 3D 模型的 “顶点”(比如长方体的 8 个角点):计算每个顶点在屏幕上的位置(比如模型移动后,顶点坐标怎么变)、传递顶点的基础信息(比如顶点的颜色、纹理坐标)。给 “骨架” 定位置 —— 比如拍电影时,先确定演员的关节(顶点)在哪里,再调整姿势。
片段着色器(Fragment Shader)处理 “片段”(可以理解为 “像素的前身”):根据顶点着色器传递的信息,计算每个片段的最终颜色(比如结合纹理、光照,决定这个像素是木纹色还是阴影色)。给 “皮肤” 上颜色 —— 比如根据演员的服装(纹理)、灯光(光照),决定脸上每个部位该涂什么色。

为什么重要?

所有你看到的 “酷炫效果”(比如游戏里的光影、电影里的爆炸特效、手机拍照的滤镜),本质都是着色器在工作。比如 “模糊滤镜” 就是片段着色器修改了每个像素的颜色,让相邻像素颜色更接近;“金属反光” 就是片段着色器计算了光线在金属表面的反射效果。

3.1.4 渲染管线(Rendering Pipeline):图像的 “生产流水线”

渲染管线是将 “3D 模型、纹理、光照” 等原始数据,一步步变成屏幕上 2D 图像的流程,就像工厂里 “从原材料到成品” 的流水线 —— 每个步骤只做一件事,依次传递处理。

核心步骤(简化版,从 3D 到 2D 的 7 个关键环节):


  1. 顶点输入:把 3D 模型的 “顶点数据”(位置、颜色、纹理坐标)输入管线(相当于把 “积木零件” 送到流水线起点)。

  2. 顶点着色器:处理每个顶点,计算其在屏幕上的 2D 位置(比如把 3D 的 “长方体角点” 转换成屏幕上的 “像素坐标”)。

  3. 图元装配:把处理后的顶点 “连成面”(比如把长方体的 8 个顶点,拼成 6 个矩形面),这些 “面” 叫 “图元”(三角形、矩形等)。

  4. 光栅化:把 “图元”(比如一个矩形面)拆分成无数个 “片段”(即对应屏幕上的像素位置,相当于把 “大积木” 拆成 “小像素碎片”)。

  5. 片段着色器:计算每个片段的最终颜色(结合纹理、光照,比如给这个片段涂成木纹色,或因为在阴影里涂成深色)。

  6. 逐片段操作:做 “最终检查”—— 比如判断这个片段是否在屏幕内(不在就丢弃)、是否被其他物体遮挡(被遮挡就不显示)、是否需要半透明混合。

  7. 像素输出:把最终确定的颜色 “画” 到屏幕对应的像素上,所有像素拼起来就是最终的 2D 图像。

3.1.5 光照模型(Lighting Model):让 3D 世界 “有亮有暗”

光照模型是模拟 “光线如何与物体交互” 的规则,目的是让 3D 模型看起来有 “立体感”—— 没有光照的话,所有物体都是 “纯色块”,分不清正面和背面;有了光照,就能看到明暗差异(比如阳光照到的面亮,背面暗)。

3 个最基础的光照模型(从简单到真实):

  • 环境光(Ambient Light):“全局普照的光”,比如房间里的日光灯 —— 所有物体都会被均匀照亮,没有明暗差异,只能让物体 “不发黑”,无法体现立体感(相当于给所有物体涂一层 “基础色”)。

  • 漫反射光(Diffuse Light):“光线照射到粗糙表面的反射”,比如阳光照在墙上 —— 光线会向各个方向散开,物体 “朝向光线的面亮,背对光线的面暗”,能体现基本的立体感(比如长方体的正面亮、背面暗)。

    • 关键特点:只和 “物体表面与光线的夹角” 有关 —— 夹角越小(面越正对着光线),越亮。

  • 镜面反射光(Specular Light):“光线照射到光滑表面的反射”,比如阳光照在镜子、金属上 —— 会形成 “亮斑”(高光),能体现物体的 “光滑度”(比如金属杯子的高光比木头桌子明显)。

    • 关键特点:和 “观察角度” 有关 —— 只有当你的眼睛在 “光线反射方向” 上时,才能看到高光。

3.1.6 核心关联图

3D模型(顶点组成) → 贴纹理(穿皮肤) → 进入渲染管线(流水线)↓                      ↓光照模型(算明暗)    顶点着色器(定位置)→ 片段着色器(算颜色)↓                      ↓最终像素(有颜色、有明暗) → 屏幕显示2D图像

3.2 基础渲染流程(顶点→图元→光栅化→片元→屏幕像素)

渲染流程是将 “虚拟图形” 转化为 “屏幕可见像素” 的核心过程,就像 “把设计图做成实物” 的流水线。

渲染的本质


渲染的目标是把 3D/2D 空间中的 “几何形状”,转化为屏幕上一个个发光的 “像素点”
比如你在游戏里看到的 “一个立方体”,最终会变成屏幕上几百个红色、蓝色的小方块(像素)—— 这个 “立方体→像素” 的转化过程,就是下面要讲的 5 步渲染流程。

3.2.1 顶点输入(Vertex Input):定义 “形状的骨架”

收集 “顶点数据”,这些顶点是构成图形的最基础 “锚点”,就像搭建房子前先确定 “墙角、房梁端点” 的位置。

  • 顶点:只有 “位置信息” 的点(比如 3D 空间中的 X/Y/Z 坐标,2D 空间中的 X/Y 坐标),没有大小和颜色。

  • 一个图形至少需要 3 个顶点(比如三角形,这是计算机最擅长处理的基础图形);复杂图形(比如立方体)由多个三角形拼接而成,需要更多顶点(立方体有 8 个顶点)

技术细节

  • 数据形式:顶点数据通常是 “数组”(比如[x1,y1,z1, x2,y2,z2, x3,y3,z3]),存储在计算机内存中。

  • 核心作用:告诉计算机 “图形的轮廓在哪里”,是后续所有步骤的 “数据基础”。

实例

  • 画一个 2D 三角形:需要输入 3 个顶点的坐标,比如(100,200)(300,200)(200,100)

  • 画一个 3D 立方体:需要输入 8 个顶点的 3D 坐标(比如(0,0,0)(0,1,0)(1,1,0)...),每个顶点代表立方体的一个角。

3.2.2 图元装配(Primitive Assembly):把 “骨架” 拼成 “基础形状”

将第一步输入的 “零散顶点”,按照规则 “连接成完整的基础图形”(计算机里叫 “图元”),最常用的图元是三角形(偶尔也用点、线段)。

  • 为什么用三角形?因为三角形是 “最稳定的多边形”—— 无论怎么旋转、缩放,它的三个顶点始终能确定一个唯一的平面,不会出现 “变形” 或 “歧义”,计算机处理起来最高效。

类比理解

拼乐高时,你把刚才确定的 “连接点”(顶点)用 “乐高板块”(图元)连接起来,比如用 3 个连接点拼出一个三角形板块,再用多个三角形板块拼出立方体的一个面。

技术细节

  • 装配规则:计算机按 “顶点输入顺序” 分组(比如每 3 个顶点组成一个三角形),或按 “索引” 指定分组(比如用[0,1,2]表示 “用第 0、1、2 个顶点拼三角形”)。

  • 核心作用:把 “点” 变成 “可识别的形状”,为后续 “填充颜色” 做准备。

实例

  • 2D 三角形:3 个顶点(100,200)(300,200)(200,100)被装配成一个完整的三角形图元。

  • 3D 立方体:8 个顶点先被分成 6 个面(每个面是 2 个三角形),共 12 个三角形图元,最终拼成立方体的轮廓。

3.2.3 光栅化(Rasterization):把 “基础形状” 拆成 “像素格子”

将第二步拼好的 “图元(比如三角形)”,映射到 “屏幕的像素网格” 上,确定 “这个图元会覆盖屏幕上哪些像素”—— 就像给形状 “打格子”,标出需要上色的像素位置。

  • 注意:这一步只确定 “哪些像素要处理”,还不给像素上色,相当于 “确定快递要送到哪些小区”,但还没装货

类比理解

你要在一张方格纸上画三角形:先确定三角形的轮廓,然后用铅笔圈出 “三角形内部包含的所有小方格”—— 这些小方格就是 “要处理的像素”。

技术细节

  • 核心计算:计算机通过 “扫描线算法”(从图元顶部扫到底部),逐行判断每个像素是否在图元内部,最终输出一个 “像素列表”(包含所有需要上色的像素坐标)。

  • 抗锯齿(可选):如果图元边缘的像素 “半在内部、半在外部”,会标记为 “半透明像素”,避免边缘出现 “锯齿状”(比如游戏里的 “抗锯齿” 功能就是干这个的)。

实例

  • 2D 三角形:假设三角形覆盖屏幕上的(101,201)(102,201)(101,202)等 100 个像素,光栅化后就输出这 100 个像素的坐标。

  • 3D 立方体:每个三角形面都会被光栅化,最终输出立方体在屏幕上 “投影后” 覆盖的所有像素坐标(比如总共 1000 个像素)。

3.2.4 片元着色(Fragment Shading):给 “像素格子” 上色

对第三步光栅化输出的 “每个像素”(这里叫 “片元”,因为可能包含透明、深度等额外信息),计算它的 “最终颜色”—— 这是渲染中 “最能决定视觉效果” 的一步,比如纹理、光影、颜色都在这里处理。

类比理解

你给刚才圈出的 “方格纸小方格” 上色:有的方格涂红色(比如立方体的正面),有的涂深红色(比如立方体的阴影部分),有的涂蓝色(比如反射的天空)—— 每个方格的颜色都是单独计算的。

技术细节

  • 输入信息:每个片元会携带 “自身坐标”“所在图元的顶点颜色 / 纹理坐标”“光照信息” 等数据。

  • 核心计算:通过 “片元着色器”(一段小程序)执行计算,比如:

    • 颜色插值:如果三角形的三个顶点分别是红、绿、蓝,中间的像素颜色会 “渐变过渡”(红→黄→绿→青→蓝)。

    • 纹理映射:把图片(比如 “木头纹理”)贴到图元上,计算每个像素对应的纹理位置,取图片上的颜色作为像素颜色。

    • 光照计算:根据光源位置,判断像素是否在阴影里,调整颜色亮度(比如阴影部分颜色变暗)。

  • 输出结果:每个片元最终会得到一个 “RGBA 颜色值”(比如(255,0,0,255)表示不透明的红色)。

实例

  • 2D 三角形:假设顶点颜色是红、绿、蓝,片元着色后,三角形中心的像素颜色会是 “白色”(红 + 绿 + 蓝混合),边缘像素是红色、绿色或蓝色。

  • 3D 立方体:给立方体贴 “木头纹理”,片元着色时会根据每个像素在立方体上的位置,取木头纹理图片中对应的颜色,同时计算 “阳光照射角度”,让朝向阳光的面更亮,背对阳光的面更暗。

3.2.5 逐片元操作(Per-Fragment Operations):确定 “最终显示哪些像素”

对第四步输出的 “带颜色的片元”,做最后一轮筛选和处理,最终决定 “哪些片元能显示在屏幕上”,并写入 “帧缓冲区”(屏幕的 “临时画布”)。

类比理解

你把上色后的方格纸 “整理成最终画面”:

  • 如果两个方格重叠(比如立方体的正面挡住背面),只保留 “前面的方格”(背面的方格不显示)。

  • 如果有的方格是半透明的(比如玻璃),就把它的颜色和 “下面的方格颜色混合”(比如玻璃的蓝色 + 下面的红色 = 紫色)。

技术细节

  • 核心操作(按顺序执行):

    1. 深度测试:判断片元是否被 “更靠前的物体” 挡住(比如立方体正面的片元会挡住背面的片元),被挡住的片元直接丢弃,不显示。

    2. 模板测试(可选):按 “模板缓冲区” 的规则筛选片元(比如只在屏幕的某个区域显示图形,像游戏里的 “小地图”)。

    3. 混合:如果片元是透明的(RGBA 中的 A 值 <255),就把它的颜色和 “帧缓冲区中已有的颜色” 混合(比如半透明的玻璃会透出后面的物体)。

    4. 写入帧缓冲区:通过所有测试的片元,最终会把颜色写入 “帧缓冲区”—— 当一帧所有像素都处理完后,显示器会读取帧缓冲区的内容,显示到屏幕上。

实例

  • 3D 立方体:正面的红色片元通过深度测试,写入帧缓冲区;背面的蓝色片元被正面挡住,深度测试失败,直接丢弃;如果立方体有一个半透明的玻璃面,玻璃的蓝色片元会和后面的红色片元混合,变成紫色后写入帧缓冲区。

  • 游戏画面:玩家看到的 “角色 + 场景 + UI”,都是通过这一步判断 “谁在前、谁在后、谁透明”,最终合成一帧完整画面。


文章转载自:

http://Exo2StBT.dgmjm.cn
http://mRgaREMD.dgmjm.cn
http://CcdUvfIX.dgmjm.cn
http://ViHXnOt9.dgmjm.cn
http://XGE2DY2H.dgmjm.cn
http://w8qVA5yy.dgmjm.cn
http://PXukkyE7.dgmjm.cn
http://OmDoSFpc.dgmjm.cn
http://YYXmUws7.dgmjm.cn
http://eO67PFtJ.dgmjm.cn
http://daFauWMc.dgmjm.cn
http://RiFavjjT.dgmjm.cn
http://aqhPPj0H.dgmjm.cn
http://fUCFZ2N4.dgmjm.cn
http://vUHtGgrr.dgmjm.cn
http://ezgJWOAi.dgmjm.cn
http://fiYBD978.dgmjm.cn
http://ySCnnyIh.dgmjm.cn
http://Tfv7A6Ze.dgmjm.cn
http://P7pgQBOq.dgmjm.cn
http://c8I9Kbb2.dgmjm.cn
http://fuNKiWHS.dgmjm.cn
http://sHj1aEWx.dgmjm.cn
http://J598Qp0F.dgmjm.cn
http://vXWLhzUa.dgmjm.cn
http://pNAT5xGC.dgmjm.cn
http://Jp72xNwT.dgmjm.cn
http://ylZrQTC8.dgmjm.cn
http://hZlDbJTc.dgmjm.cn
http://Ihwu0L8z.dgmjm.cn
http://www.dtcms.com/a/379166.html

相关文章:

  • Whois查询域名信息
  • 机器学习vs人类学习:人类学习如何借鉴机器学习方法?
  • ES6 面试题及详细答案 80题 (41-54)-- 异步编程(Promise/Generator/async)
  • Bug记录:Lombok @Builder 注解的两大陷阱及解决方案
  • ARM汇编 beep及bsp工程管理
  • 深入理解 Vue3 Router:三种路由模式的工作原理与实战应用
  • 2025 ICPC Gran Premio de Mexico 3ra Fecha
  • ZLMediaKit性能测试
  • 使用PyQt5和NumPy从TXT文件读取平面点集数据
  • nacos1.3.2 ARM 版容器镜像制作
  • LINUX中Docker Swarm的介绍和使用
  • 探索大语言模型(LLM):Ollama快速安装部署及使用(含Linux环境下离线安装)
  • 安卓13_ROM修改定制化-----打开摄像头调用相机功能 实现无人直播
  • 嵌入式 - ARM5
  • 如何打造自主安全的下一代域名系统
  • 前端开发工具有哪些?常用前端开发工具、前端调试工具、前端构建工具与效率提升工具对比与最佳实践
  • 机器学习1.Anaconda安装+环境配置
  • GrapeCity Documents V8.0 Update2 重磅发布:性能飞跃、AI 赋能与文档处理全流程升级
  • 【软考架构-案例分析】质量属性场景描述6要素
  • IBMS智能化集成系统:构建建筑全场景协同管控中枢
  • 【高级】系统架构师 | 2025年上半年综合真题DAY4
  • 系统接口故障排查
  • MyBatis框架(编写代码部分1)
  • mes之工序管理
  • P4053 [JSOI2007] 建筑抢修
  • Unity Embedded Browser文档翻译
  • 阻容感专题学习笔记
  • ARM指令集(Instruction Set)细节
  • 28.线程互斥与同步(二)
  • 批量修改图片尺寸大小的免费工具