第一部分:计算机图形学核心原理
计算机图形学是研究如何在计算机中表示、生成、处理和显示图像的学科,其核心可以概括为:将数学模型和算法转化为屏幕上可见的像素。

(图片来源网络,侵删)
基础概念与管线
这是理解整个图形学流程的基石。
-
图形渲染管线:将三维场景信息转换成二维屏幕图像的一系列处理阶段,可以想象成一个工厂的流水线。
- 应用程序阶段:开发者处理逻辑、碰撞检测、准备场景数据(如模型、光照等)。
- 几何阶段:在GPU上进行,主要处理三维几何体。
- 模型变换:将模型顶点从模型坐标系转换到世界坐标系(把一个茶杯放到桌子上)。
- 视图变换:将世界坐标系的物体转换到摄像机(眼睛)坐标系(移动摄像机或场景)。
- 投影变换:将三维坐标投影到二维裁剪空间,并产生深度信息,主要分为:
- 透视投影:模拟人眼效果,近大远小,有灭点,这是最常用的。
- 正交投影:没有近大远小的效果,常用于CAD、UI等。
- 裁剪:将视野范围之外的物体“剪掉”,不进行后续处理。
- 透视除法:将齐次坐标转换成标准化设备坐标。
- 光栅化阶段:将处理过的几何图形(如三角形)转换成屏幕上的像素。
- 视口变换:将标准化设备坐标映射到屏幕窗口的实际像素坐标。
- 三角形遍历:确定哪些像素在三角形内部。
- 片段着色:为每个像素计算最终的颜色(考虑光照、纹理等)。
- 像素操作阶段:深度测试、模板测试、混合等,最终将颜色写入帧缓冲区。
-
帧缓冲区:存储屏幕上所有像素颜色数据的内存区域,显卡不断将帧缓冲区中的内容显示到屏幕上(这个过程叫显示或Present)。
数学基础
图形学本质上是数学的艺术。

(图片来源网络,侵删)
- 向量:表示方向和位置,核心运算:加法、减法、点积(判断角度、计算光照)、叉积(计算垂直向量、判断朝向)。
- 矩阵:表示变换(平移、旋转、缩放),任何变换都可以用一个矩阵来表示,多个变换可以通过矩阵乘法组合成一个矩阵。
- 坐标系:
- 模型坐标系:物体自身的中心。
- 世界坐标系:整个场景的坐标系。
- 摄像机坐标系:以摄像机为原点。
- 裁剪坐标系/屏幕坐标系:最终输出的坐标系。
- 齐次坐标:用 (x, y, z, w) 来表示三维点,使用齐次坐标可以用一个统一的矩阵来表示平移、旋转和缩放,极大地简化了计算,当 w=1 时,(x, y, z, 1) 代表一个位置点。
几何表示
- 图元:构成图形的基本单元,如点、线、三角形。
- 多边形网格:用大量的小平面(通常是三角形)来近似表示复杂的曲面,这是3D模型最主流的表示方法。
- 参数曲线与曲面:如贝塞尔曲线、B样条曲线,用于创建平滑的形状和路径。
光照与着色
让物体看起来有立体感和真实感。
- 光照模型:模拟光线与物体表面相互作用。
- 环境光:均匀、无方向的基础光照。
- 漫反射:光线向所有方向均匀反射,产生粗糙表面的感觉,遵循Lambert's Cosine Law。
- 镜面反射:光线在光滑表面产生高光,反射方向遵循入射角等于反射角,遵循Phong/Blinn-Phong模型。
- 着色:将光照模型应用到每个像素上的过程。
- 平面着色:对整个三角形使用一个颜色,效果很假。
- Gouraud着色:在三角形的三个顶点上计算光照颜色,然后在顶点之间进行颜色插值,效果较好,效率高。
- Phong着色:在三角形的每个像素上都进行光照计算,效果最好,但计算量最大。
纹理映射
给物体表面添加细节。
- 纹理图像:一张二维图片,如木纹、砖墙图案。
- 纹理坐标:将三维模型表面的点映射到二维纹理图像上的坐标,通常用 (u, v) 表示,范围是 [0, 1]。
- 纹理过滤:当纹理在屏幕上被放大或缩小时,如何选择颜色,如最近邻点(速度快,马赛克严重)、双线性过滤(质量较好)。
- Mipmap:为纹理创建一系列不同分辨率的版本,用于解决远处物体纹理闪烁和性能问题。
第二部分:C语言实践
使用纯C语言实现图形学,意味着你需要手动完成很多现代GPU自动完成的工作,这虽然繁琐,但能让你深刻理解图形学原理,最经典的工具是 OpenGL。
核心工具:OpenGL (Open Graphics Library)
- 它是什么?:一个跨语言的、跨平台的应用程序编程接口,用于渲染2D和3D矢量图形,它定义了一系列函数,让你可以与GPU通信,告诉它“画什么”和“怎么画”。
- 工作模式:客户端-服务器模式,你的C程序是客户端,GPU和显示驱动是服务器,你调用OpenGL函数,发送“绘图指令”给GPU,GPU执行这些指令并将结果写入帧缓冲区。
C语言实践环境搭建
你需要一个窗口库来创建和管理窗口,并将OpenGL上下文与这个窗口关联起来。

(图片来源网络,侵删)
- GLFW:轻量级的C库,用于创建窗口、管理输入、OpenGL上下文。
- GLAD:一个OpenGL加载器,用于在运行时动态获取不同版本OpenGL的函数指针,现代OpenGL需要这个。
基本步骤:
- 安装编译器(如GCC)。
- 下载并配置GLFW和GLAD库。
- 编写你的第一个程序:创建一个窗口,并清除窗口为某种颜色。
C语言实现核心原理示例
下面我们用C语言和OpenGL来实践前面提到的原理。
示例1:绘制一个彩色三角形
这涉及到顶点数据、缓冲区对象和着色器。
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <stdio.h>
// 顶点着色器源码
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n" // 位置属性变量,从位置0读取
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" // 直接将顶点坐标输出
"}\0";
// 片段着色器源码
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec4 ourColor;\n" // 颜色uniform变量
"void main()\n"
"{\n"
" FragColor = ourColor;\n" // 输出颜色
"}\0";
int main()
{
// 1. 初始化GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// 2. 创建窗口
GLFWwindow *window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
printf("Failed to create GLFW window\n");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
// 3. 初始化GLAD
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
printf("Failed to initialize GLAD\n");
return -1;
}
// 4. 编译着色器程序 (链接几何阶段和光栅化阶段)
// ... (这里省略了编译和链接着色器的详细代码,实际项目中需要单独写函数)
// 假设我们已经编译好了着色器程序,并得到了 shaderProgram ID
// 5. 设置顶点数据 (定义三角形的三个顶点)
float vertices[] = {
-0.5f, -0.5f, 0.0f, // 左下角
0.5f, -0.5f, 0.0f, // 右下角
0.0f, 0.5f, 0.0f // 顶部
};
// 6. 创建并绑定顶点缓冲区对象
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// 绑定VAO (先绑定VAO,再绑定VBO和配置属性指针)
glBindVertexArray(VAO);
// 将顶点数据复制到缓冲区中
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 告诉OpenGL如何解析顶点数据 (链接顶点属性)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 解绑VBO和VAO (虽然不是必须,但是个好习惯)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// 渲染循环
while (!glfwWindowShouldClose(window))
{
// 清除屏幕
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 绘制三角形
glUseProgram(shaderProgram); // 激活着色器程序
glBindVertexArray(VAO); // 绑定VAO
glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制
glfwSwapBuffers(window);
glfwPollEvents();
}
// 清理资源
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glfwTerminate();
return 0;
}
代码解析:
- 顶点数据:
vertices数组定义了三角形在裁剪空间中的三个顶点坐标,这就是几何阶段的输入。 - VBO (Vertex Buffer Object):GPU显存中的一块缓冲区,用于高效存储顶点数据。
- VAO (Vertex Array Object):存储顶点属性配置(如VBO的绑定和
glVertexAttribPointer的调用),方便重复使用。 - 着色器:
- 顶点着色器:接收顶点数据,通过
gl_Position输出最终位置,这里我们直接输出。 - 片段着色器:接收一个叫
ourColor的uniform变量,并输出最终颜色,我们可以通过C代码在渲染时动态改变这个颜色,实现动画效果。
- 顶点着色器:接收顶点数据,通过
示例2:添加变换
现在我们让这个三角形动起来,比如旋转,这需要用到变换矩阵。
-
修改顶点着色器:我们需要在顶点着色器中进行变换。
// 修改后的顶点着色器 const char *vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "uniform mat4 transform;\n" // 声明一个变换矩阵uniform "void main()\n" "{\n" " gl_Position = transform * vec4(aPos, 1.0);\n" // 用矩阵变换顶点 "}\0"; -
在C代码中创建和更新矩阵:你需要一个数学库(如 GLM)来处理矩阵运算,GLM是专为OpenGL设计的,模仿了GLSL的语法。
// 在渲染循环中 #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> // ... (在渲染循环内部) float timeValue = glfwGetTime(); float redValue = sin(timeValue) / 2.0f + 0.5f; int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor"); glUniform4f(vertexColorLocation, redValue, 0.3f, 0.2f, 1.0f); // 创建变换矩阵 glm::mat4 transform = glm::mat4(1.0f); // 单位矩阵 transform = glm::translate(transform, glm::vec3(0.5f, -0.5f, 0.0f)); // 平移 transform = glm::rotate(transform, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f)); // 旋转 // 将矩阵传递给着色器 unsigned int transformLoc = glGetUniformLocation(shaderProgram, "transform"); glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform)); glDrawArrays(GL_TRIANGLES, 0, 3);这段代码实现了模型变换(旋转和缩放)和视图变换(平移),并将结果矩阵传递给GPU,让顶点着色器对每个顶点进行变换。
第三部分:学习路径与资源推荐
学习路径
- 打好基础:熟练掌握C语言(特别是指针、结构体、内存管理)。
- 数学准备:复习线性代数(向量、矩阵)。
- 环境搭建:成功配置好
GLFW + GLAD + GLM的开发环境。 - 从零开始:跟随一个完整的教程(见下文),一步步实现第一个窗口、第一个三角形、第一个着色器。
- 核心概念实践:
- 实现相机控制(WASD移动,鼠标视角)。
- 实现基础的Phong光照模型。
- 学习加载和应用纹理贴图。
- 理解深度测试和模板测试。
- 进阶方向:
- PBR (Physically Based Rendering):更真实的光照模型。
- 阴影:Shadow Mapping, Cascaded Shadow Maps。
- 后处理:泛光、景深、抗锯齿等屏幕空间效果。
- 图形学编程:学习用GLSL编写更复杂的着色器,实现流体模拟、体积渲染等。
经典书籍
- 《计算机图形学:原理及实践(原书第3版)》:你问题的来源,这是一本“圣经”级的参考书,非常全面,但可能不适合作为入门的第一本书,因为它更侧重于理论和算法。
- 《OpenGL编程指南(原书第9版)》:俗称“红宝书”,OpenGL最权威的官方指南,内容详实,理论与实践结合。
- 《Real-Time Rendering, 4th Edition》:实时渲染领域的“圣经”,如果你立志从事游戏引擎或图形引擎开发,这本书是必读的,但难度较高。
在线教程
- LearnOpenGL (CN):强烈推荐! 这是目前最好的OpenGL入门教程,由JoeyDeVries编写,有中文版,它从零开始,讲解清晰,代码完整,覆盖了从基础到高级的几乎所有主题,它的代码结构非常规范,是学习C++和OpenGL结合的典范。
- [OpenGL SuperBible (官方示例)]:红宝书的配套代码,也很有参考价值。
- The Cherno - OpenGL Series:一个非常流行的视频教程系列,由The Cherno主讲,讲解生动,适合喜欢视频学习的人。
用C语言学习计算机图形学,是一条虽然辛苦但回报丰厚的道路,它强迫你直面底层的数学和算法,而不是被高层API(如Unity, Unreal Engine)所封装,当你理解了管线、矩阵变换、着色器这些核心概念后,再去看更高层次的工具,你将拥有更深刻的洞察力。
祝你学习顺利!
