简介:一份面向GAMES101课程作业0的官方指南PDF,重点解决图形学初学者在虚拟机环境搭建与作业框架使用上的常见问题。内容从Oracle VM VirtualBox安装、虚拟硬盘文件下载与配置,到Guest Additions增强功能的安装,逐步引导读者搭建统一作业环境;同时介绍作业框架从主机导入虚拟机,并推荐使用VSCode查看编辑、命令行编译运行。压缩包内共1个文件,类型为PDF,大小874KB,轻量便携。已有433人学习浏览,适合准备完成作业0或需要统一开发环境的课程学员。除操作步骤外,PDF还补充了C++头文件与函数体编写、编译错误与段错误等常见问题排查,以及Eigen线性代数库的使用注意事项,帮助读者避开典型坑点,顺利进入后续图形学作业。
1. 拿到 GAMES101 作业0先别怕:这其实是一份图形学入门热身
第一次听说 GAMES101 的人,多半是在找"图形学入门"资料的时候撞见它的。而当你终于打开课程页面,第一个跳出来的不是炫酷的渲染效果,而是一份叫做"作业0"的PDF:通篇是虚拟机、Ubuntu、cmake、Eigen,还没开始学图形学,先被环境配置吓一跳。别慌,这恰恰是这门课最友好的一步。作业0不要求你做任何图形学算法,它只做三件事:让你把虚拟机环境跑起来、编译运行一个最小的 C++ 程序、顺便解一道旋转加平移的小题。本篇文章就把整份作业0从头到尾讲透——每一步该点什么、每行代码在干什么、报错怎么处理,都按零基础能跟上的节奏来。适合人群:想学图形学、但被环境配置和 C++ 吓得不敢动手的新手。
2. 为什么图形学第一课要折腾虚拟机:统一环境才是真正的目的
先说实话:我第一次看到"请使用虚拟机完成作业"这句话时,心里是拒绝的。明明自己电脑用得好好的,为什么非要再装一个 Ubuntu?后来才知道,这不是课程故意刁难你,而是图形学这个方向的环境依赖实在太容易出问题。
图形学程序要依赖一大堆库,比如后面作业里会频繁用到的 Eigen 线性代数库、OpenCV、OpenGL 之类的工具。每个人的操作系统、编译器版本、库的安装路径稍有不同,同一个代码就可能在你电脑上跑得好好的,在他电脑上报一堆错。课程直接给你一个装好一切的 Ubuntu 虚拟机,等于把"千人千面的环境问题"一次消灭掉。你要做的不是自己从零配环境,而是把别人配好的环境跑起来。
2.1 虚拟机不是玄学:它就像一台随时可以重来的备用电脑
你可以把虚拟机理解成一台装在你电脑里的"假电脑"。它有自己的操作系统(这份作业里是 Ubuntu 18.04),有自己的桌面、文件系统、终端,甚至有自己的开机密码。你在里面干什么,都不会影响到宿主机——也就是你真实的 Windows 或 Mac 系统。
这份作业的精髓在于:课程提供的是一个虚拟硬盘文件(扩展名是 .vdi),它就像这台"假电脑"的硬盘。里面已经装好了 Ubuntu 系统、VSCode 编辑器、cmake、Eigen 库,连环境变量都配好了。你拿到这个文件,用虚拟机软件把它"挂载"起来,就能直接开机进入一个配置好的 Linux 系统。这比自己装 Ubuntu、再手动装一堆图形学依赖要省事一百倍,也几乎不会翻车。
2.2 跟着走一遍:在 VirtualBox 里创建你的第一台虚拟机
虚拟机软件这里用的是 Oracle VM VirtualBox,免费且跨平台。安装软件这一步没什么可讲的,去官网下对应系统的安装包,一路"下一步"就行。需要注意的一点:Windows 用户下载 .exe,Mac 用户下载 .dmg,Linux 用户看自己发行版的对应说明。版本号不用纠结,课程文档里提到的 6.1.4 能用,官网更新的稳定版也能用。
接下来是下载虚拟硬盘文件。课程提供的网盘链接和密码在作业文档首页,下载完你会得到一个压缩包,解压后就是那个 .vdi 文件。这里有个小坑:文件名里带空格和括号,比如"GAMES101_Ubuntu 18.04.2 (64bit).vdi",解压之后最好把它放到一个路径简单、没有中文的文件夹里,后面选文件时不容易找不到。
然后打开 VirtualBox,点"新建",按下面的参数配置:
- 名称:随便起,比如 myGraphics101
- 类型:Linux
- 版本:Ubuntu 64-bit
- 内存大小:建议 2GB(课程文档特别强调这个值)
- 虚拟硬盘:选择"使用已有的虚拟硬盘文件",然后选中你解压好的 .vdi 文件
最后点"创建",虚拟机会出现在左侧列表里。选中它,点顶部的"启动",等系统进入桌面后,输入登录密码即可。密码在课程文档里有,是一串固定的字符串,注意区分大小写,特别是第一个字母是大写的 i,很容易被看成小写 L,输错了系统会提示密码错误。
提示:如果密码怎么输都不对,先去确认是不是把 I(大写 i)和 l(小写 L)看混了。这是新手最容易犯的视觉错误。
2.3 安装增强功能:让鼠标和文件传输不再别扭
刚启动的 Ubuntu 虚拟机有个很别扭的状态:鼠标被"锁"在虚拟机窗口里,按右边 Ctrl 键才能释放回来;窗口也不会自适应分辨率;文件也没办法直接拖进去。这一切的解决办法是安装 Guest Additions——你可以把它理解成官方出的"驱动增强包",装了它,虚拟机才和宿主机配合得顺畅。
安装方法很简单:进入 Ubuntu 系统后,在虚拟机窗口顶部的菜单栏里点"设备"→"安装增强功能",系统会自动挂载一个光盘镜像,按提示运行里面的安装脚本,完成后重启虚拟机即可。
如果上面这个方法失败了,比如点了之后没反应,或者提示找不到光盘,可以打开终端(快捷键 Ctrl+Alt+T),手动执行下面这几行命令:
sudo mkdir -p /media/cdrom # 创建一个挂载目录,-p 表示目录已存在也不报错 sudo mount -t auto /dev/cdrom /media/cdrom/ # 把虚拟光驱挂载到这个目录 cd /media/cdrom/ # 进入光驱目录 sudo sh VBoxLinuxAdditions.run # 运行增强功能的安装脚本命令执行完,重启虚拟机,增强功能就生效了。重启之后记得再做一件事:在"设备"菜单里把"拖放"设置为"双向"。这样你后面从宿主机下载的作业框架,直接拖进虚拟机窗口就能拷贝进去,不用再折腾 U 盘或共享文件夹。
这一步做完,你的虚拟机环境就真正"可用"了。接下来可以打开 VSCode 开始写代码。
3. 先在虚拟机里跑通一个 C++ 样例:Eigen 和 cmake 没有想象中难
环境准备好了,下一步是把课程提供的作业框架跑起来。作业框架是下载虚拟硬盘时一起提供的文件包,你在宿主机上把它解压,拖进虚拟机里,然后用 Ubuntu 里自带的 VSCode 打开。打开方式很多,最省事的方式是:右键点击解压出来的文件夹,选择"使用其他应用打开",然后选 Visual Studio Code;或者在 VSCode 里点 File → Open Folder,选中那个文件夹。
这一步不要跳过。哪怕你觉得自己"已经会 C++ 了",也要亲手编译运行一遍样例程序。因为作业0的真正目的不是考算法,而是让你熟悉一条完整的工作流:写代码 → 编译 → 运行 → 看结果。这条流程后面每一份作业都要用,现在跑通它,后面你就专心调逻辑,不用再跟环境搏斗。
3.1 先读懂样例程序:头文件、主函数和那个"弧度制"的陷阱
打开样例程序 main.cpp,你会发现它很短,大约十几行。第一次看 C++ 程序的人不用害怕,我们一行一行拆开看。
#include <cmath> // 数学函数库,sqrt、sin、cos、acos 都在这 #include <iostream> // 输入输出库,std::cout 在这 int main() { // main 是程序入口,程序从这里开始执行 float a = 1.0, b = 2.0; std::cout << a << std::endl; // 输出 1 std::cout << a / b << std::endl; // 输出 0.5 std::cout << std::sqrt(a) << std::endl; // sqrt(1) = 1 std::cout << std::acos(-1) << std::endl; // acos(-1) 是圆周率 π std::cout << std::sin(30.0 / 180.0 * acos(-1)) << std::endl; // sin(30°) = 0.5 return 0; // 返回 0 表示程序正常结束 }#include的作用是引入别人写好的模块,不用自己造轮子。std::cout是"往屏幕上输出内容"的命令,std::endl是换行。float定义了一个单精度浮点数变量,a / b就是普通的除法。这些都属于 C++ 基础知识,你不需要一下子全记住,见到能猜个大概就行,写作业时会反复遇到。
最后一行是重点:std::sin(30.0 / 180.0 * acos(-1))看起来复杂,其实就是在算 sin(30°) 的值。课程故意在这里埋了个提醒——C++ 的三角函数用的是弧度制,不是角度制。30.0 / 180.0 * acos(-1)是把 30 度换算成弧度的标准写法,因为 180 度 = π 弧度。如果你图省事直接写 sin(30.0),程序会把 30 当作弧度来算,结果约等于 -0.988,非常离谱。这个坑在作业0里不致命,但后面做旋转矩阵时,角度和弧度写错会让整个图像变形,那时候再发现就晚了。
3.2 在终端里编译运行:mkdir、cmake、make 究竟是什么
看懂代码之后,来把它跑起来。在 VSCode 里打开终端(菜单栏 Terminal → New Terminal),确保当前路径停在 main.cpp 所在的文件夹。然后依次输入下面这些命令:
mkdir build # 新建一个叫 build 的文件夹 cd build # 进入 build 文件夹 cmake .. # 让 cmake 去上一级目录读取 CMakeLists.txt,生成编译配置 make # 根据配置编译代码,生成可执行文件 ./Transformation # 运行生成出来的程序,./ 表示"当前目录下"这一步的操作顺序是固定的,请先别改。它的逻辑是这样的:build文件夹专门用来放编译产生的中间文件,不污染你的源码目录;cmake ..里的..表示上一级目录,因为 CMakeLists.txt 在 build 的上一级;make才是真正执行编译的程序;最后./开头是因为 Linux 默认不会在当前目录找可执行文件,需要显式告诉它路径。
如果一切顺利,终端里会依次输出那 5 行数字。如果报错,记住一个原则:看报错信息,永远先看第一行。编译器会告诉你在哪个文件哪一行出了什么问题,照着改就行。比较常见的错误我会在第 4 章的避坑部分集中讲,这里先不展开。
还有一个关键文件你可能注意到了:CMakeLists.txt。它就是"编译说明书",告诉 cmake 这个项目要生成什么名字的可执行文件、要用哪些代码。比如里面会有这样一行:
add_executable(Transformation main.cpp)这一行定义了可执行文件叫 Transformation。你想让编译产物换个名字,改这一行就行;想在作业1里加新的源文件,也要在这个文件里加一行。只有改了 CMakeLists.txt,才需要重新执行cmake ..;如果只改了代码,直接用make重新编译就够了。
3.3 认识 Eigen:向量和矩阵就是图形学的算盘
样例程序里还有一部分是 Eigen 的使用示例。Eigen 是 C++ 的一个线性代数库,图形学里几乎所有东西——点的位置、旋转、缩放、投影——本质上都是在做向量和矩阵的运算。手写矩阵运算容易错,Eigen 把这些操作封装成简单的类和方法,你直接调用就行。
看这段代码:
#include <eigen3/Eigen/Core> // Eigen 的核心模块,向量和矩阵都在这 #include <iostream> int main() { // 定义三维向量 v = (1, 2, 3),w = (1, 0, 0) Eigen::Vector3f v(1.0f, 2.0f, 3.0f); Eigen::Vector3f w(1.0f, 0.0f, 0.0f); std::cout << v + w << std::endl; // 向量加法:每个分量分别相加 std::cout << v * 3.0f << std::endl; // 数乘:每个分量都乘以 3 // 定义 3x3 矩阵,用逗号按行填入元素 Eigen::Matrix3f i; i << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0; std::cout << i << std::endl; // 输出整个矩阵 std::cout << i * v << std::endl; // 矩阵乘向量,得到一个新的三维向量 return 0; }Eigen::Vector3f表示"三个 float 组成的向量",Eigen::Matrix3f表示"3 行 3 列的 float 矩阵"。你可能注意到头文件名是<eigen3/Eigen/Core>,多了一层eigen3目录,这是 Ubuntu 系统里 Eigen 的安装路径决定的,后面写代码照抄这个头文件就行,不要自己改成<Eigen/Core>,否则编译会报找不到文件。
这段代码里的i <<是 Eigen 特有的初始化方式:把 9 个数字按行填进矩阵。不用急着理解它的底层实现,你只需要知道"按行填"这个行为就够了。后面作业里大量会出现"定义矩阵 → 填数字 → 做运算"的套路,多写几次自然就熟了。
4. 作业0正题与避坑指南:用齐次坐标算出旋转加平移的答案
环境跑通了,C++ 和 Eigen 的基本用法见过了,现在来看作业0真正的算法题。题目是这样的:给定一个点 P = (2, 1),绕原点先逆时针旋转 45°,再平移 (1, 2),计算出变换后点的坐标,而且明确要求用齐次坐标计算。
这道题在图形学里非常典型。图形学本质上是研究"一堆点怎么在空间里变换"的学科,旋转、平移、缩放,都是对点做运算。作业0的题虽然只有 2 个步骤,但它把图形学变换最核心的两块——旋转矩阵和平移矩阵——都覆盖了。把这一题做明白,作业1 里做三角形旋转、后续做相机变换,理解起来都会顺畅很多。
4.1 先搞懂齐次坐标:为什么一个点要写成三个数字
在齐次坐标里,二维平面上的点 (x, y) 会被写成 (x, y, 1),多出来一个固定的 1。你可能会问:这不是多此一举吗?答案是:不多。因为平移这件事,用普通的 2×2 矩阵根本表达不了。
你可以试一下:任何 2×2 矩阵乘一个二维向量,结果的每个分量都是原分量的线性组合,常数项加不进去。而平移需要的是"x 加上一个常数、y 加上一个常数"。为了解决这个问题,图形学引入了齐次坐标:把点升到三维,用一个 3×3 矩阵去作用它。这个多出来的第三维平时不起眼,但恰恰让"平移变成矩阵乘法"成为可能。
对应这道题,我们要构造两个 3×3 矩阵。绕原点逆时针旋转 45° 的旋转矩阵 R 长这样:
R = [ cos45° -sin45° 0 ] [ sin45° cos45° 0 ] [ 0 0 1 ]向右平移 (1, 2) 的平移矩阵 T 长这样:
T = [ 1 0 1 ] [ 0 1 2 ] [ 0 0 1 ]注意先旋转再平移的顺序:变换是从右往左读的,T * R * p表示先让 R 作用在 p 上,再让 T 作用在结果上。如果写反成R * T * p,含义就变成了"先平移再旋转",结果完全不同。顺序搞错是图形学里最常见的错误之一,没有例外,记住这个规则:矩阵越靠右,越先执行。
4.2 写出作业0的 main.cpp:矩阵怎么定义、乘法怎么写
有了矩阵,代码就顺理成章了。打开 VSCode,把 main.cpp 改成下面这样:
#include <eigen3/Eigen/Core> #include <iostream> #include <cmath> int main() { // 点 P = (2, 1),用齐次坐标表示成三维向量 (2, 1, 1) Eigen::Vector3f p(2.0f, 1.0f, 1.0f); // 45 度转成弧度:C++ 的三角函数全部使用弧度制 float angle = 45.0f / 180.0f * acos(-1.0f); // 旋转矩阵 R:绕原点逆时针旋转 45 度 Eigen::Matrix3f R; R << cos(angle), -sin(angle), 0.0f, sin(angle), cos(angle), 0.0f, 0.0f, 0.0f, 1.0f; // 平移矩阵 T:x 方向平移 1,y 方向平移 2 Eigen::Matrix3f T; T << 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 2.0f, 0.0f, 0.0f, 1.0f; // 先旋转再平移,所以矩阵从右往左读:T * R * p Eigen::Vector3f result = T * R * p; // 输出结果,前两个分量就是变换后的 x 和 y std::cout << result << std::endl; return 0; }这里有几处细节要说明。acos(-1.0f)算出来的是 π,用它来做角度到弧度的换算,比自己手写 3.14159 更精确。Eigen::Matrix3f R;定义之后没有默认值,必须用<<把所有 9 个元素都填满,漏填一个编译时不一定报错,但运行结果会莫名其妙,这是 Eigen 新手很容易踩的雷。
Eigen::Vector3f result = T * R * p;这行是整个程序的核心。T * R * p是一个矩阵乘一个矩阵、再乘一个向量,Eigen 会按照数学规则算出结果。这个结果向量有三个分量,前两个是变换后点的 x 和 y 坐标,第三个分量会是 1,代表它还是一个齐次坐标表示。
4.3 编译运行并检查结果:输出什么样才算对
代码写完保存,回到终端执行编译四连:
mkdir build cd build cmake .. make ./Transformation如果一切正常,你会看到终端输出一个三维向量,大概是这样的:
1.70711 4.12132 1这个结果对吗?我们快速手动验算一下。cos45° 和 sin45° 都等于 √2/2 ≈ 0.7071。先算旋转:R * (2, 1, 1) 的 x 分量是 2×0.7071 − 1×0.7071 ≈ 0.7071,y 分量是 2×0.7071 + 1×0.7071 ≈ 2.1213。再算平移:x 加 1 得 1.7071,y 加 2 得 4.1213。和程序输出一致,说明代码写对了。
这里推荐你做一个动作:把./Transformation的输出和手算结果对照一下。作业0 不评分,提交的目的只是走通流程,但如果你连结果对不对都没验证过,后面作业出问题时会很难定位——到底是算法错了,还是代码写错了,还是环境有问题。
4.4 新手最容易踩的 5 个坑:现象、原因、解决
以下 5 个坑,是我见过和经历过最多的,每一条都按"现象 → 原因 → 解决"的顺序说明,你遇到时直接对照排查。
坑 1:mkdir build提示File exists。现象:命令执行失败,提示 build 文件夹已经存在。原因:之前编译过一次,或者误删了 build 里的部分文件导致残留。解决:先执行rm -rf build删掉旧文件夹,再重新mkdir build。注意rm -rf很危险,删除前务必确认当前路径就是项目目录,不要在根目录或家目录乱敲。
坑 2:cmake ..报错说找不到 CMakeLists.txt。现象:cmake 提示CMakeLists.txt: No such file or directory。原因:你所在的位置不是 build 文件夹,或者 build 建在了别的地方。解决:用pwd查看当前路径,用ls查看当前目录内容,确认 main.cpp 和 CMakeLists.txt 在上一级目录。规范的做法是永远在 build 文件夹里执行cmake ..,不要把 build 挪地方。
坑 3:make报undefined reference to xxx。现象:编译过程中出现一串带undefined reference的错误。原因:这不是 C++ 语法错误,而是链接错误——代码里用了某个函数或库,但 CMakeLists.txt 没有把它链进来。解决:打开 CMakeLists.txt,检查有没有target_link_libraries这一行,确认需要链接的模块写进去了。Eigen 是头文件库,一般只需要target_include_directories指向/usr/include/eigen3。
坑 4:编译通过,运行时报Segmentation fault。现象:程序编译没问题,一运行就崩,终端提示Segmentation fault (core dumped)。原因:段错误,通常是数组越界、访问了空指针、或者栈空间开得过大。解决:先检查代码里有没有下标越界,比如访问了 i[10] 而矩阵只有 3 个元素。再检查定义的大数组是不是放在了main函数内部,超大数组应该声明为全局变量或改用动态分配。
坑 5:把文件拖进虚拟机没有反应。现象:拖拽文件时鼠标变成禁止符号,或者文件根本没出现在虚拟机里。原因:Guest Additions 没装成功,或者拖放功能没有设置为双向。解决:重启虚拟机,确认"设备"菜单里的"拖放"是"双向";再点一次"安装增强功能",等待安装完成。如果还是不生效,回到 2.3 节的命令行方式手动安装一次。
5. 作业0交完之后怎么学:给零基础的三步图形学入门路径
作业0 提交之后,很多人的第一反应是"终于结束了,可以松口气"。但其实这是整个课程里最轻松的一次作业,趁着流程还热乎,有件小事值得做:把角度从 45° 改成其他值,比如 90°,重新编译运行,看看结果怎么变。再把平移 (1, 2) 改成 (-1, 0),观察坐标变化。这个小小的实验能帮你真正理解"矩阵参数变了,点的位置就变了"这层直觉,比死记矩阵公式有用得多。
接下来如果想继续往图形学走,我的建议是三步走。第一步,补 C++ 基础,不需要啃完整个语法,重点看函数、类、头文件、编译报错这几块,够你读作业框架就行。第二步,把线性代数的几何意义捡起来——向量点积、叉积、矩阵乘法的几何含义,这些是图形学的地基。第三步,趁热做作业1,那是第一次真正接触三角形光栅化,你会发现作业0里学的 cmake 流程和 Eigen 用法全部用得上,到时候你会感谢自己当初没有跳过作业0。
从那次之后,我每次在虚拟机里写图形学相关的代码,都强制自己走一遍固定流程:先mkdir build建好编译目录,写完代码先保存再编译,报错了只看第一行错误信息,运行结果先和手算对照再继续调算法。这个习惯帮我避开过很多次"环境问题和技术问题混在一起"的灾难现场,毕竟图形学越到后面,代码越复杂,把环境稳定住,才有余力去调渲染算法。
希望帮到你。
本文还有配套的精品资源,点击获取