腾讯社招面试复习系列之二,图形篇

腾讯社招面试复习系列之二,图形篇

最近在准备复习面试腾讯游戏开发,接下来会出一系列复习文章,总结一些他人的面试题与经验,以及之前自己面试时经验,并给出一些自己的见解,供大家一起学习。


1.渲染管线:

CPU,GPU之间的通信

渲染流水的起点是CPU,即应用阶段,应用阶段大致分为3个阶段:

  • 数据加载到显存将渲染需要的数据从硬盘加载道到运行内存,再从内容加载到显存
  • 设置渲染状态:一个通俗的解释就是,这些状态定义了mesh是怎么被渲染的。例如:使用哪个shader(定点着色器,片段/像素着色器),材质信息,光源属性等。如果不改变渲染状态,那么所有的mesh都将使用同一种渲染状态。
  • 调用drawcall:CPU通过图像编程接口向命令缓存区中添加命令,这个命令仅仅会指向一个需要被渲染的图元列表,不再包含任何材质信息,这个操作叫drawcall。

question 1:CPU GPU如何并行工作的?
是通过命令缓冲区实现的,命令缓冲区是一个队列,CPU向其中添加,GPU读取,添加跟读取是相互独立的,命令缓冲区有很多种类型,drawcall是一种,还有像改变渲染状态(例如改变使用的shader,使用不同的纹理等)。
question 2:为什么drawcall多了会影响效率:
每次drawcall之前,CPU需要向GPU发送很多内容,包括数据,状态,指令,在这一阶段,CPU还要完成一些其他工作,比方说检查渲染状态等。一旦这些准备工作完成,GPU就可以开始本次的渲染。GPU的渲染能力是很强的,渲染10个跟100个mesh通常差别不大,因此drawcall高,渲染速度往往快于CPU提交命令的速度,造成CPU过载。
question 3:如何减少drawcall:
一般是采用合批的方式减少,常见的有:

  • 静态合批:对于静态不移动的物体,只需要一次合并操作
  • 动态合批:经过批处理的物体仍然可以移动,由于每次都要重新batch
  • 共享材质:尽量在不同网格之间使用同一个材质
  • 避免使用大量很小的mesh

GPU流水线:

任何事物都在3D空间中,而屏幕和窗口却是2D像素数组,这导致OpenGL的大部分工作都是关于把3D坐标转变为适应你屏幕的2D像素。3D坐标转为2D坐标的处理过程是由OpenGL的图形渲染管线(Graphics Pipeline,大多译为管线,实际上指的是一堆原始图形数据途经一个输送管道,期间经过各种变化处理最终出现在屏幕的过程)管理的。图形渲染管线可以被划分为两个主要部分:第一部分把你的3D坐标转换为2D坐标,第二部分是把2D坐标转变为实际的有颜色的像素。
在这里插入图片描述
从图中可以看出,GPU流水线接收定点数据作为输入。这些定点数据是由应用阶段加载到显存的,再由drawcall指定,随后被传入定点着色器。
顶点着色器:输入为cpu加载到显存的顶点数据,处理单位为顶点,即每个顶点都会调用一次顶点着色器。主要任务:

  • 坐标变换,把顶点的位置从模型空间转换到齐次裁剪空间,常用方式:o.pos = mul(UNITY_MVP, v.position)
  • 逐顶点光照
  • 输入后续阶段所需要的数据,如坐标,颜色等

图元装配:本阶段将顶点着色器输出的所有顶点作为输入,并所有的点装配成指定图元的形状;

几何着色器:几何着色器把图元形式的一系列顶点的集合作为输入,它可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状。

光栅化:这里它会把图元映射为最终屏幕上相应的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。在片段着色器运行之前会执行裁切(Clipping)。裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。

片段着色器:主要目的是计算一个像素的最终颜色,这也是所有高级效果产生的地方。通常,片段着色器会要使用3D场景的许多数据(比如光照、阴影、光的颜色等等),这些数据可以被用来计算最终像素的颜色。

测试

  • 深度测试:如果开启了深度测试,GPU会把该片元的深度值与已经存在于深度缓冲区的深度进行比较,决定是否舍弃该片元。
  • 模板测试:如果开启了模板测试,GPU首先读取模板缓冲区中该片元位置的模板值,然后将该值与读取的参考值比较,决定是否舍弃该片元

混合:对于不透明的物体,可以直接关闭混合操作,因为不透明的物体的颜色值可以直接覆盖掉颜色缓冲区的值。如果是透明的物体,让该物体的颜色值与颜色缓冲区的值进行混合。

2.有哪些坐标空间

  1. 模型空间(model space):也称为对象空间或者局部空间,是在每个模型的原点有一个坐标系,该坐标系跟随模型的移动或者旋转变化
  2. 世界空间(world space):它建立了我们所关心的最大空间。通常我们把世界空间原点放置在游戏空间的中心,一般用于描述绝对位置。
  3. 观察空间(view space):也称摄像机空间,可以认为是模型空间的特例,摄像机空间决定了我们渲染游戏所使用的视角,区别于屏幕空间,摄像机空间是3D的,观察空间到屏幕空间需要投影。定点变化的第二步,需要将定点从世界空间转换到观察空间。
  4. 裁剪空间(clip space):也称齐次裁剪空间:目标是能够方便的对渲染图元进行裁剪,裁剪空间由视椎体决定,用于变换的矩阵也是我们常说的投影矩阵(projection matrix).
  5. 屏幕空间:投影矩阵变换之后,我们可以进行裁剪操作。当完成所有的裁剪后,就需要真正的投影了,也就是我们需要把是椎体投影到屏幕空间,得到真正的2D像素位置。
  6. 切线空间:在实际制作中,法线贴图常常采用切线空间的法线纹理

question 1:法线要使用切线空间的优点:

  1. 自由度,移植性高:记录的是绝对法线信息,不依赖某个模型
  2. 可以进行uv动画
  3. 重用性,比如一个砖块的6个面可以使用一样的法线信息
  4. 可压缩,切线空间的Z总是正方向的,我们可以只存储XY,推断出Z

question 2:法线贴图为什么是蓝色:
这是因为切线空间保存每个法线贴图,每个法线方向所在的坐标空间不一样,即表面每个点有各自的切线空间。法线纹理其实就是存储了在每个顶点各自的Tangent Space中,法线的扰动方向。也就是说,如果一个顶点的法线方向不变,那么在它的Tangent Space中,新的normal值就是z轴方向,也就是说值为(0, 0, 1)。但这并不是法线纹理中存储的最终值,因为一个向量每个维度的取值范围在(-1, 1),而纹理每个通道的值范围在(0, 1),因此我们需要做一个映射,即pixel = (normal + 1) / 2。这样,之前的法线值(0, 0, 1)实际上对应了法线纹理中RGB的值为(0.5, 0.5, 1),而这个颜色也就是法线纹理中那大片的蓝色。这些蓝色实际上说明顶点的大部分法线是和模型本身法线一样的,不需要改变。

3. 几种反走样(抗锯齿)算法实现、问题、效率

1. SSAA(Super Sampling Anti-Aliasing)
从名字可以看出,超采样技术就是以一个更大的分辨率来渲染场景,然后再把相邻像素值做一个过滤(比如平均等)得到最终的图像(Resolve)。因为这个技术提高了采样率,所以它对于解决上面几何走样和着色走样都是有效果的。如下图所示,首先经对每个像素取n个子采样点,然后针对每个子像素点进行着色计算。最后根据每个子像素的值来合成最终的图像。
在这里插入图片描述

虽然SSAA可以有效的解决几何走样和着色走样问题,但是它需要更多的显存空间以及更多的着色计算(每个子采样点都需要进行光照计算),所以一般不会使用这种技术。顺着上面的思路,如果我们对最终的每个像素着色,而不是每个子采样点着色的话,那这样虽然显存还是那么多,但是着色的数量少了,那它的效率也会有比较大的提高。这就是我们今天想要主要说的MSAA技术。

2. MSAA(Multisample anti aliasing)
SSAA等于暴力渲染了4倍分辨率的图像,在目前的硬件条件下这种性能开销是不可接受的,因此在SSAA的基础上发展出了MSAA。
MSAA与SSAA的区别在于像素着色器(Pixel Shader)的运行次数。MSAA同样对于每个像素进行了4次子采样(Sample),但是只在像素中心位置运行一次像素着色,然后根据Sample是否被三角形覆盖而将像素着色的颜色复制到Sample上。注意这里有一个很重要的点,就是每个子像素都有自己的颜色、深度模板信息。
在这里插入图片描述
以上图为例,在前向渲染中,三角形的绘制是依次进行的。绘制蓝色三角形时,MSAA的具体执行步骤如下:

  1. 光栅化阶段,对四个X位置的Sample执行三角形覆盖判断,在一个四倍分辨率大小的coverage mask中记录每个Sample被覆盖的情况。
  2. 像素着色阶段,在像素中心圆点处执行像素着色器。该点的位置、深度、法线、纹理坐标等信息由三角形三个顶点重心插值得到。图中计算得到像素颜色为紫色。
  3. 对四个Sample执行模板测试与深度测试,并将测试通过的Sample数据写入四倍分辨率的模板缓冲与深度缓冲。每个Sample都拥有自己的深度值,依然是重心插值得到。
  4. 上图中左下两个Sample通过了深度测试,并且coverage mask为1,因此将紫色复制到这两个Sample对应的颜色缓冲中(依然是每个Sample一个颜色,共四倍大小)。其他两个Sample暂为背景色。
  5. 重复上述流程绘制第二个黄色三角形,将像素着色获得的黄色复制到右上角的Sample中。
  6. 所有绘制结束之后,通过一个对高层透明的PASS,将四个Sample的颜色插值获得最终输出的像素颜色。

可以看到在MSAA流程中所使用的所有缓冲区都变成了原来的四倍大小,这也是为什么MSAA增加了非常多的显存和带宽消耗。上述流程中第4步如果改成对每个Sample运行像素着色,MSAA就变成了SSAA。

3. FXAA( Fast Approximately -Aliasing)
FXAA拥有一种很朴素的算法思想。 它的思路很简单,既然我们要进行图像抗锯齿,那总得区分出来哪些像素是锯齿,哪些像素不是。根据图像锯齿的成因和日常的经验我们知道,它通常出现在与背景呈现高对比度的物体边缘(视觉比较明显)。FXAA是种后处理技术,后处理技术一般在画面完成后,通过像素颜色检测边缘(色彩差异太大时,不是边缘也被认为成边缘,精度有问题)。后处理技术一般没倍数概念,因为不存在放大。效果上FXAA接近MSAA的4倍效果,但在一些细节上会比4倍MSAA差一点。

4. FXAA( temporal anti-aliasing)
从时间维度上进行抗锯齿处理,使用同个像素在不同帧上的不同采样点,根据时间先后进行一个加权平均计算;但是TAA也有缺点,就是容易出现鬼影和抖动的现象;

5. DLSS( Deep Learning Super Sampling)
DLSS 2.0首先也是一个基于多帧的图像重建技术。DLSS2.0抛弃了人肉手调的启发式算法,用一个在超级计算机上数万张超高质量图片训练的神经网络来代替这些Heuristics。就像深度学习在几年前在计算机视觉领域超越了各种手调的特征提取算法一样,深度学习第一次在实时渲染中也非常合理的跑赢了图形领域的手调算法。
用DLSS 2.0重建的渲染图像序列达到了非常高的多帧样本利用率,这也是为什么只用四分之一的样本就可以重建出媲美原生分辨率渲染的图像质量的原因。
DLSS 2.0加速渲染的原理很简单。开启DLSS后,引擎的渲染会在1/2到1/4像素的低分辨率下运行。这意味着,一大半的像素级别的计算直接被粗暴的砍掉了。像素级别的计算通常包括GBuffer的渲染,动态光源、阴影的计算,屏幕空间的特效例如屏幕空间环境遮挡(SSAO)、屏幕空间反射(SSR),甚至实时光线追踪。
所以DLSS 2.0的加速多少,也直接取决于像素计算在多大程度上是性能瓶颈。通常来说,画面越好的3A大作,越会用更加耗费性能的渲染技术,像素也就会更大程度的成为瓶颈,而DLSS则会提供更大的加速!

4. 边缘检测

边缘检测的原理是利用一些边缘检测算子对图像进行卷积操作。
什么是卷积?
在图像处理中,卷积操作指的就是使用一个卷积核( kernel)对一张图像中的每个像素进行系列操作。卷积核通常是一个四方形网格结构(例如2×2、3×3的方形区域),该区域内每个方格都有一个权重值。当对图像中的某个像素进行卷积时,我们会把卷积核的中心放置于该像素上,如图所示,翻转核之后再依次计算核中每个元素和其覆盖的图像像素值的乘积并求和,得到的结果就是该位置的新像素值。
在这里插入图片描述
常见的卷积核:
在这里插入图片描述

常见的边缘检测算子如图所示,它们都包含了两个方向的卷积核,分别用于检测水平方向和竖直方向上的边缘信息。在进行边缘检测时,我们需要对每个像素分别进行一次卷积计算,得到两个方向上的梯度值Gx和Gy,而整体的梯度可按下面的公式计算而得:
G = G x 2 + G y 2 \sqrt{G_x^2 + G_y^2} Gx2+Gy2
由于上述计算包含了开根号操作,出于性能的考虑,我们有时会使用绝对值操作来代替开根号操作:
G= ∣ G x ∣ |G_x| Gx+ ∣ G y ∣ |G_y| Gy
当得到梯度G后,我们就可以据此来判断哪些像素对应了边缘(梯度值越大,越有可能是边缘点)。

5.Shadowmap

阴影映射由两个步骤组成:
一:以光源为视点,正交投影渲染整个场景,得到深度图(shadow map)并保存变换矩阵。深度图中记录了以光源为视点时,所有的可视点的深度。
二:以相机为视点渲染场景,对于场景中的每个顶点,将其变换到以光源为视点的空间,若其深度大于shadow map中对应点的深度值,则说明光源射来的光线被物体遮蔽了。则该点处于阴影中

float ShadowCalculation(vec4 fragPosLightSpace)
{
    // 执行透视除法
    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
    // 变换到[0,1]的范围
    projCoords = projCoords * 0.5 + 0.5;
    // 取得最近点的深度(使用[0,1]范围下的fragPosLight当坐标)
    float closestDepth = texture(shadowMap, projCoords.xy).r; 
    // 取得当前片段在光源视角下的深度
    float currentDepth = projCoords.z;
    // 检查当前片段是否在阴影中
    float shadow = currentDepth > closestDepth  ? 1.0 : 0.0;

    return shadow;
}

// 计算阴影
    float shadow = ShadowCalculation(fs_in.FragPosLightSpace);       
    vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * color;    
    FragColor = vec4(lighting, 1.0f);

6.向前渲染跟延迟渲染

  1. 向前渲染:
    前向渲染路径是传统的渲染方式,也是最常用的一种渲染路径。前向渲染路径的原理每进行一次完整的前向渲染,我们需要渲染该对象的渲染图元,并计算两个缓冲区的信息:个是颜色缓冲区,一个是深度缓冲区。我们利用深度缓冲来决定一个片元是否可见,如果可见就更新颜色缓冲区中的颜色值。
    对于每个逐像素光源,我们都需要进行上面一次完整的渲染流程。如果一个物体在多个逐像素光源的影响区域内,那么该物体就需要执行多个Pass,每个Pass计算一个逐像素光源的光照结果,然后在帧缓冲中把这些光照结果混合起来得到最终的颜色值。假设,场景中有N个物体,每个物体受M个光源的影响,那么要渲染整个场景一共需要N*M个Pass可以看出,如果有大量逐像素光照,那么需要执行的Pass数目也会很大。因此,渲染引擎通常会限制每个物体的逐像素光照的数目。

  2. 延迟渲染:
    前向渲染的问题是:当场景中包含大量实时光源时,前向渲染的性能会急速下降。例如,如果我们在场景的某一块区域放置了多个光源,这些光源影响的区域互相重叠,那么为了得到最终的光照效果,我们就需要为该区域内的每个物体执行多个Pass计算不同光源对该物体的光照结果,然后在颜色缓存中把这些结果混合起来得到最终的光照。然而,每执行一个Pass我们都需要重新渲染一遍物体。除了前向渲染中使用的颜色缓冲和深度缓冲外,延迟渲染还会利用额外的缓冲区,这些缓冲区也被统称为G缓冲( G-buffer),其中G是英文 Geometry的缩写。G缓冲区存储了我们所关心的表面(通常指的是离摄像机最近的表面)的其他信息,例如该表面的法线、位置、用于光照计算的材质属性等
    延迟渲染的原理:延迟渲染主要包含了两个Pass。在第一个Pass中,我们不进行任何光照计算,而是仅仅计算哪些片元是可见的,这主要是通过深度缓冲技术来实现,当发现一个片元是可见的,我们就把它的相关信息存储到G缓冲区中。然后,在第二个Pass中,我们利用G缓冲区的各个片元信息,例如表面法线、视角方向、漫反射系数等,进行真正的光照计算。

区别:
(1)区别:正向渲染,先执行着色计算,再执行深度测试;渲染n个物体在m个光源下的着色,复杂度为O(n*m),光源数量对计算复杂度影响大;对于正向渲染,我们通常会对一个像素运行多次片段着色器;延迟渲染,先进行深度测试,再执行着色计算;对于延迟渲染,每一个像素只会执行一次片段着色器。
(2)延迟渲染的优点:将光源的数目和场景中物体的数目在复杂度层次上完全分开。渲染n个物体在m个光源下的着色,复杂度为O(n+m),只渲染可见的像素,节省计算量;
(3)延迟渲染的缺点:

  • 不支持真正的抗锯齿(anti- aliasing)功能。
  • 不能处理半透明物体。
  • 对显卡有一定要求。如果要使用延迟渲染的话,显卡必须支持MRT( Multiple Render Targets)、 Shader Mode3.0及以上、深度渲染纹理以及双面的模板缓冲。

7.

参考

[1].learnopengl-cn
[2].Unity Shader入门精要 ,冯乐乐 pdf链接:https://pan.baidu.com/s/1USBgxsxyB0B5ZWDEVZWSQw 提取码:7iin
[3].深入剖析MSAA
[4].learnopengl-cn
[5].DLSS 2.0 - 基于深度学习的实时渲染图像重建

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 腾讯android社招面试题除了要求基本的编程基础外,更加注重应聘者的思路和解决问题的能力。例如,有一道题目是让应聘者解决一个弹球游戏的碰撞问题,考察了应聘者对物理学原理的了解程度和建模思维的能力。而另外一道题则是要求应聘者自己设计一个数据结构,并写出相关的代码,考察了应聘者对数据结构的掌握程度以及解决实际问题的能力。 此外,腾讯面试也注重应聘者的团队合作能力,例如会询问应聘者过往参与的项目经历以及其中的角色和职责。面试官还会关注应聘者平时的兴趣爱好和学习方式,看重自我发展和持续学习的态度。 总的来说,腾讯android社招面试注重应聘者的思考方式和解决问题的能力,希望应聘者能够在面试中展现出技术实力的同时也能够体现出比较全面的素质和团队合作技巧。 ### 回答2: 首先,我认为腾讯是一家非常优秀的公司,成熟的技术、稳定的业务、开放的文化及良好的薪酬待遇,这些都是我希望所在公司所具备的。当然,现如今,安卓技术在移动互联网领域已经越来越普及,所以我对腾讯Android社招面试也充满期待。 鉴于腾讯一贯的严谨和高要求,我认为其社招面试中会涉及到个人技能、团队协作能力、沟通能力及学习能力等,且可能会采用多种形式的测试与考核方式,如笔试、技术面试、项目经历考核等。而我会尽最大努力,从细节和思路等方面准备自己,积极展示自己的能力和素养,在适当的时候提问与反问,来表现出自己的个性魅力和职业素养。总之,我会以最好的状态去应对腾讯Android社招面试,尽我所能去展现自己,来赢得这个职位的机会。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值