反射的N种渲染方式

概述

  • 本节主要介绍游戏图形渲染中几种反射效果的原理、实现方式及其应用场景

  • 主要内容

  • 反射在游戏尤其是3D游戏中的使用场景

  • 从原理、优缺点及适用场合等几个方面,分别分析四种反射效果的实现方法

  • 反射效果的性能优化技术,如立方体影射的原理、如何处理平面反射等

版权声明

  • 本文为“优梦创客”原创文章,您可以自由转载,但必须加入完整的版权声明

  • 文章内容不得删减、修改、演绎

  • 本文视频版本:见文末

反射的应用场景

  • 上图中,展示了玻璃反射整个游戏场景的效果

  • 上图展示了地面实时反射的光影效果

  • 上半部分是开启地面反射的效果,下半部分是关闭后的效果

  • 可以看到,开启地面反射后,相对于不开启,地面阴影的细节更丰富,效果更好

  • 并且,针对不同的介质,光影的反射效果是不同的,比如:

  • 针对水面,最终呈现出来的是镜面反射效果

  • 而凹凸不平的地面,最终的呈现就会显得更加斑驳不清一些

  • 上图中,展示了水体反射的效果

  • 左边没有开启水体反射,右边则是开启后的效果

  • 上图中,展示了立体光球对周围环境的反射

  • PS:前面展示的三类反射的应用,都是基于平面的反射,而这里,是非平面的反射应用场景

  • 显然,反射不仅可以用在平面上,在非平面上,也是可以实现的,只是他们之间的实现方式是不尽相同的,而这也正是本节我们想要来分享和探讨的核心问题,除此之外,我们还将进行以下讨论:

               i. 每一种反射方案之间的区别

               ii. 图形学中流行什么样的方案

               iii. 在我们的实际游戏中,比如手机游戏领域会如何进行选择

  •  接下来,我们分别介绍反射的四种渲染方式

环境映射

  • 环境映射,又被称为立方体映射,这是实现反射效果的第一种方式,也是Unity内置的反射实现方式

环境映射实现的基本原理

  • 这种方案的核心实现方式是,使用摄像机对物体所在位置的上下左右前后六个方向,各拍摄一张照片,并存入到环境贴图中(如上图右)

  • 通过上述方式拍出来的图片有点像是一个立方体的盒子,这个立方体盒子将我们的反射物体包裹在里面

  • 拍摄的上下左右前后6张图片,每张图片就对应立方体的六个面,而这六个面就已经包括了场景里面所有的图像

  • 这样一来,只需要对图像上下左右前后进行一个纹理贴图的采样,便可以获取场景里任意一个点的反射信

  • 获得环境贴图后,将该环境贴图贴到物体的表面,再配合专门的Shader,这样,反射效果便呈现出来了

  • 可以看到,上图中的茶壶能够反射整个场景环境信息,并且,这个反射还是实时进行的

  •  PS:上述效果,使用Unity内置反射探针(Reflection Prob)就可以实现

  • 环境映射存在的问题

  • 由于上述方案的反射是实时进行的,那么呈现出来的每一帧画面,都需要对场景的上下左右前后6个方位都拍一张照片,这个性能开销是比较大的

  • 优化方案:每一帧只拍一张照片

  • 具体操作步骤

  • 游戏正常一秒钟60幅画面,如果我们在第一到六帧分别拍摄物体的前、右、后、左、顶、底,第七帧之后循环以上步骤,那么,我们每一帧需要对场景环境进行拍摄的次数,就从6次减少到了只多拍摄一次(正常的场景渲染要拍一次),这样,就比拍6次节省了5次拍摄的性能开销

  • 存在的问题

  • 由于每一帧只拍摄一幅画面,会造成一定的穿帮,比如,你看到的其他帧的画面是前面几帧的,表现在呈现效果上就是,当物体移动比较快的情况下,可能物体已经远离了,但阴影仍然留在原地,这个过程最长会多保留5帧

  • 什么时候使用此优化方案

  • 如果反射对于游戏来说并不特别重要,或者游戏里的物体移动不是那么明显,就比如说赛车游戏,赛车沿着一条道路去行进,行进是比较规律的,周围的环境通常变化的不会那么快,就可以采用此方案

思考

  • 上图中的问题,我们在接下来将一一讲解

进阶1:环境贴图的有哪几种格式?

  • 环境贴图有几种格式这个问题,无论是对于技术美术,还是对于一般的Unity程序员,都是需要掌握的

  • 前文中在“环境映射实现的基本原理”中给大家展示的方案,即通过一个立方体拍摄六张照片来进行存储的方案,就是其中一种方式,在该方案中,使用的是常见的横向立方体贴图(上图1)

  • 除了横向立方体贴图,还有纵向立方体贴图(上图2),纵向长条贴图(上图3),横向长条贴图(上图4),全景贴图(上图5)

  • 接下来我们以立方体贴图为例,来看一看Unity,或者说显卡,是如何根据我们传入的xyz分量值,计算出来我们要在立方体的哪一个方向的图片上进行采样的

进阶2:立方体映射原理【面试题】

  • 上图展示了显卡是如何根据传入的xyz分量值,计算出来我们要在立方体贴图的哪一个面上进行采样的

  • 前置知识:立方体映射在Shader中,使用texCUBE(cube,float4(x,y,z,w))函数进行纹理采样

  • 第一个参数cube,即我们上面拍摄的六个面的立方体贴图

  • 第二个参数float4,代表了采样的方向,这个方向,通常是传入一个四维向量,其中的x、y、z分量用来代表要采样的方向,w分量则用来控制采样时画面是清晰的,还是模糊的。比如,要实现哑光或磨砂材质的时候,就可以通过配置不同w分量的值,来使得贴图采样的结果,最终呈现出哑光,或者是磨砂效果

  • 那么,接下来,我们进入正题:

  • 显卡是如何根据我们传入的xyz分量值,计算出来我们要在哪一个方向的图片上进行采样的呢?【面试题】

  • 基本流程

  1. 如上图左,假定有一条光线,起点是眼睛所在位置,方向向右,射向橘黄色立方体所在位置

  2. 根据法线,我们就能获得这条入射光线的反射方向

  3. 最终,我们将根据计算出的反射方向,来确定在立方体贴图的哪个方向、哪个像素点进行采样

  • 流程详解

  • 什么是法线?

  • 如上图,跟立方体的平面垂直的方向,叫法线(上图左中的N),法线是一条射线,其方向是远离垂直平面的方向

  • 如何根据反射方向获得要采样的面?

  • 反射方向是一个向量,具有xyz三个坐标

  • 上图中,可以注意到,我们的每一个面都有一个标记,比如说第0(face 0)个面朝向x轴正方向;第1个面(face 1)朝向x轴负方向,以此类推,比如说我有一个采样的纹理坐标,这个纹理坐标是(0.2,0.3,0.8),其中坐标值最大的在z轴(0.8),根据这个,我们就能确定采样的方向在z轴

  • 接下来,我们把所有的坐标值根据0.8进行放大,放大到1(即单位化)。同时,在把z轴的0.8单位化的过程中,其它两个坐标也同时等比放大,比如x轴从0.2放大到0.3,y轴从0.3放大到0.4

  • 注意:

  • 上述对x、y轴的值的放大只是举例,不是精确的计算,uv坐标在映射的时候还需要从[-1,1]映射到[0,1]。相关原理解释详见“TA全栈”项目:TA全栈开发大师 V2024(公开课)

  • 通过上述对z轴的单位化操作,我们就确定了,该坐标方向是一定在z轴的正方向上面

  • 接下来,就可以以u方向为0.3,v方向为0.4来在这个立方体的背面来采样某一个像素点

  • 采样点在立方体上的位置示意

  • 采样点在立方体贴图上的位置示意

  • 在面试中,如果你能回答到上面的程度,那么你就能拿到80%的分数了。如果你能够精确的掌握“立方体纹理采样的算法原理”,那么你可能会对这个问题有更加清晰的认知,在应对面试官的具体面试问题时,你可以回答的更游刃有余一些;同时,在使用纹理贴图的时候,你的思路也会更加清楚,更能把握纹理贴图的进阶用法

  • 上图是立方体纹理采样算法实现,感兴趣的小伙伴可以参考研究一下

  • 拓展:性能开销对比

  • 总体来说,立方体贴图采样的性能开销并不会比2D纹理贴图采样大,它的开销仅仅是在于立方体贴图的纹理映射算法比2D纹理映射复杂一点,但是在具体的采样上并没有没有比2D的纹理采样更复杂

  • 接下来我们来看看第3个进阶点,立方体映射的优缺点

进阶3:立方体映射的优缺点

  • 上图展示了3种映射方式

  • Classic Cube-map Environment Mapping:经典立方体贴图环境映射

  • Box Projected Cube-map Environment Mapping:盒体投影立方体贴图环境映射

  • Flat Mirror:平面反射镜

  • 接下来,我们会围绕这三种映射方式,来一起分析、对比一下立方体映射各种实现方式之间的优缺点

  • 上图中,罗列了立方体贴图的整体优缺点,在此就不一一展开描述了,我们接下来,只对其缺点进行阐述

  • 立方体贴图的其中一个缺点,就是对平面反射的效果支持不好

  • 由于立方体贴图自身的技术限制,在进行反射的时候,跟物体本体不能形成严丝合缝反射面

  • 上图中圈出来的部分,都是存在问题的地方,比如窗户底下这一块,正常应该是没有反射的

  • 在经过Box Projected技术矫正之后,反射的效果会有比较明显的改善,但仍然存在问题

  • 上图中,绿色线条框选出来的部分,都是存在问题的地方,比如,左下角的侧面反射是扭曲的,窗台下跟窗户相对位置的反射高度是不对的,显得不太真实,最好的效果是Flat Mirror,也即是平面反射

  • 对比可见,平面反射的最终效果最好,它基本规避了上述两种映射方式所产生的所有错误效果,在这里,我们可以记住一个原则:

  1. 对于曲面的物体,用立方体映射比较好,用平面反射不太好,或者说平面反射是不能做曲面反射的

  2. 对于平面的物体,最好不要用立方体映射的反射技术

  • 比如说地平面、墙的侧面等平面物体,最好使用平面反射技术

  • 既然平面反射在处理非曲面物体的反射效果上表现这么好,那么它是如何实现的呢?这个我们后面再讲。在此之前,我们很有必要讨论一下另一个问题:

  • Box Projection对经典立方体映射的校正是怎样实现的?

进阶4:Box Projection的原理【面试题】

  • 首先,我们看一下这个效果

  • 图中,通过玻璃橱窗能看到橱窗内部的各种物件,非常真实,像是里面真的摆放了各种模型(其实不是);并且从不同角度看去,获得的视觉效果也会跟着改变

  • 该效果的实现,使用了一种类似于立方体贴图的方式,加上类似于Box Projection这种校正技术,产生了一种景深的视差效果,这种技术叫做Interior Mapping

  • 优点:

  • 不需要真实建模,一张立方体贴图加上Interior Mapping技术就可以实现

  • 渲染性能开销非常低,不需要光追,手机上也可以跑

  • 那么究竟Box Projection的原理是什么?它为了解决什么问题?相信看到这里的小伙伴们,心里已经有了大概得答案,这也是一个典型的面试题,大家一定要搞懂它

  • 如果想要更深入的了解,也可以跟雷蒙德老师预约面试模拟

平面反射如何实现?

  • 上图是《底特律变人》中的游戏画面

  • 可以看到,其地面反射,无论是干地面,还是湿地面,是瓷砖地面还是大理石地面,都有比较好的效果。这里,它就采用了平面反射技术

  • 平面反射技术有几种实现方式,第一种方式是使用反射相机原理,第二种是使用屏幕空间反射(SSR),接下来我们就来逐一了解一下

反射相机

  • 上图中展示了反射相机的原理

  • 游戏中,正常的渲染流程是:

  • 首先,在游戏里架设一盏摄像机,去观察整个场景

  • 然后,整个场景会通过3D图形学的变换,产生一个透视效果

  • 最终,我们就可以通过在场景里面架设的摄像机,观察到了整个场景渲染在屏幕上

  • 那么如果我们要渲染平面反射,该怎么去计算呢?如上图左我们只需要在摄像机相反的方向往下做垂线,这个垂线跟我们的平面垂直垂线一直往下,做一个镜像,然后在跟我们的这个平面高度相同、方向相反的位置,架设另外一盏摄像机,这台摄像机的作用也是对场景进行拍照

  • 比如说我们的主摄像机是朝下的方向,那么,反射相机就是跟它相反的,朝上来进行拍照,这个时候,本来场景里面有个笑脸是正着的,当笑脸从反射相机里面看,就倒过来了

  • 这就是反射相机的原理,上图右中的效果也同理

反射相机进阶

  • 反射相机技术还具有很多进阶点,比如:

  1. 如何实现模糊平面?

  • 如上图,可以比较清晰的感知到,在靠近我们眼睛的位置,反射是比较清晰的;而远离我们眼睛的位置呢,有一层发白, 有一种模糊的感觉,这个是怎么模拟出来的?

  • 2.如何实现控制反射的物件?

  • 在场景里面有一些物件是需要反射的,有些物件是不需要反射的。那么我们怎么样控制哪些物件需要反射?哪些不需要反射?

  • 3.平面反射强度不是均匀的,如何体现反射强度变化?

  • 4.……

  • 在做一些技术美术效果的时候,并不是说只是实现一个简单的效果就可以了,更多时候还需要我们去细细打磨,在这个过程中,我们不仅仅需要知道应该要做什么效果,还要知道要能往什么方向去提升效果,可能大家没有这个思路,而这正是我们所专长的地方,感兴趣的小伙伴可以联系我们预约作品指导,获得更进一步的学习和帮助

反射相机的渲染开销问题

  • 反射相机应该来说是一种比较完美的一个反射方式,它的优点,是能把场景里面本来看不见东西也反射出来,这个是使用SSR或者SSRP技术所做不到的

  • SSR或者SSRP技术是屏幕里面有什么,它才能反射什么;没有的它就反射不了而反射相机,则能很完美的实现这个效果。但是,大部分的游戏都不会采用反射相机,其核心原因就是它的渲染开销确实很大,在实时环境下,反射相机的开销比立方体环境映射要小一些,但是整体开销仍然不小

  • 那么判断依据是什么呢?

  • 假设我们游戏里面有场景、角色,如果有200个渲染调用,也就是说要把场景里面的物件跟角色分成200次渲染调用,让我们的显卡来进行渲染,由于此时除了主摄像机, 还架设了一个反射相机,所以说要把这200个需要反射的物体再渲染一遍,那么总体的渲染的绘制调用次数就是400次,在我们的图形显卡当中,现在的图形显卡硬件都很厉害,渲染个几万个面毫无压力,几十万个面也是,如果将这些渲染放在一个批次里面,渲染性能也还可以,但是问题是,如果渲染的次数比较多,比如说一秒一帧,一帧400次渲染,调用400次DrawCall,这种情况下,即使顶点数量很少,显卡渲染的速度也是非常慢的

  • 具体为什么,大家可以看我的关于GPU硬件和GPU优化的公开课(文末加我领取),在这就不再赘述了

  • 需要知道的一点,就是反射相机每渲染一次,渲染开销就需要双倍,DrawCall乘以二,这个过程中,渲染相机的开销没有办法避免,一定是两倍的开销,这是反射相机的第一个问题。

  • 接下来我们来看看反射相机的第二个问题:如果突然进入一个带反射的复杂场景,会造成帧率不稳定。比如说从一个室外场景(没有反射)进入到一个室内的一个大理石教堂(有反射了),也就是从一个简单场景进入到一个带反射的场景,这个场景可能还很复杂,那么你整个游戏的帧率会突然卡一下,给用户的体验,就像这个游戏卡bug了一样

  • 那么上述两个问题是否存在解决方案呢?答案是有的,也就是接下来我们要讲的,SSR技术

SSR

SSR效果

  • 上图展示了太阳在水面被反射的效果

  • 上图展示了立方体和球体在水面的反射效果

  • 相对反射相机来说,SSR既有反射效果,又能不让渲染帧率翻倍,并且能在一个DrawCall里面完成反射渲染,优势很明显

  • 下面,我们来了解一下其具体的实现原理

SSR的原理

  • SSR的最基本原理是利用屏幕空间的深度、法线、颜色信息进行RayMarching,得到反射的采样在屏幕上的颜色

SSR的步骤

  • 上图中展示了SSR实现的大体流程

  • 首先,我们需要一个渲染的场景(上图中的“Shaded scene”图)

  • 然后,根据渲染场景,我们可以获得其法线信息(上图中的“Normals”图)

  • 法线信息代表场景中地面、墙壁(或者说门帘)的反射信息,不过相对来说,法线信息不是很重要,我们更需要关注的是深度信息

  • 以及深度信息(上图中的“Depth”图)

  • 再然后,就可以根据深度信息,获得渲染出来的场景的反射信息

  • 上图中红框框选的部分,即反射信息

  • 最后,当根据场景的深度信息把底下的反射信息给渲染出来以后,再把它叠加到原来的地面上,我们就可以在地面上看到反射了,这就是我们的反射呈现的全部流程

RayMarching示意图

  • 那么什么叫RayMarching?

  • RayMarching就是对反射的颜色进行采样

  • PS:将这个采样的结果混合到地面上的技术,就是SSR技术

  • RayMarching技术的算法实现起来比较复杂,我们会把它放在系统的学习的时候去讲,在这里,我们仅简单进行一个原理的讲解,或者,我的另外一个“体积云”的公开课里面也有对RayMarching技术的详细介绍,如果你感兴趣的话,可以关注一下我的这个公开课

  • 在RayMarching时,我们要进行一个反射的计算

  • 比如说,从上图中的眼睛看向地面上的一个点A,这个点有其对应的反射点(此例中为墙面上的B点),我们需要计算这个反射点的位置,此时就需要利用到包含深度信息的深度图(上图右上的Depth图)

  • 接下来我们通过下面这个场景来具体展开一下

  • 上图中,展示了入射光线如何通过深度图、法线图,最终采样到反射像素点的颜色

  • 如果射线从上图眼睛处发出,通过深度图,我们就可以确定,在射线方向的什么位置会碰到一个物体,如上图,射线最终碰撞到“可被反射物体A”。碰到这个物体后,我们再通过法线图,就可以知道碰撞点的法线朝向,在知道法线朝向后,很容易就能算出来一个反射方向

  • 再然后,我们沿着反射方向,再次通过深度图去找,在另一个位置我们又碰到“另一个物体B”,那么我就取这个碰撞点的颜色,把它渲染在我们的“采样颜色最终呈现位置点C”

  • 到此为止,整个的反射就渲染完了,这就叫做RayMarching技术

  • RayMarching的整个的运算,是在屏幕后期处理的时候完成的,也就是类似于我们讲的美颜阶段,美颜就是拍照片拍完以后,照相机通过美颜算法,可以把相貌变得更美丽。同样的,我们的RayMarching也是在整个场景拍照,把场景照出来了,再做一个后期处理,获得场景的深度,获得场景的法线,计算反射方向,计算反射点的颜色,然后把它贴在原图上面,流程和美颜类似

  • RayMarching的原理就这么简单,但如何利用深度图进行反射点采样,是SSR技术的一个难点,需要比较多的图形学知识,也需要对Unity的理解

  • 关于SSR的基本原理,我们就先讲到这里,如果你想要系统的学习如何实现,或者想知道如何通过算法把这个效果加到你的作品里面,那你也可以去系统学习我们的“TA全栈”TA全栈开发大师 V2024(公开课)

  • 接下来,我会给大家讲一个更进阶的一个内容。不过在讲这个内容之前,我们先来看看SSR的问题所在:SSR并不是没有缺点的

SSR的问题

  • SSR主要具有以下2个缺点:

  1. SSR必须依赖于颜色、深度和法线缓冲

  • SSR依赖于场景里面必须要渲染一个场景颜色图,也需要场景渲染深度信息、法线信息

  • 简单的场景项目,可能并不需要渲染深度图和法线图,这种情况下,单独为了SSR去进行渲染,就会产生额外的开销,然而,这并不是最紧要的问题,因为什么原因呢?因为在URP管线,或者HDRP管线里,大部分时候我们都是需要深度和法线信息的,没有这些东西很多效果做不出来,所以说。这个倒不是什么特别大的问题

  • 并且,如果熟悉URP管线的定制的话,很容易就能够获取到这几个图的信息,进而把效果渲染出来。相对来说,下面的第二个问题会更紧要些

2. RayMarching开销巨大,不适合移动端

  • RayMarching技术虽然是在一个Pass里面就能够完成这个反射的计算,但是它这一个Pass的开销是比较大的,因为它是往屏幕上面的每一个像素点去发射射线,并且,它发射出来的这个射线,不是说一次性的发射一个射线就计算一个反射,而是要一步一步的往前试探

  • 这里是一个核心的面试点:

  • 它是一步一步的往前走,一步一步的往前走,一步一步的往前走,每走一步其实都是for循环的一次循环,都会产生一次循环的指令开销。而想要效果做得好,就需要每一次走的步数尽量小,因为步数小效果就会更精确;但与此同时,这样的操作,就会带来更大的渲染的循环开销

  • 综上所述,这就是RayMarching的问题所在。我们在移动端使用的时候,通常需要把RayMarching迭代的步数降低,或者不采用RayMarching技术,而是使用SSPR技术

SSPR

  • SSPR,比SSR多了一个“P”(Plane),就是平面的意思,也就是说,这种技术只能用在平面的反射上,但是它对平面反射做了高度的优化,使得它既能在一个Pass里去完成渲染;同时,这一个Pass又不需要像SSR一样进行那么多次的循环

SSR vs. SSPR

  • 上图是SSR和SSPR的对比, 写得很清晰,这里就不再一一展开说了

  • 值得提到的一点是,SSPR优化的核心点,在于它只能基于平面进行反射,因此可以直接算出反射点的世界坐标;而SSR,由于它能针对任意的平面,所以说不能够直接通过入射方向直接算反射方向,而是需要根据法线方向的不同,算出不同的反射方向。并且SSR还需要进行RayMarching;SSPR不需要RayMarching,也不需要做RayMarching的优化

SSPR渲染流程

  • 上图是SSPR的渲染流程

  • 具体的技术,我会在系统学习的时候详解,这里就不深入了,因为它跟SSR原理是差不多的,只不过它会更简单一些,大家需要的话可以根据我在上图中提供的算法再自己去推论一下

小结:反射的四种实现方式

  • 那这样的话,我们就把反射的四种实现方式给大家分析清楚了,这里再给大家做一个总结

  • 我们介绍了四种在游戏实时渲染当中的反射方式

  • 第一种方式:使用环境映射的方式

  • 环境映射方式是 unity 本身内置支持的一种方式,你只要在场景里面放入一个 Reflection,就能够进行环境反射。

  • 环境映射方式的缺点是不能用于一些平面的反射,因为平面反射的时候会出现反射跟原物体不对齐的情况。你可以使用 Box Projection 来去进行校正,但是即使校正,也不能做到完全的严丝合缝的平面反射

  • 在讲环境映射的时候,我还给大家讲到了环境映射时候的一些重要的面试考点,比如立方体映射的原理,比如Interior Mapping 技术

  • 第二种方式:使用反射相机

  • 对于一些平面物体,我们可以使用反射相机来进行反射,反射相机可以获得一个完美的反射效果,对场景里面看不到的物体进行反射,用反射相机可以做到,而用 SSR 或者 SSPR ,都是反射不到的

  • 但是反射相机也有它的问题:反射相机它会让我们的渲染的开销直接翻倍,那这个对于我们的设备的性能压力是非常大的。所以说,我们就引入了我们的第三种方式,就是使用基于屏幕空间的一个反射

  • 第三种方式:使用基于屏幕空间的反射(SSR)

  • 这种屏幕空间反射可以基于任意的物体表面,不管你是曲面、球形还是平面,它都可以反射

  • 但是 SSR 本身是需要用到一步一步的去求得反射点,效率是比较低的,用在移动端呢,需要经过大量的优化,所以我们说还有另外一种方式就是使用 SSPR

  • 第四种方式:使用SSPR

  • SSPR 只能用于平面,但是它是经过高度优化,它可以在一个 Pass 里面去实现反射

  • 唯一的问题就是它不能反射屏幕以外的物体

进一步学习的内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值