Computer Graphics · Chapter 1 · 03

图形绘制系统概述

CPU、图形 API、GPU 与绘制流水线:从模型数据到屏幕像素

CPU / Application
→
Graphics API
→
GPU Pipeline
→
Framebuffer / Display
Bridge-in · 0–4 min

CPU 和 GPU 为什么要分工?

CPU 与 GPU 图形系统

上一课停在这里

我们已经知道一幅图像来自对象 + 相机 + 光 / 材质。

现在的问题是:这些描述怎样真正变成屏幕像素?

Learning goals

今天要把“从模型到像素”的黑盒打开

01

系统分工

解释 CPU、Graphics API 与 GPU 的角色。

02

绘制流水线

描述顶点、图元、片元到像素的核心步骤。

03

Demo验证

用同一个多模型场景观察每个阶段的输入与输出。

互动提问 1 / 4 · Pre-assessment

给 GPU 三个顶点,为什么最终会出现一个三角形?

几何数据

const points = new Float32Array([
 -0.5, -0.5, 0.0,
  0.5, -0.5, 0.0,
  0.0,  0.5, 0.0
]);

三个顶点只描述几何位置。

屏幕需要的却是

大量离散位置上的像素颜色

  • 顶点在哪里?
  • 这三个点组成什么图元?
  • 图元覆盖哪些屏幕采样位置?
  • 最终颜色是什么?
想一想1 min

从“顶点数组”到“屏幕像素”,你认为至少需要经过哪三步?先写关键词,不求准确。

System architecture · 4–9 min

图形绘制体系结构:三层协作

Application / CPU

  • 响应输入、更新场景状态
  • 组织几何、纹理和材质资源
  • 决定何时提交绘制工作

Graphics API / Driver

  • 定义应用如何访问 GPU
  • 管理资源、状态、管线与命令
  • 为不同软硬件提供标准化接口

GPU

  • 大规模并行处理顶点与片元
  • 执行光栅化、测试与混合
  • 把结果写入帧缓冲
Data & operations

CPU 向 GPU 传递两类东西:数据 + 操作

Data · 要画什么

  • 顶点位置
  • 颜色 / 法向量
  • 纹理坐标
  • 索引、纹理、材质参数……

Operations · 怎样处理

  • 模型怎样放置
  • 相机怎样观察
  • 怎样投影、裁剪
  • 怎样着色、测试、混合……

整个图形体系结构,就是为了高效地传递数据和定义对数据的操作。

Why GPU?

为什么图形计算天然适合 GPU?

2.07 M

1920 × 1080 像素 / 帧

124 M

约 1.24 亿像素 / 秒 @ 60 FPS

× many ops

变换、插值、纹理、光照、深度、混合……

大量元素执行相似计算 → 高吞吐并行处理 → GPU

Demo · 课堂观察工具

Demo:多模型绘制流水线逐阶段可视化

D02|绘制流水线逐阶段可视化

这个 Demo 用一个包含立方体、棱锥和三角面板的场景,连续观察每个阶段的输入与输出。

  • 可调整模型旋转、相机绕场景旋转
  • 可改变裁剪窗口位置与宽度
  • 可观察片元生成、遮挡关系与深度测试
打开 Demo
Demo route

Demo观察路线:不要逐页机械点击,要围绕三个问题看

①坐标如何变化CPU数据 → Model → View → Projection
②哪些几何被保留Primitive Assembly → Clipping → NDC → Viewport
③哪些片元最终可见Rasterize → Fragment → Depth → Framebuffer

下面的概念讲解与 Demo 观察一一对应:先看结果,再解释为什么。

Pipeline overview · 9–15 min

GPU 内部不是一个步骤,而是一条绘制流水线

图形绘制流水线

同一种处理要作用于海量顶点和片元,因此流水线 + 并行能够提高吞吐量。

Pipeline map

先记住“对象经历了什么”,暂时不追公式

01Vertex Input顶点与属性进入GPU
→
02Vertex Shader处理每个顶点
→
03Assembly / Clip组成图元、保留可见部分
→
04Rasterization图元 → 片元
→
05Fragment Shader计算片元输出
→
06Tests / Blend决定是否写入缓冲

可编程

Vertex Shader、Fragment Shader 是本课程最直接控制的核心阶段。

固定 / 配置

组装、光栅化、深度测试、混合等主要由硬件与管线状态高效执行。

互动提问 2 / 4 · Demo观察

坐标变换:相机转动时,世界坐标改变了吗?

Demo操作

  1. 打开 Demo,选择阶段 模型变换 Model。
  2. 调整“所选模型额外旋转”,观察世界坐标。
  3. 切换到 观察变换 View,调整“相机绕场景旋转”。

课堂提问

旋转相机后,模型的世界坐标是否改变?如果没有,为什么画面看起来发生了变化?

观察目标2 min

区分 Model 矩阵改变的是对象姿态,View 矩阵改变的是观察方式。

Vertex processing · 15–22 min

顶点处理:同一个点,要经历多个坐标系统

顶点处理过程

核心任务

  • 对象坐标 → 世界坐标
  • 世界坐标 → 相机坐标
  • 相机坐标 → 投影 / 裁剪空间
  • 传递颜色、法向量、纹理坐标等属性

每个坐标变换都可以表示成矩阵运算。

Demo思考问题

在 Demo 的 Model / View / Projection 阶段中,同一个顶点的数值在哪一步开始因为“相机”而改变?这个改变说明了什么?

Programmable stage

Vertex Shader:每个顶点独立执行一段小程序

输入

顶点位置以及随顶点携带的属性。

例如:颜色、法向量、纹理坐标。

典型输出

gl_Position = projection
            * view
            * model
            * position;

最终必须产生用于后续裁剪和光栅化的位置。

互动提问 3 / 4 · Demo观察

裁剪:哪些几何被保留,哪些被舍弃?

Demo操作

  1. 切换到 图元装配,观察三角形由哪些顶点组成。
  2. 切换到 裁剪 Clipping。
  3. 调整“裁剪窗口中心”和“裁剪窗口宽度”,必要时勾选“将面板推向裁剪边界”。

课堂提问

一个三角形跨越窗口边界时,是整个三角形被删除,还是只保留可见部分?这一步为什么不能等到最后像素阶段再做?

观察目标2 min

理解图元装配、裁剪、透视除法、视口变换共同完成“可见范围到屏幕范围”的转换。

Demo思考问题

调窄裁剪窗口时,跨越边界的三角形是整体消失,还是被截出新的可见部分?这一步的输出仍然是三角形吗?

Primitive assembly

三个顶点只是三个点:还必须说明它们怎样组成图元

POINTS

三个顶点 → 三个点

LINES

顶点按规则组成线段

TRIANGLES

三个顶点 → 一个三角形

同一组顶点数据,可以因为图元类型不同而产生不同几何。

Clipping · NDC · Viewport

从“可见空间”到“屏幕窗口”

01Clipping在裁剪空间中保留可见部分
→
02Perspective Dividex/w, y/w, z/w 得到 NDC
→
03ViewportNDC 映射到屏幕窗口坐标
→
04Rasterize准备转换为离散片元
互动提问 4 / 4 · Demo观察

片元、深度与帧缓存:Fragment 一定会显示吗?

Demo操作

  1. 切换到 光栅化 Rasterize,观察候选片元。
  2. 切换到 片元着色 Fragment,观察片元颜色。
  3. 切换到 深度测试 Depth,降低“模拟绘制前已有深度”。

课堂提问

片元着色器算出的颜色是否一定进入屏幕?如果两个模型投影到同一个位置,最终由什么决定谁可见?

观察目标2 min

建立关键区分:Fragment 是候选结果,Pixel 是最终写入帧缓存的结果。

Demo思考问题

切换到深度测试阶段并降低已有深度值:片元着色器已经算出的颜色为什么可能没有写入帧缓冲?

Rasterization

光栅化:把连续几何覆盖转换成离散片元

输入

几何图元

例如屏幕空间中的一个三角形。

输出

Fragments

哪些采样位置被图元覆盖?各属性在这些位置是多少?

Triangle
→
Rasterizer
→
Many fragments
Interpolation

片元为什么能得到颜色、深度、纹理坐标?

顶点只给出端点属性

A 红色、B 绿色、C 蓝色;或者三个顶点各自具有纹理坐标、深度等。

光栅化阶段进行插值

三角形内部每个片元获得连续变化的属性值,交给片元着色器继续处理。

Fragment processing

Fragment Shader:决定“这个片元输出什么”

输入可能包括

  • 插值颜色 / 法向量
  • 纹理坐标
  • 材质与光源参数
  • 其他自定义数据

典型输出

outColor = texture(tex, uv)
         * lighting;

最常见的输出是颜色,但现代 API 可以写入多个渲染目标。

Tests · Blend · Framebuffer

Fragment ≠ Pixel:它还必须通过最后一组“筛选”

Fragment候选结果
→
Depth / Stencil是否被遮挡?是否允许写入?
→
Blend与已有颜色怎样组合?
→
Framebuffer最终像素及深度等缓冲
Graphics API · 31–35 min

从程序员角度看,图形系统首先表现为一个 API

图形 API 与系统关系

API 把高层概念映射到系统能力

  • 对象 / 几何
  • 观察者 / 相机
  • 光源与材质
  • 纹理、缓冲、着色器
  • 绘制命令与系统状态
Object description

对象怎样进入 API?——最终仍要落到可处理的图元

0D

Point

点

1D

Line

线段

2D

Triangle

多边形基础

Higher

Curve / Surface

最终常被离散为简单图元处理

GPU 最喜欢规则、简单、数量巨大的数据;三角形因此成为实时图形最重要的基本图元。

From old OpenGL to WebGL

对象描述方式的演进:从“立即给顶点”到“把数组交给 GPU”

早期 OpenGL · Immediate Mode

glBegin(GL_TRIANGLES);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
glEnd();

概念直观,但已不是现代 OpenGL / WebGL 的主流组织方式。

现代 GPU 数据方式

const points = new Float32Array([
  0.0, 0.0, 0.0,
  0.0, 1.0, 0.0,
  0.0, 0.0, 1.0
]);
// 上传到 GPU buffer

批量存储数据,再用 draw call 告诉 GPU 如何解释与绘制。

Camera description

相机怎样进入 API?——把上一课的概念变成参数

相机参数

典型需要指定

  • 位置与方向
  • 上方向 / 相机姿态
  • FOV 或投影参数
  • 宽高比
  • Near / Far 裁剪范围

于是“相机”最终也变成 GPU 可执行的矩阵与参数。

API evolution · 35–37 min

WebGL 仍是学习主线,WebGPU 是必须认识的新方向 2026

WebGL 2

  • 教材 8E 的主要实验环境
  • 适合学习经典可编程图形流水线
  • 浏览器中广泛可用
→

WebGPU

  • 面向现代 GPU 架构的 Web API
  • 同时支持 Render + Compute
  • 资源、管线和命令组织更显式

WebGPU 当前由 W3C 推进;本课程仍以 WebGL 2 作为掌握经典图形学原理的主要实验工具。

What stays stable?

API 会变,但哪些知识不会轻易过时?

数据

顶点、纹理、缓冲

坐标

变换、观察、投影

流水线

顶点 → 图元 → 片元

并行思维

大量元素执行相似计算

Summary · 37–40 min

本课内容总结:从应用程序到屏幕像素

01

系统分工

CPU 组织任务,API 提供接口,GPU 高并行执行。

02

几何阶段

顶点变换、投影、图元组装与裁剪。

03

像素阶段

光栅化产生片元,着色、测试后写入帧缓冲。

04

Demo验证

用同一场景观察每个阶段的输入与输出。

Chapter 1 map

第一章最终地图:三节课连成一条完整链

Scene / Model
→
Camera + Light / Material
→
Application / API
→
GPU Pipeline
→
Framebuffer
→
Image

从下一章开始:真正让第一个三角形出现在浏览器里。