2019年搜狐畅游校招季,3D引擎开发工程师(计算机视觉)这个岗位放出来的时候,不少准备投递的应届生第一反应是“这到底算引擎岗还是算法岗”。有这个疑问很正常。游戏公司的引擎组里,常年需要做动作捕捉数据处理、表情追踪、贴图自动生成这些事情的工程师,这些活儿既离不开图形学渲染逻辑,又要用视觉算法做信息提取,所以干脆设一个交叉方向的岗位。它考察的不完全是纯视觉的论文复现能力,也不完全是让GPU跑得更快的传统引擎能力,而是“你能不能把视觉算法在游戏引擎的场景里用起来”。
这篇文章就按我实际准备这类笔试的经验,把整个考察体系拆开:核心知识有哪些、高频题型长什么样、复习顺序怎么排、哪些坑不该踩。目标读者是正在准备游戏公司校招笔试的同学,尤其是想投3D引擎开发工程师、图形学工程师,却被“计算机视觉”这个方向绕晕的。就算你暂时不投畅游,这套复习框架对国内其他游戏公司的同类型岗位也完全适用。
1. 先拆清楚:为什么游戏引擎岗要考计算机视觉
1.1 这个岗位在游戏公司里到底做什么
先说结论:游戏公司招聘计算机视觉方向的人,核心目的永远是“帮美术、策划、程序省出更多做内容的时间”。引擎渲染本身有很成熟的管线,不需要视觉算法去代替,但视觉算法在内容生产环节里能做的事情非常多。
举几个真实场景。
动作捕捉(mocap)是游戏开发里最常规的视觉应用。演员穿上带有标记点的动捕服,多台光学摄像头同时记录标记点的2D位置,系统通过多视角几何重建出三维轨迹,再解算出骨骼旋转数据,最后映射到游戏内的3D模型骨骼上。这个完整链条里,前三步全是计算机视觉的经典问题:相机标定、多视图匹配、三维重建。引擎开发工程师在这条链路里负责的是数据和引擎的对接,比如把解算后的骨骼数据在引擎里做平滑、重定向、与动画状态机衔接。
表情捕捉、手势追踪、AR玩法(比如基于手机的AR试装)也是同一个逻辑,摄像头输入二维图像,视觉算法输出三维姿态或语义信息,引擎消费这些信息做渲染和交互。
还有一类需求落在美术资产生产上。一张真实世界的照片要变成游戏里的贴图,需要做透视校正、光照一致性处理、无缝拼接;一张扫描模型要进引擎,需要做点云配准、网格简化、纹理映射。这些都是视觉算法密集的环节。
所以这个岗位描述里虽然写着3D引擎开发,但实际上是在引擎组里写视觉工具的工程师。笔试自然会同时照顾到引擎基础、视觉算法、工程落地三个维度。
1.2 这场笔试和纯算法岗、纯引擎岗的差异
纯算法岗笔试考什么?机器学习理论基础、模型推导、手写SVM/KNN、卷积累积运算解题。但这场笔试不会把重心放在机器学习上,即使偶尔有几道图像处理相关的题,也偏向经典算法而非深度学习。
纯引擎岗笔试考什么?渲染管线、GPU架构、图形API、shader编写。这场笔试会涉及其中一部分,但不会深到写GPU底层优化。
交叉岗笔试的最大特点就是:知识面广,但每项都考得浅,考得实用。这意味着你不需要把计算机视觉的教材背完,但基础知识必须非常牢靠,因为题目往往会以“一个引擎开发中常见的计算问题”的形式出现。
比如它不会直接问“请写出四元数的乘法公式”,而是会让你计算一个物体绕某个轴旋转90度后再做投影的坐标;不会让你从零推导相机标定算法,而是让你解释为什么标定板角点检测是标定流程的第一环。
这些细节我会在后面几节里详细展开。
2. 笔试考察的知识体系全景:数学和空间底子比任何框架都重要
2.1 数学基础:线性代数与三维几何是绝对主线
我来做个大胆的判断:这份笔试如果只允许复习一门课,那就复习线性代数和三维几何,别的都可以往后放。
原因很简单。3D引擎里的所有变换——模型矩阵、视图矩阵、投影矩阵——本质都是线性变换,计算机视觉里的相机成像、多视图几何、三维重建,也全是线性代数在三维空间中的应用。所有题目无论表面上是视觉题还是引擎题,最后都会化成一堆矩阵和向量计算。
重点要掌握的知识点我列一下:
- 矩阵乘法、逆矩阵、转置、行列式
- 正交矩阵的性质(旋转矩阵就是正交矩阵)
- 向量的点乘、叉乘,以及它们各自在几何上的含义
- 特征值与特征向量(在PCA、椭球拟合、协方差分析中高频出现)
- 线性方程组的求解思路(最小二乘、SVD分解)
其中叉乘容易被人忽略,但在游戏开发里它无处不在:叉乘可用来求法向量,可用来判断点的左右朝向,可用来构建坐标系的第三个正交轴。视觉里三角化也需要用到叉乘构造约束方程。笔试不一定会单独出一道叉乘的计算题,但在多道题目的推导过程里都会出现。
2.2 图形学核心概念和视觉核心概念其实是同一套底层
引擎和视觉交叉最集中的地方是坐标系和投影。
引擎里的坐标系约定需要记清楚:模型坐标系、世界坐标系、视图坐标系(相机坐标系)、裁剪坐标系、屏幕坐标系。模型顶点从本地空间到屏幕空间,要依次经过模型矩阵、视图矩阵、投影矩阵、视口变换。这里面的每一环,在计算机视觉里都有一个对应的概念:模型矩阵对应物体在世界中的位姿,视图矩阵对应相机的外参,投影矩阵对应相机的内参,视口变换对应图像分辨率。二者讲的其实是同一件事,只是术语不同。
所以复习的时候千万别把图形学和视觉当成两门独立的课。把一条链路打通:顶点在模型空间里的坐标,经过引擎的MVP矩阵到屏幕;同样一个点,经过视觉里的外参和内参到像素坐标。你会发现核心计算过程完全一致。理解这条链路,比背下20道真题都管用。
2.3 工程基础:C++、数据结构和性能意识
3D引擎开发工程师的笔试里,C++和算法题永远不会缺席,而且占比不会低。毕竟你的代码最终要跑在引擎里,引擎是C++写的,你的工具也大概率是C++写的。
C++的考察集中在:内存管理(RAII、智能指针、new/delete)、STL容器原理(vector扩容、map与unordered_map的底层差异)、多线程(线程同步、锁、原子操作)、面向对象(虚函数、多态、菱形继承)、现代C++(移动语义、lambda、完美转发)。这些是引擎岗笔试的常客,和互联网后端岗考察的点高度重合,反而比纯算法岗考得更细。
数据结构与算法方面,不追求极难,但基础必须扎实。数组、链表、栈、队列、二叉树、图、哈希表、堆,这些结构的基本操作要能当面手写;排序、二分、双指针、DFS/BFS、动态规划,这些算法要练到肌肉记忆。游戏公司笔试算法的总体难度低于一线互联网大厂,但代码风格和边界条件处理会被挑剔。
还有一项经常被忽略:性能意识。笔试可能不直接考,但面试环节一定会被追问“你写的这段代码在引擎里每秒要跑多少次?缓存命中怎么考虑?能不能用SIMD优化?”。所以复习时要带着性能指标去看问题,比如实现一个矩阵乘法时想想怎么让数据在缓存里更友好,而不是简单套公式。
3. 核心考点拆解:坐标变换与旋转表示
3.1 旋转矩阵、欧拉角、四元数的考察方式
三维坐标变换在3D引擎和计算机视觉里都是高频考点,没有之一。
先看旋转的三种表示。旋转矩阵直观,适合作为中间计算格式,但有9个参数(实际上只有3个自由度),且存在冗余;欧拉角用三个角度表示,人最容易理解,但有万向锁问题,在12种顺序约定下还容易产生歧义;四元数是4个参数的复数扩展,插值方便,没有万向锁,是引擎里实际存储旋转最常用的格式。
笔试里常见的问法有几种:
第一,给出绕X轴或绕Y轴旋转一定角度的旋转矩阵,要求写出或计算某点变换后的坐标。这种题是送分题,但送分的前提是你把三轴旋转矩阵记熟了,并且分清楚旋转正方向是按右手定则还是左手定则(引擎通常用左手系,视觉通常用右手系,这个细节极易踩坑)。
第二,给出一条旋转轴的向量和一个旋转角度,要求用Rodrigues公式计算旋转矩阵。这题如果把公式记熟就是直接代入,但公式里有个小陷阱:叉乘矩阵的构造要正确,尤其注意符号。
第三,问四元数和旋转矩阵如何互转。这是引擎岗的高频题,四元数q = w + xi + yj + zk(模长为1)转旋转矩阵的公式要能写出大概,至少知道主对角线元素与w、x、y、z的关系,以及非对角元素与旋转轴和角度的关系。反过来,从旋转矩阵提取四元数,核心思路是用矩阵的对角线元素求得w,再通过w反解x、y、z。
3.2 一个典型的坐标变换题怎么做
假设题目是这样的:世界坐标系下有一个点Pw = (2, 1, 3),相机在世界坐标系下的位置是(0, 0, 5),相机朝向为Z轴正方向,求Pw在相机坐标系下的坐标。
这个题几乎没有难度,核心是理解刚体变换:Pc = R(Pw - C),或等价地写成Pc = R·Pw + t。相机在(0,0,5)、朝向Z正方向,意味着R是单位阵,t = -R·C = (0, 0, -5)。所以Pc = (2, 1, -2)。
但如果相机朝向变了,比如相机绕Y轴旋转了90度,矩阵R就不再是单位阵,你必须先写出R,再做矩阵乘法。这题要想不丢分,关键是把坐标系的建立顺序记清楚:先平移后旋转、还是先旋转后平移,在公式上只有写法的区别,但很容易搞混。我个人的习惯是永远先做旋转,再做平移,也就是 Pc = R(Pw - C)。这样相机中心C先被旋转抵消,剩下的t就只是-R·C,非常干净。
如果题目进一步要求计算这个点在图像上的投影坐标,就会引入相机内参。这部分我放在下一节,先不展开,但你要意识到:坐标变换题往往是投影题的前置步骤,做对了这一步,后面才谈得上对。
4. 核心考点拆解:相机模型与投影关系
4.1 针孔相机模型必须烂熟于心的公式
在计算机视觉的笔试里,针孔相机模型是“保留项目”。这个模型描述了一个三维空间点如何通过一个小孔成像到相机的图像平面上。公式很简单:
u = fx * Xc / Zc + cx v = fy * Yc / Zc + cy
其中(Xc, Yc, Zc)是点在相机坐标系下的三维坐标,(u, v)是成像后的像素坐标,fx、fy是焦距的像素表示,cx、cy是主点坐标。
这个公式一定要能闭着眼睛写出来,并且要理解每个参数的含义。fx = f / dx,dx是一个像素的物理宽度;fy同理。cx、cy是光轴与成像平面交点对应的像素坐标,理论上在图像中心,实际上由于传感器制造安装偏差不在中心。
笔试里常见的直接考察方式:给出相机内参(fx=500, fy=500, cx=320, cy=240),给出一个三维点在相机坐标系下的坐标(X=10, Y=20, Z=100),求像素坐标。代入公式得到u = 50010/100 + 320 = 370,v = 50020/100 + 240 = 340。如果这题的Z特别小,比如Z=0或者负数,点就在相机背面,理论上不会成像,这种边界条件也常被拿来做干扰项——考你物理直觉,看你会不会检查Z的正负。
4.2 从像素到三维的逆推
正向投影是画得出来的,但实际应用中更多是逆向需求:已知图像里某个像素点,怎么知道它在世界中的位置?
这是一个典型的欠约束问题。一个像素点对应的是相机发射的一条射线,而不是一个确定的三维点。要想得到确定的三维坐标,要么已知深度Z,要么找到另一个视角的对应点做三角化,要么假设物体在某个平面上。
笔试里最常考的是“已知深度求三维坐标”这类题。思路是先把像素坐标去归一化:Xc = (u - cx) / fx * Zc,Yc = (v - cy) / fy * Zc。注意这里一定要把像素坐标做“减主点、除焦距”的步骤,得到归一化平面坐标,再乘深度Zc。
再把相机坐标转成世界坐标:Pw = R^T(Pc - t),因为旋转矩阵是正交矩阵,R的逆等于R的转置。这一步如果写成Pw = R·Pc + t就会错,方向完全反了。这是笔试里最经典的失分点,我见过太多人在这个符号上栽跟头。
4.3 畸变与相机标定的概念题
镜头畸变在笔试里通常不会要求做复杂的矫正计算,但概念必须清楚。径向畸变(桶形/枕形)由透镜形状引起,切向畸变由镜头与传感器不平行引起。畸变的数学表达一般用多项式近似,视觉里叫Brown-Conrady模型,笔试只要记住畸变发生在像素坐标转归一化坐标之后、投影之前,以及畸变矫正是标定的一部分,基本够用。
相机标定流程是另一个高频概念题。张正友标定法基本是标准答案:准备棋盘格标定板,从多个角度拍摄,检测棋盘格角点,建立像素坐标和已知物理网格坐标的对应关系,计算单应矩阵H,再基于多视图间的旋转正交约束求解内参矩阵K,最后用最小二乘估计畸变系数并做全局优化。
为什么标定是视觉算法在游戏落地时的第一步?因为在动捕、AR、扫描重建所有环节里,先算清楚相机内参和外参,后续所有三维计算的正确性才有保障。这一点在笔试答题时如果能主动点出,会让阅卷人觉得你是有工程直觉的,而不是只会背公式。
5. 核心考点拆解:图像处理与特征点识别
5.1 卷积、滤波与边缘检测
图像处理在游戏公司的视觉笔试里是性价比非常高的部分,考点明确,准备起来也不难。
卷积是图像处理最基础的操作:用一个小的核(kernel)在图像上滑动,每个位置的输出是邻域像素与核的加权和。高斯滤波的核是一个二维高斯分布采样值,用于降噪;Sobel算子用于计算图像梯度,分为水平方向Gx和垂直方向Gy,本质是对图像做一阶导数的近似。
笔试里可能直接给出Sobel核,要求计算某个3x3区域的梯度幅值和方向。梯度幅值 = sqrt(Gx^2 + Gy^2),方向 = atan2(Gy, Gx)。这个计算本身不难,但要注意核的符号方向和坐标轴方向的关系,别把梯度方向判反。
Canny边缘检测的流程是另一个经典概念题:高斯滤波降噪 — 计算梯度幅值与方向 — 非极大值抑制 — 双阈值检测 — 滞后连接。在回答这类问题时,要能说明每一步在做什么、解决了什么问题。比如非极大值抑制是把局部的梯度极大值保留下来、把边缘变细;双阈值检测是把高阈值确定的强边缘和低阈值保留的弱边缘区分开,弱边缘只有在与强边缘相连时才被保留。
这些概念在游戏引擎里也有直接应用:后处理中的边缘检测(如描边效果)、屏幕空间环境光遮蔽(SSAO)中用到深度和法线的滤波,思路都源于图像处理。
5.2 特征点与描述子
特征点检测与匹配是视觉算法在游戏开发里最常见的应用。
核心考点是三种经典特征点,一定要能说清楚各自的特点和区别。SIFT(尺度不变特征变换)在尺度空间找极值点,特征对缩放、旋转、光照变化有较强的鲁棒性,但计算量大;SURF本质是SIFT的加速版,用Haar小波求特征,速度更快但鲁棒性略降;ORB结合了FAST角点检测和BRIEF二值描述子,计算速度极快,适合手机端和实时场景。
笔试里常见的问法是:为什么SIFT具有旋转不变性和尺度不变性?答案要点是:旋转不变性来自关键点方向的主方向估计,描述子会沿主方向旋转对齐;尺度不变性来自高斯差分金字塔(DoG)的尺度空间极值检测。
另一个常考的是描述子的匹配:暴力匹配是两两计算描述子的距离取最近,但对噪声敏感;更常用的是最近邻比次近邻(ratio test),只有当最近邻距离明显小于次近邻时才算匹配成功。这个比例通常取0.6到0.8之间。这类题在笔试里会以“你怎么过滤错误匹配”的形式出现,直接答ratio test再配合基础矩阵约束或单应矩阵剔除外点,基本就能拿分。
5.3 视觉算法在游戏开发里的实际落地
笔试虽然考的是概念,但你答题时如果能自然地联系到游戏开发场景,绝对是加分项。
前面提到一个典型场景:贴图材质自动生成。设计师拍摄一张真实材质的照片,需要透视矫正,这就是用特征点匹配求单应矩阵的过程;照片有反光、光照不均,需要做滤波和直方图均衡化;照片分辨率不够,需要做超分辨率或细节增强。
骨骼动画的重定向也与视觉联动。后期想要自动解算骨骼长度比例、自动匹配不同模型的体型差异,都需要视觉特征来辅助对齐。
还有一类很容易被忽略但实际上很有价值的方向是“渲染优化中的感知模拟”。比如把图像质量评分和质量调优做在渲染管线的自动调节里,用视觉效果做动态分辨率缩放,这套逻辑也是视觉与引擎结合的场景。笔试如果问你“视觉算法在游戏引擎里除了动捕还有什么用途”,脑海中一定要有这几个方向:内容生产自动化、交互感知、渲染反馈优化。
6. 工程能力考察:C++细节和算法题准备
6.1 C++必考知识点清单
除非你的目标岗位是纯算法研究员,否则3D引擎开发岗的笔试一定绕不开C++。我整理了一份我当年用过的自查清单,你可以逐项检查:
- 智能指针三种类型的语义区别和使用场景:unique_ptr独占所有权,shared_ptr共享所有权并通过引用计数管理生命周期,weak_ptr解决循环引用和悬空引用;引用计数本身的线程安全性
- vector的扩容机制到底怎么实现的:capacity翻倍增长、元素逐个移动/拷贝、这会导致迭代器失效;emplace_back和push_back的差异
- map与unordered_map的底层区别:红黑树 vs 哈希表,对应的时间复杂度、有序性、内存占用
- 虚函数的实现机制:虚函数表指针、动态绑定、以及虚析构函数的必要性
- 移动语义与完美转发:什么时候会触发移动构造,std::move的本质是什么
这些点不需要每个都能写五千字,但每个都要能举出一个具体的代码例子来说明。比如被问到shared_ptr循环引用,例子就是两个对象互相持有shared_ptr,导致引用计数永远归不了零,内存泄漏;解决方法是改用weak_ptr。
6.2 算法题侧重点与刷题策略
算法题在游戏公司的笔试里更偏向基础应用,不会刻意出那种一看就要推半天结论的智力题。但反过来说,正因为难度不高,代码的规范和正确性要求会更严格。
高频题型包括:数组和字符串的操作(滑动窗口、双指针、前缀和)、链表(反转、找环、合并有序链表)、二叉树(遍历、层次遍历、最近公共祖先)、图(DFS/BFS、拓扑排序、最短路径)、动态规划(背包、最长子序列、编辑距离)。
我的建议是不要只刷难题,主线放在中等难度的题目上,把每种题型的模板写法练熟。考场时间有限,第一分钟内判断出题型的熟练度,往往比思考一小时憋出个精巧解法更重要。
代码风格也会被考察。命名规范、边界条件处理、空指针判断,这些细节在笔试评分时分量不小。写代码前先用一两行注释说明思路,写完再检查一遍边界,这些都是提高通过率的习惯。
7. 常见问题与避坑指南:如果你现在开始准备,该按什么顺序来
7.1 复习排序建议
不同基础的同学复习优先级不一样,但核心思路可以统一:先补数学和三维空间基础,再复习C++和算法,最后熟悉视觉和图形学的概念。
我给一个具体的顺序参考:
第一周,集中搞定线性代数和三维几何。矩阵、旋转、坐标变换、四元数,这些不仅仅是为了应付笔试里的数学题,更是后面理解和做题的地基。
第二周到第三周,刷C++和数据结构算法题。每天保证3-5道手写代码题的量,核心是建立手感,让自己在考场上写代码不卡壳。
第三周后半段到第四周,复习图像处理和相机模型。卷积、滤波、边缘检测、特征点、相机内参外参,这些是视觉考题的重心,而且很多可以直接用到前面的数学功底来辅助理解。
考前两天,把各知识点过一遍,尤其是旋转矩阵和相机投影这两块必考内容。考前过一遍的价值在于避免公式遗忘,而不是临时学新东西。
这个顺序最核心的逻辑是:数学为你提供理解工具,C++和算法为你提供写题手段,视觉和图形学是应用场景。倒过来复习会非常痛苦,因为你每遇到一个公式都要临时补数学背景。
7.2 笔试中容易失分的几个细节
第一个细节是坐标系约定。引擎里常用左手系,视觉论文里常用右手系,答题时不标注会得错。两个系的旋转矩阵方向完全相反,如果你用左手系的答案去回答右手系的问题,结果必错。拿到题目先看坐标系,再看旋转方向,这是第一优先级。
第二个细节是公式里的正负号。相机投影公式中,像素坐标的u、v与Z的正负性,外参变换中的平移向量方向,畸变矫正公式的符号,这些地方最容易因为细节丢分。写完之后把结果代回公式验证一遍数值合理性,是一个很有效的检查方法。
第三个细节是概念题的陷阱。比如“欧拉角是不是唯一表示法”这种问题,答案是“不是”,因为不同旋转顺序产生不同结果;再比如“四元数能不能表示缩放”,答案是“不能”,四元数只编码旋转,不含缩放信息,缩放要另行处理。这些概念性的“一句话考点”在笔试里非常常见,复习时不要把注意力全部放在计算题上,概念题同样要兼顾。
第四个细节,也是最容易被忽视的:不要忽视Z值的取值范围。任何一个三维点投影到图像平面之前,它的深度必须为正。很多计算题设置的点可能在相机背后或者深度极小的位置,这种情况下投影结果没有物理意义。我在批改同学模拟题的时候经常看到有人算出负的深度还不自知,直接导致整个题的分数泡汤。
写到这里,关于这场笔试的核心内容基本就拆完了。最后说一点我自己的体会:准备这类“3D引擎+计算机视觉”交叉岗位时,最怕的不是知识面不够,而是把自己定位错了。如果你把自己当成纯算法方向的CV工程师,你会花大量时间在深度学习模型和论文复现上,但这些在笔试里根本不怎么考;如果你把自己当成纯引擎工程师,又会忽略相机模型、特征匹配这些视觉基础,同样失分。正确的定位是“会用视觉算法解决引擎问题的工程师”,所有复习的落脚点都应该是:这个算法在游戏开发流程里能解决什么问题,怎么和引擎的数据结构对接,性能上能不能扛得住。带着这个视角去准备,笔试和面试都会顺很多。