搜狐畅游那年的补录批次里,3D引擎开发工程师挂了个计算机视觉方向,这个组合放到今天看不算稀奇,但在2019年,它释放的信号和今天的岗位要求完全不一样。当时我第一反应是:游戏引擎团队要会OpenCV的人干嘛?后来真正接触过这类岗位的面试题和项目实际需求,才明白这个方向招的不是“会调库的CV工程师”,而是“懂渲染、懂数学、能写C++,同时看得懂视觉算法在游戏里怎么落地”的复合型选手。
这篇文章不打算做成标准答案集,毕竟搜狐畅游那套卷子市面上也没有公开的全套原题,谁要真发出来基本等于告别行业。但我可以把这类岗位的考察逻辑、常考知识块、典型题型和备考过程中最容易踩的坑完整拆一遍。不管你是准备游戏大厂引擎岗,还是想走游戏+视觉的交叉方向,这套思路都能直接用。
1. 一份同时考引擎和视觉的笔试题,到底在考什么
1.1 3D引擎团队为什么要招计算机视觉方向
先聊聊这个岗位为什么存在,这决定了笔试的出题风格。
2018到2019年那个节点,手游精品化已经卷到画质层面,引擎团队不再只盯渲染管线本身,而是开始琢磨怎么让画面更“聪明”。计算机视觉在游戏里的应用场景在那个阶段开始落地,比较典型的有几类:
- 人脸表情捕捉与虚拟角色驱动:用摄像头采集玩家表情,实时驱动游戏内的角色面部动画,这需要人脸关键点检测和表情参数拟合。
- 动作姿态识别:通过单目或多目相机估计人体关键点,映射到游戏角色骨骼,做体感玩法或虚拟偶像驱动。
- 图像分割与场景理解:在AR玩法里识别真实世界的平面、物体边界,做遮挡处理和虚实融合。
- 动画生成辅助:用光流或视频帧插值算法辅助补间动画,减少美术手K关键帧的工作量。
这些需求有一个共同点:算法模型跑通了不算完,必须实时跑在引擎里,跟渲染线程、骨骼动画系统、物理系统做数据交换。传统做CV的人不熟引擎的数据结构和帧循环,传统引擎开发又不碰视觉算法,中间这个交叉地带就是这类岗位要填的坑。
所以笔试的考察逻辑就很清晰了:图形学基础必须过硬,C++工程能力必须能直接干活,视觉部分考的是工程落地的底子,而不是让你推深度学习论文。深度学习在笔试题里占比其实很低,顶多一道概念题,真正占大头的反而是图像处理、多视图几何这个“传统CV”领域。这个判断,我在后来跟几位参加过同类岗位笔试的同行交流时也得到了印证。
1.2 补录批次的特殊性:题量和难度都跟正式批不同
补录批次的笔试和秋招正式批次有几个明显区别,备考策略需要相应调整。
第一是题量偏少。正式批通常是90分钟到120分钟的大卷,覆盖选择、填空、简答、编程多类题型;补录批因为时间仓促,经常压缩成核心几道题,编程题比重反而更大。这意味着你没有太多“蒙选择题”的运气成分,写代码的时间占比高。
第二是难度分布不均匀。补录一般是有名额急招,面试官会优先筛“即战力”,所以题目会更偏向工程实现细节。比如直接给你一个渲染相关的小需求让你实现,或者给你一段有性能隐患的代码让你优化,而不是考背诵型概念。
第三是流程快。补录的笔试和面试间隔通常很短,有的甚至笔试完两三天就约面。准备补录时间窗口小,复习策略必须抓大放小。如果你现在还想着把《C++ Primer》从头翻一遍,那是来不及的,得直接奔着高频考点和代码手感去。
1.3 用一张知识地图看清考察范围
把这类岗位的笔试考点整理出来,大致是四个方向:
| 知识块 | 核心内容 | 考察权重 |
|---|---|---|
| 数学基础 | 线性代数、空间几何、微积分基础 | 极高 |
| 图形学基础 | 渲染管线、坐标变换、光照、阴影、纹理 | 极高 |
| 计算机视觉 | 图像处理、特征点、相机标定、位姿估计 | 高 |
| 编程能力 | C++、数据结构、算法实现、内存管理 | 极高 |
权重是我按过往题目类型预估的,不同批次会有浮动,但工程数学能力和C++这两块永远是大头。有时候甚至会出现一道编程题同时覆盖图形学和数学两个点,这种题区分度极高。
2. 图形学与数学:笔试的地基
2.1 线性代数不牢,后面全白搭
3D引擎方向的笔试题,数学题基本跳不出线性代数这个圈。矩阵变换、向量运算、四元数这几个点每年都会反复出现。但这类题有意思的地方在于,直接考你“矩阵乘法的结合律证明”的情况几乎不存在,更多是给你一个具体场景,让你用数学工具去解。
举个例子,经常会看到类似这样的题目:给一个绕任意轴旋转的角度,让你写出旋转矩阵,或者用四元数表示。表面考的是公式记忆,实际上考的是你有没有真正理解旋转在三维空间中的含义。
先说绕坐标轴旋转,这个大多数人背得下来:
绕X轴旋转θ的矩阵是:
[1, 0, 0, 0] [0, cosθ, -sinθ, 0] [0, sinθ, cosθ, 0] [0, 0, 0, 1]但如果题目改成绕任意单位向量n旋转,就有人开始翻车。这时候需要用到罗德里格斯旋转公式。我建议你手推一遍这个公式的推导过程,而不是只背结论,因为后续考到四元数插值或骨骼动画的时候,对旋转的理解深度直接影响你能不能做出来。
四元数也是高频考点。要理解四元数为什么能避免万向锁问题,为什么在骨骼蒙皮和相机控制里比欧拉角更稳定。常见考察形式是:给你两个旋转的四元数,让你合并成一个,或者给你一个四元数让你转成旋转矩阵。这道题属于典型的“会的不难,不会的完全无从下手”,提前练过就能拿分。
我在复习时发现一个很实用的技巧:把矩阵、四元数、欧拉角之间的互相转换做成一张速查表,考前拼命练到形成肌肉记忆,笔试时能省下大量时间做后面的编程题。
2.2 视图、投影与坐标系变换:引擎里的坐标流转
图形学笔试里有一个永远逃不掉的知识块:坐标系变换。从模型空间到世界空间,再到视图空间,再到裁剪空间,最后到屏幕空间,每一步的矩阵长什么样、为什么这么设计,必须能默写出来。
这里我建议你用一条实际的顶点流转链路去记忆,而不是死背矩阵。比如一个顶点在模型空间是(1,0,0),经过模型矩阵变换到世界空间,再经过视图矩阵变换到相机空间,再经过投影矩阵到裁剪空间,最后做透视除法变成NDC坐标。
笔试常见的考法有两种。第一种是给你其中某几步的矩阵,让你算出最终坐标;第二种是让你写出某个矩阵的具体形式,比如透视投影矩阵。第二种更容易丢分,因为透视投影矩阵的构造涉及视锥体的参数(FOV、宽高比、近裁剪面、远裁剪面)。
以透视投影矩阵为例,它需要把视锥体映射到一个立方体空间。内部涉及一个关键点:透视除法。投影矩阵本身其实没有做除法,它只是把w分量设置为-zview(具体取决于坐标系定义),真正的透视效果发生在后续的透视除法阶段。有些同学考试时直接把除以z写进矩阵里,这种错误很典型。
理解这一点对后面学视觉里的相机模型也有帮助。图形学里用投影矩阵把3D点投影到2D屏幕,视觉里用内参矩阵把3D点投影到像素坐标,本质都是针孔相机模型的变体,只是表达方式不同。能打通这两个领域的概念,你就已经比相当一部分考生有优势了。
2.3 光照模型与渲染方程的基本功
光照模型也是简答题的常客。兰伯特漫反射、Phong高光、Blinn-Phong这几种经典模型至少要能写出公式并解释每个参数含义。
一般会考到这样的程度:给定一个表面法线、光照方向、视线方向、漫反射系数和高光系数,让你计算最终颜色输出。这道题不算难,但容易出现符号和方向约定的混淆。提醒一个容易错的点:计算漫反射时,NdotL必须是法线方向和光照方向的点积,如果光照方向指向光源,要确保方向向量是光源方向而不是光源到表面的方向,两个方向会差一个负号,结果可能算反。
双向反射分布函数(BRDF)这个概念的简答题也出现过,主要考察你是否有PBR的基础认知。不需要你推导菲涅尔方程,但要知道BRDF描述的是入射光方向到出射光方向反射能量的比例,理解它对渲染方程的意义。
这些题在视觉方向笔试题里属于“送分题”,前提是你真的理解,而不是背概念。面试官问法稍微变一下,背概念的人是接不住的。
3. 计算机视觉方向的重点考察范围
3.1 图像处理与特征点:不会让你推CNN,但会考这些
计算机视觉方向的考察范围和图形学完全不同,上来就考深度学习的很少。笔试更关心你有没有掌握经典图像处理的基本功。“滤波、边缘检测、特征点提取”这几个主题出现的频率非常高。
高斯滤波是必考项。常见考法有两种:一种是概念题,问高斯核为什么对图像做平滑能去噪,σ大小对结果的影响;另一种是手写实现题,给你一个3×3的高斯核和一张小图像,让你算中心像素的卷积结果。手动实现时容易漏掉的关键点是核归一化:3×3高斯核的权重之和可能不等于1,使用前必须做归一化,否则图像整体亮度会发生变化。
Sobel边缘检测也经常出现。需要会计算梯度幅值和方向,理解为什么Sobel算子可以用两个方向的卷积核分别检测横向边缘和纵向边缘。这类题不需要背得很深,但至少看到算子矩阵要能认出,并能说明它的作用。
特征点检测方面,Harris角点检测算子的核心思想——通过局部窗口在各个方向上的灰度变化判断是否为角点——是简答和选择的高频考点。至于SIFT和ORB,知道基本原理和各自特点就够了,不太可能让你默写描述子生成过程,但有可能让你口头描述SIFT为什么对尺度变化具有不变性。
3.2 几何视觉:相机模型、标定与位姿估计
视觉方向笔试里真正能拉分的是几何视觉部分。这部分也是和3D引擎关联最紧密的知识。
相机模型是地基。需要理解针孔相机模型,内参矩阵K的形式和物理含义:
K = [fx, 0, cx, 0] [ 0, fy, cy, 0] [ 0, 0, 1, 0]fx、fy是焦距相关的尺度因子,cx、cy是主点坐标。笔试常考这样一题:给定一个相机的焦距(像素单位)和图像分辨率,写出内参矩阵。这个题做不出来的原因通常是没理解fx和fy为什么可能不相等——传感器像素不是正方形时,x和y方向每毫米的像素数不同,导致归一化焦距不同。
相机标定部分,知道张正友标定法的基本流程和为什么用棋盘格就够用。要能说出棋盘格的特征点提取容易、角点精度高、可以自动检测这些优点,顺便理解标定的目标是求内参、外参和畸变系数。
位姿估计是高阶题目。经典考法是给一组2D-3D对应点,让你用PnP算法求解相机位姿。真题可能不会要求你完整推导EPnP,但概念题中你要能说清楚输入是什么、输出是什么、最少需要几组点。PnP最少需要3组点(P3P),实际使用通常用5组以上降低噪声影响。
这部分题目对学过SLAM或做过三维重建的考生相对友好,没接触过的需要额外补课。我建议重点把坐标系转换、相机投影模型、外参矩阵的构成搞明白,这些点和图形学里的视图矩阵、投影矩阵直接相通。
3.3 视觉技术怎么落到引擎里:跨领域思维是关键
笔试中真正决定“复合型人才”定位的,是那些把视觉技术和引擎场景结合起来的题目。这类题目更像开放型简答:给你一个游戏玩法需求,让你想想用视觉技术怎么实现。
举个我印象比较深的例子:面部捕捉的表情驱动。需求是玩家用普通摄像头做表情,实时驱动游戏角色面部。考法可能让你画出系统流程:摄像头采集帧,人脸关键点检测,表情系数拟合,绑定到角色的BlendShape权重,混合动画,最后渲染。每一步用什么算法或引擎组件,需要你写清楚。
再有就是AR游戏里的平面检测:要用视觉方法找到真实世界中的水平面。解答思路一般是特征点提取、匹配、单应性估计或SLAM点云拟合,最后把检测到的平面坐标传给引擎的AR系统,做虚拟物体的放置和遮挡。
这类题没有标准答案,考察的是你有没有把两个领域串起来的能力。回答时建议突出数据如何流转、坐标系如何对齐、性能如何考虑,这比罗列算法名要有说服力得多。我自己笔试这类题时习惯先画一条数据流链路,然后每个环节标注实现方案和潜在瓶颈,面试官会觉得你的工程思维很完整。
4. 编程题实战:两道高频题的思路拆解
4.1 C++基本功:考的不是语法是习惯
引擎开发岗的笔试题,C++基本必考。编程题里考的不只是能不能写出正确答案,更看重代码的风格、边界处理和潜在性能问题。
常考知识点包括:智能指针与内存管理、移动语义与右值引用、虚函数与多态、内存对齐、STL容器的时间复杂度。这些不需要长篇大论背概念,但要在代码里正确使用。
内存管理这块是最容易出答题质量差距的地方。写一个类时,如果动态申请了内存,是否实现了析构函数、拷贝构造函数、拷贝赋值运算符(三法则)?用了裸指针还是智能指针?如果题目明确说了“注意性能”,你是否用reserve避免vector反复扩容?这些都是隐藏考点。
我见过不少同学核心逻辑写对了,却因为拷贝构造导致二次析构崩溃,整道题扣掉一半分。笔试环境里没有调试器,这种问题必须在写代码时主动规避。
4.2 一道高频代码题:射线与三角形求交
射线与三角形求交是引擎底层的经典问题,也是这类笔试题的常客。原因是它既考数学功底,又考代码实现能力,而且和渲染、碰撞、拾取都相关。
推荐Möller–Trumbore算法,因为它不需要预计算三角形所在平面方程,速度快,实现简洁。核心思路是把三角形内的点用重心坐标表示,联立射线方程求解t、u、v三个未知数。
伪代码如下:
bool RayTriangleIntersect(const Ray& ray, const Vector3& v0, const Vector3& v1, const Vector3& v2, float& t, float& u, float& v) { Vector3 e1 = v1 - v0; Vector3 e2 = v2 - v0; Vector3 p = Cross(ray.dir, e2); float det = Dot(e1, p); // det为0时射线与三角形平行 if (fabs(det) < 1e-8) return false; float invDet = 1.0f / det; Vector3 s = ray.origin - v0; u = Dot(s, p) * invDet; if (u < 0.0f || u > 1.0f) return false; Vector3 q = Cross(s, e1); v = Dot(ray.dir, q) * invDet; if (v < 0.0f || (u + v) > 1.0f) return false; t = Dot(e2, q) * invDet; return t > 0.0f; }代码里有几个细节是笔试评分点。第一个是eps阈值判断。浮点比较不能用等号,必须用fabs(det) < 1e-8判断是否接近零。第二个是重心坐标的范围判断,u、v、u+v都要在合法区间内。第三个是t必须大于0,等于0表示射线起点在三角形上,通常不算有效交点。
4.3 另一道经典题:手写高斯模糊
图像处理类编程题常见的是实现高斯模糊。如果笔试环境有OpenCV,那直接调filter2D可以过一部分用例,但有些题目会明确要求不依赖外部库,手写卷积。
实现要点有两个。第一个是核的生成,以一个5×5、σ=1.0为例,先用公式计算每个位置的权重:
float gauss(float x, float y, float sigma) { return exp(-(x*x + y*y) / (2.0f * sigma * sigma)) / (2.0f * M_PI * sigma * sigma); } std::vector<float> generateKernel(int radius, float sigma) { int size = 2 * radius + 1; std::vector<float> kernel(size * size); float sum = 0.0f; for (int y = -radius; y <= radius; y++) { for (int x = -radius; x <= radius; x++) { float val = gauss(x, y, sigma); kernel[(y + radius) * size + (x + radius)] = val; sum += val; } } // 归一化,使权重和为1 for (int i = 0; i < size * size; i++) { kernel[i] /= sum; } return kernel; }这里最容易出错的就是忘记归一化,或者σ太小导致中间权重过大、核的边缘权重接近于0,等于没平滑。第二个容易被忽略的地方是图像边界的处理。笔试卷子经常不会明确说边界怎么处理,你需要在代码里自己决定,有些题目会提示用边界填充,一般建议你用最邻近填充(clamp)或者反射填充,在代码注释里写清楚即可。
另外还有个性能优化点:高斯核是可分离的,可以用水平方向和垂直方向两次一维卷积替代二维卷积,复杂度从O(n²m²)降到O(nm)。笔试时间有限,先写对功能,再在注释里提一句“可以进一步优化为分离卷积”,面试官会对你加分。
5. 备考与答题的避坑实录
5.1 往年考生最容易丢分的五个点
和几位参加过类似笔试的同学对过答案,整理出几个共性丢分点,排名不分先后:
- 第一,矩阵乘法的顺序搞反。引擎里从模型空间到世界空间,再到视图空间、投影空间,向量总是左乘矩阵。如果写成右乘或合并矩阵时顺序颠倒,结果全错。
- 第二,四元数不会转矩阵。背了四元数定义,但实际考试要你给出旋转矩阵的时候写不出来,或者矩阵元素位置写错。这个只能多练,建议找一个标准参考实现核对。
- 第三,推导过程缺失。简答题直接写结论不给过程,题目要求“画出流程”就只写算法名不展开。就算你的结论对,面试官也无法判断你是真懂还是背的。
- 第四,代码不写边界条件。射线求交不判断det为0,高斯模糊不处理图像边界,二叉树的题不管空指针。这类错误在笔试环境下太常见,也能通过检查规避。
- 第五,时间分配失衡。前面简答题写太长时间,编程题没时间写,或者一上来就挑战高难度编程题,导致基础题没做。
5.2 CV方向考生容易踩的两个思维误区
第一个误区是过度关注深度学习。很多视觉方向的同学看到“计算机视觉”就以为要考CNN、目标检测、语义分割,其实笔试里占比很小。拿我实习面试的经历来说,面试官更关心算法能不能在帧预算内跑完、怎么和引擎管线对接,传统视觉的相机模型、特征点匹配、几何重建他们在考卷上都还没问够。
第二个误区是忽略图形学基础。视觉方向的同学可能觉得图形学是引擎岗的事,自己会视觉算法就够了。实际上这个岗位叫“3D引擎开发工程师”,图形学是主业,视觉只是方向偏好。备考时图形学的权重绝不能低于视觉,否则笔试第一轮就会被过滤掉。
反过来,图形学背景的同学准备视觉部分时,会比较容易接受相机模型和坐标系变换,因为和引擎里的相机概念可以直接对应。跨方向的同学不妨利用这个迁移效应,先摸清两边的共通概念,比如投影矩阵和内参矩阵的关系、世界坐标系和相机坐标系的转换、深度图和点云的关系,这会让学习顺畅很多。
5.3 应试时间分配建议
根据过往经验,这类补录笔试的编程题数量大概率不少于两道。拿到卷子不要直接埋头做题,先花两分钟浏览全部题目,标出自己最有把握的题,再规划时间。
一个可行的比例是:选择填空和简答总共控制在40%的时间以内,编程题留60%。这主要是因为在笔试平台上,代码没跑通基本等于零分,而简答题写几行关键词还能捞点分。编程题一定要选自己最有把握的一题先写,别一上来就死磕最难的。
补录批次题量虽然小,但题目质量通常不低。碰到完全不会的数学推导题,不要直接空着,把你确定的前置公式写在旁边,有时候面试官能看到你的思考过程,哪怕最后没得出最终答案,也会给一点分。
6. 关于这个方向,我个人的一些体会
说句实在话,“3D引擎开发工程师+计算机视觉方向”这种组合岗位,在2019年算稀缺,放到现在依然是游戏技术岗位里的一个小众分支。它不像纯粹的后端、客户端岗位那样有成熟的题库和培训机构,备考资料少,更需要自己动手把分散的知识点串起来。
我当时的复习策略是:先从图形学基础扫盲,重点把坐标变换、投影矩阵、光照模型吃透;然后拿视觉的经典教材补相机模型和多视图几何;最后用LeetCode和图形学编程题练手感。每周会花一整个下午的时间,用代码实现一个从图像特征点提取到PNP位姿估计的小流程,把两个领域的知识串起来,这个练习对笔试和面试帮助最大。
补录批次的笔试通常没那么多“偏题怪题”,它更想看你在基础扎实的前提下有没有解决交叉问题的潜质。如果你平时就对引擎渲染和视觉算法都有积累,那这套卷子对你来说不应该是障碍,而是展示自己的机会。希望这份拆解能帮你少走一些弯路,把精力花在刀刃上。