GAMES106-现代图形绘制流水线原理与实践 - P10:10. 纹理生成与压缩 - GAMES-Webinar - BV1Uo4y1J7ie
ok ok不好意思,今天晚了一些,大家好,各位同学,大家好,我是胡一伟啊,我之前刚刚从耶鲁大学毕业,现在在罗比research做research scientist。
然后我今天呢给大家讲的这个课程的名字,就叫做这个纹理生成与压缩,我们今天的内容主要分为三个部分,第一个部分是纹理的简介,第二部分是简,然后讲一下纹理是如何生成的,一般的方法。
然后最后的话讲一下纹理的使用和压缩。
首先我们简介一下什么是纹理,纹理是一个非常常见的一个单元。
在整个这个流水线中,一般来说的话,我们谈到纹理,就首先我们会把它与这个材质联系在一起,就是说如果有一个场景的话,它只有几何建模,如果没有纹理的话,那么这个场景渲染出来的话就没有颜色。
或者说它根本就没有办法被渲染,而纹理或者材质的话,定义了这个几何物体,它如何与光进行交互的,我们整个渲染过程,在整个渲染过程中,纹理是或者材质是不可或缺的,一般来说的话。
就是如果我们讲到更细节一点的纹理定义,通常来说,它在这个程序中被表达为一个多维的固定数组,比如说一维的,二维和三维的数组,里面呢可以储存不同类型的元素,比如说浮点类型,整形等等。
它本质上可能是一个就是一个固定的,多维的固定速度,所以呢他除了这个材质的表达之外,它还可以表达深度,还有这种帧缓存等等,而这节课,我们主要还是围绕着这个材质的表达来展开,因为这也是材质它最多应用的地方。
在整个这个图形学的渲染中的话,材质是非常重要的组成部分,就是几何材质,它都是怎么说呢,你要去渲染以后,这种非常真实的图片,这两个是非常非常就是关键的,它在数学上一般会定义为这个b2 d f。
就是这个双向分反射分布函数,如果同学们之前学过就是图形学的基础课程,肯定对这个完全不陌生,因为它这个它是这个渲染方程中的一个,核心的组成部分,这个渲染方程大概就是定义了这个。
比如说一束光如何与这个几何表面啊,一个不透明的几何表面如何进行交互,然后这束光比如说我们想计算这个出射光的话,我们相当于要对所有的入射光进行一个积分,而这些入射光它相当于它的这个反射率。
就是这像这样一个这个b2 d f,就定义了这样一个入射光的反射率,通常来说的话它是一个4d4 维的函数,为什么他这个因为这个反射力的话,他必须要有这个入射方向和出射方向,两个方向做定义,而每个方向呢。
它通常来说它可以在这个球面坐标系中所定义,给定义在一个单位球上,所以它一般来说会被参数化为theta和phi,因此它一共是四维的,也就是说出射光它一个sea和fi,入射光它也seed和fi。
所以是一个四维函数,而这个四维函数呢,你想想看这个四维函数它维度非常的高,通常来说的话嗯,如果大家学过之前的基础的图形学课程,知道,如果你要得到这个b i g f,通常可能需要一些那个。
比如说仪器对这个对一个物体进行采集,而且他描述的往往是一个单一的材质,比如说啊铝啊,这种纸板啊,对不对等等,但是真实世界上的很多的物体的话,实际上是由各种各样的材质所组成的。
因为比如说就比如说你周围的这种地板啊,地砖啊,它实际上来说,它每一个地方的材质都有所不一样,你不能用完全的单一的,比如说我这个是金的,这个是银色这种来表达,那么如何我们去表达这种呃。
类似于就是复杂的比较复杂的材质呢,可能大家也之前了解过,就是说如果你要描述这个材质的空间变化的话,就比如说我们一个一个比如说面前的一堵墙,强壮的每一个点就是因为风化腐蚀,可能它的材质都会变化。
这种材质的空间变化的话,就需要我们引入新的变量,x,这个x就是定义了这个材质在空间上那个分布,也就是说我们除了它,就像对于每一个x,我们都有一个他一个这样的一个b2 d f,所以因此的话。
我们可以定义这个special berry的这个b2 d f,这个special varing b d f,因为我们加入x这个分量,因为我们假设x是定义在这个物体的这个uv space。
比如说它这个表面的,对不对,我们不考虑3333维空间了,我们可以理解,把这个s0 ,b2 d f定义为像是一个六位的函数,但是我们也可以看到这个四,不论是四维还是六维。
他这个他这个相当于这个维度非常的高,你比方说啊特别维度高的时候,你就是说你要描述这样的函数的话,你你通常来说需要一个,比如说你要做测量的话,可能需要一个很大的一个表去储储存它。
这个你需要一个644d的一个数字,或者6d的个数组去存储它,因此它实际上就是这种表达,很少在这种实时渲染或真实感渲染中,他运用了,因为他的一个是很难够很难以被表达,第二个就是他你要你要得到精确的测量。
这种代价很大,因此的话一般来说在整个实时渲染中啊,我们他都不会这些,不论是这种什么4d的b,2d或者6d的这种s b b r d f,通常来说都不会被直接用的,所以说大家都会用这种解析模型去逼近它。
比如说大家非常那熟悉的,可能大家医学的这个图形学都知道,比如说缝缝模型啊,对不对,就是这个最最非常简单,high bo liberation模型,就是这种非常简单的。
通过直觉上来表达了这样的一个解析函数,解析函数它的优点是什么呢,原来的话b d不是一个四维的函数,因为它要精确的记录,他每一个入射光和初始光的方向,它的之间的关系,对不对,但是解析函数的话。
他就没有位数了,为什么呢,它只需要储存几个有限的参数,比如说这个服务模型,可能你只需要储存那个shining值,那个比如说阿尔法值一个参数,我就可以定义类似于一种材质。
所以说它的维度从一个四维降到了零维,也就是说几乎是不需要有任何的储存空间的,而我们这个在现在来说,现在这种渲染中的话,最经典的这种p p r工作流,比如说呃不呃,lv le meti工作流。
它也是由这种解析的模型来构成的,比如说像这样的一个啊b r d f f2 ,它通常都是由两个部分组成,因为因为现在为了这个他表达的更加的方便性,对它,它它相当于我们要提升他的这个表达的能力吗。
你通常来说不会把拆成两步,一个是慢反式的分量f d,一个是镜面反射分量fs对不对,然后呢通常来说的话,fs的话大家肯定也知道了,镜面反射分量通常来说可以用一个,比如说microphy model。
一般来说这就是一个非常经典的这种p p2 ,p b2 模型,对不对,然后它的这个d的话,它是一个法线分布函数,对不对,并描述了这个novel,它怎么样在这个micro fi下面进行变化的。
通常来说可能有一个g g x函数去表达它,以及它还有比如说f2 里面包含了dfg 3项,对不对,f是ffee项,大家肯定也知道,就是你在一个比较低的一个角度看的时候,它的反射都会增强。
这就是它的分裂点效应,还有即使交易try或者说shadow masking,像就是描述了这个可能在这微表面下,它这个是怎么样,是相当于是怎么样的一个遮挡关系,statistic统计学上的遮挡关系。
因此呢我们有这样的一个解析函数的话,我们就能够把这个维度极大的下降,也就是说这样的一个函数表达,其实上来说是零维的,为什么呢,我们定义这样的一个这样的一个b2 d f的话,我们只需要几个值就可以定。
第一个是lv 6值,对不对,还有一个是metallic值以及normal,是它这个法线方向,就是michael的这样的法线方向,不不是不是这种,不是这种微微表面的这种statistically法线方向。
以及他raghness值,以及比如说可能还有一些呃,它上面的一些其他的一些参数,比如说你刚才说的f0 ,其实上就是也是有一个参数,但通常来说你整个pp r工作流可能就是会表达。
albedo metallic法线,normal以及roughness 4个值,我们就可以定义一种b l d f,所以我们极大的就是把这个维度,从一个四维降到了一个零维,也就是说。
我们只需要通过有限的变量就能够储存,就能够表达这样的一个b2 d f,因此呢,它的一个好处就是我们去表达这个sd b2 df,就会非常简单,我们就可以把一个六维的s b r d f。
降低成一个二维的一个纹理,对不对,所以说我们看到的很多种纹理表达,其实本质上来说,你可以把它理解为,它也是一种s v b2 js的表达,他的话他会把这种lbl值,metallic值。
法线方向以及raghness,是去用一张2d图给储存起来,对不对,每个2d图就是一张2d的纹理,因此的话它就可以相当于是把一个6d的,中立地的s t b r e f,降降成为一个二维的一个纹理。
叫多通道,这个纹理,像一般来说的话,用一个用纹理表达的pp r材质,可能有各种各样的这种所谓的纹理图,对不对,我们刚才你也举的就是lv。
do normal raghness metallic是非常基本的。
我们一般来说general来说,你可能还有其他各种各样的这种材质图,比如说我或者纹理图,比如说高度图,high map,或者说ao图,或者说还有这种各项异性的anthropic map。
或者说还有它的透明度的图等等,所以说这些图呢他都可以用纹理表达,而这么多的多通道纹理,它这样的话它就可以表达一个pp r材质,所以说所以说这就是这个怎么说呢,这就是用这种纹理表达材质。
它是更加的compact,比起传统的那种s b b r d f的表达来说,所以说大量的这种纹理就是广泛的,在这种实时渲染中被使用,然后下面我们介绍一下这个纹理的生成部分,纹理的生成其实是多种多样的。
就是我们刚才也看到了,就是很多的这个纹理。
它是可以适应,是个2d图,对不对,那我们只而且你想看,就是从整个图形学发展到最先的时候,就是不考虑很多风格化的渲染,我们一开始的时候就是做的就是真实感的渲染,所以说我们要把一个纹理。
相当于我们需要需要我们的纹理和材质表达,对真实世界的物体,就是说相当于说能够更好地表达,真实世界的物体,因此呢一个最直接的获得这个纹理,材质图的方法是什么,就是拿你的相机,你的手机去拍照。
所以说在传统方法来讲,就是说我们可以把拍到的照片,然后怎么样去某种方法去转化为这样的一个,纹理图,对不对,一般来说的话可能一个非常简单的方法,就是ok我们拍一个平面的照片,比如说因为纹理嘛。
刚才我们看到很多,比如说那个砖墙,对不对,有地面或者说人的皮肤,你可以拍一个比较local的一个局部的照片呢,然后我们呢我们可以通过一些方法去处理,把它处理成这样的一个沟通的材质图。
比如说你看看很多多段采用处理,比如说l videl vido,实际上他就是把这个原来一张可能上,有光照信息的一个一个2d的图片,rgb图片转化为一张把光照信息去掉,只有它原本的颜色。
原来的颜色的这样一档v f图片,它本质上来说可能是把一个rgb的一个图片,转化为这个这样的一个多通道处理,你也可以理解为我用不同的滤镜,可以生成这样的图,比如说no wap嘛,对不对。
你可以说你拿一个类似于nomap滤镜,你可以把它这个颜色变成那种偏偏蓝色的,那就成一个normap,所以在最开始的时候就artist,可能他们就是oris,就是拿photoshop去去做这种事情。
但是这种事情他处理得到的结果,可能不是physically base,就是说就是它不是非常精确,不是说好像就能够完全的,就是按照物理的规律去,完全就是重建出他这样真实的问题。
他很可能就是实际上来说在很多的渲染场景中,真实的这种工业界渲染场景中,大多数的需求是physically plausible,就看起来它是合理的,不是要求说跟测量一样,它是完全要是物理上非常精确的。
因为你要精确的还原它的材质的话,你需要知道它的光照信息,你需要知道你的这个照片的这个什么,比如说你的位置信息等等,你才能从完全的重建出它的它的纹理,所以说这个是非常困难的。
所以一般来说的纹理生成是通过图片,然后再转换,但是呢这种过程呢其实也是非常讲究技巧,就是说并不是那么容易啊,甚至我尤其是比如说你还要把一个纹理,放在一个这个贴图,贴到这个几米水上。
贴到你的你的这个几何上等等,还要考虑各种各样的因素,还有啊除所以说呢各种各样的算法就衍生出来,帮助这个艺术家,他们生成各种各样的这样的材质纹理,比如说如果大家就对这个graph。
计算机统计学的研究有点了解的话,知道这是最早的算法,比如说是这个纹理合成,就是刚才我们说的,就是你拍你拍到一个平面图片的时候呢,你没有办法去拍个全貌,因为你可能只能拍一个局部。
因为比如说你只能拍到墙的一个小的patch,你想要去把这小的配置合成,会更大的东西就叫做纹理合成,或者说有没有更直接的材质转换工具,比如说我能直接把一个rgb图转化为,比如说多通道的这种纹理图。
因为本质上来说刚才也说过,你可能可以把理解为你是用不同的filter,去生成这样的一个纹理,纹理合成的话实际上是一个非常经典的算法,它这个可以,我觉得他的这个发明可以追溯到上个世纪,可能90年代了。
就是说他的问题,其实就是解决一个非常简单的问题,尤其是在那个当年,可能那个相机分辨率都非常低的情况下,就问就问,假设我就拍到的这个纹理特别小的话,我如何能够生成更大的图片呢,比如说我只能拍到一个这个。
一个有裂缝的墙的一个局部,我能如果能够把它生成一个,比如说更大的一张图片,这就是一个非常经典的一个纹理合成算法,但是我们要需要注意的一点就是什么呢,它跟这个他跟那个超分辨有什么区别呢。
就是其实大家可能知道这个超分辨,就是你也是把一张小图生成一张更大的图,对不对,但他这个跟超分辨有什么区别呢,知识区别就是什么呢,就是区别就是纹理的合成的话,它不改变它的一个尺度,也就是说什么意思呢。
我刚才说的,你想让把一个小的一个pad生成更大的一个图片,我们并不是说把这个patch缩放成更大的图片,而是我们希望它这个patch的这个尺度是不变,但是我们需要很多很多这样的patch。
就比如说呃什么意思呢,我们比如说我们拍了一个假设,一个比如说这个地砖对不对,我们拍摄这个地砖,我们不是想,我们假设把图,这个地砖,以相同的尺度贴到我们这个场景模型中的时候,我希望他有更多的地砖,对不对。
而不是说我只是把地砖给放大了而已,所以说我们希望他有更多的,比如我拍了一块砖,就我我想在这个地面上铺满十块这样的砖,我是希望就是重复这样的砖,但是比较比如说有一些的这个variation有点变化。
但是我想要更多的砖,而不是说只是把一块砖放大了,往我这个地面上贴,所以呢它是它是跟超分辨本质区别,就是纹理合成,不是改变它的,不会改变它的物理上,或者说它的这个实际上的一个尺度。
缺点的话就是纹理合成的一个,缺点的话其实也是非常明显,就是你将来是文理合成,它是这个整个这个发展,追溯到是就是上上个世纪的,对不对,就是说上次因为上次那个时候,总体来说他这个分辨率是比较低的,对不对。
然后那个时候我拟合成上哪,看的是我ok我有一张64的分辨率,我能不能分辨,是就是比如说合成成一张256的分辨率,这样可能能够重用,或者说能够这样的话,能够相当于说能够有更大的一些呃。
尺度的就是尺度不变的情况,我有更多的这样的pattern,pattern上有些vation,这样的话,我就可以til在我这个这个这个墙面上,或者说地面上了,但是问题就是说这种分辨率。
它是一个比较大的一个限制,而且就是呃呃而且就是什么,它这个实际上它纹理合成的算法,目前来看很多物理合成算法都不是特别的完美,比如说有明显有肉眼可见的瑕疵,还有就是它整体的分辨率就是偏低,为什么呢。
现在就比如说现在的整个游戏,或者说电影是这种设计中需要的纹理材质,通常是高分辨率,也就是说通常需要呃,至少我觉得至少是1k2 k的,就是至少他现在这种比如说如果玩游戏的话。
知道这种高清材质包可能都是4k8 k的,这种你仅仅仅仅的1k的分辨率,或者说很多大部分的这种文章,或者是以前那种工作,都是,假设你的实际工作的分辨率,都是在512左右的,通常来说是不太够用。
而且纹理合成算法大多数的这些算法,它的这个复杂复杂度,它跟它的这个分辨率是有直接相关的,也就是说你你在像素理论上来说,我可以把这个算法应用到比如4k以上,但是问题是你一旦这样做的话。
你的这个运行速度会变得非常非常的慢,因此的话就是想克服这样的一个问题,就是所以说现在很多的这个材质的设计,都是用了什么,用了这个过程式的这种纹理的生成过程,是纹理生成的。
就是大家可能知道一些比较简单的一些过程是,问题是比如说啊这个checkboard的七班格,或者说一些那个burning noise这种比较简单,但是现在复杂的,比如工业界中常用的。
就是用这种计算图的方法去生成纹理,像这种是no graph的方法去生成纹理,通常来说你可以在这个blender里面有这种什么,cd graph,这种其实都是相似的。
就是说通过一段程序去计算出更加复杂的一些,python出来,比如说我们一开始有很多的noise啊,然后把这些noise通过一些东西去blend混合,然后再去调它的颜色啊,然后再去调它的这种什么呃。
比如说他这种pattern的分布啊等等,就是我可以组成组成一张,就是实际上来说,看起来跟真实图片非常像的一张,但是它是过程式的纹理,这个过程是纹理,它的这个好处是什么,第一个是它有非常强的可编辑性。
你看它是一张计算图,你想它每个节点都是可编辑的,你想要比如说把把面前的这个这个地砖,你说我要改变它的skill,对不对,我会说我想把这个把这个白色的,把这个脏的部分变得更脏。
它实际上有几个参数可以直接调,或者说你想把前这个这个地砖变得很干净,它也可以直接调,所以它它的边界性是非常强,它也完全可编辑性,除此之外就是说它是一个计算出来的一张图,它理论上来之后。
你的分辨率它只是一个参数,而a已知参数设为8k,它就可以计算出8k的,所以他就是他这样的话,它的分辨率理论上是无穷,而且有无无限尺度的细节,其次的话就是一般是如果是一个高度优化的。
这样的一个这个这个note graph system的话,它可以达到交互式设计,也就是说我们可以直接把这个图直接计算图,比如说在这种3d max或者maya,插件里面直接应用。
然后呢我们可以实时的去看它,就对它进行编辑,然后获得一些feedback,比如说我这个这个场景,在这个场景中实时的做一个real time rendering,就结果怎么样,我觉得不满意的话。
我可以立马改参数,然后他就可以重新给我渲染,因此的话他这种他这种过程式的纹理呃,它的使用上实际上是比一些pixel的纹理,更更好用一些,但是它最大的缺点是什么呢,就是它的不好设计。
我们刚才说的很多的图片,你说我们可以很简单的拿这个照片啊,去呃去拍照,然后去转换,或者或者说你直接去网上搜,就可以搜到很多这样的素材,但是你要设计这样的一个计算图,你如果没有任何的专业背景的话。
或者说你没有任何经验的话,是很难设计出这样的一个一个过程,是纹理图的,然后过程是纹理图,比如说像这种substance的话,adobe substance它有它是一系列的套件,可以支持这样的一个过程。
是模拟生成,包括它可以用pa substance painter,也就是说他可以用这种笔刷,去在这个jameter上刷这些纹理,就是你的笔刷实际上就是一个procedure。
就是这样的一个过程的一个纹理计算图,这样的话你可以程序式的,相当于相当于在这个在这个在这个几何模型上,对它进行化,所以说它整个设计流程也是非常的,就就是相对非常的简易的,但是我刚才就说了句话。
就是如果这些这些这些过程中,问他最大的缺点就是它设计非常难,所以其实大量的这些研究还在说,我们能不能还是用这种图片的方法表,但是我们能够简化这个操作,对不对,我们从真实世界中拍照或者什么上网或者材质。
然后呢我们可以快速的,比如说生成这种高清的这种材质,是不是,所以很多职业研究开始研究这方面内容,就比如说呃比如说最近的,比如说深度学习的兴起,大家都想ok我能不能原来我们的拍照,比如说你想要用。
比如说好好的相机,然后比如说在一个光源比较好的情况下去拍,那我们能不能,而且你得到的结果,我刚才说你还需要用这种什么photoshop去处理,那我们现在能不能说能不能用,直接用手机去对应到。
比如说东西进行拍摄,然后你拍摄得到结果呢,你会然后你再用一些这种深度学习方法去去,相当于去学它到底是他physically,他物理上是如何去正确的表达的,因此呢他他如果你在渲染系统重新render的话。
会发现它其实跟真正真实拍到的照片,其实非常像,包括还有刚才说这个问题,就是你如果只拍到一张平面的图片的话,你是你,你还需要后期的时候把它给贴在你的几何上,对不对,你你这种什么,比如你这种什么,比如专。
比如这种什么墙面或者这种平面的这种纹理,还是比较好拍的,比如说你可以你可以比较简单的拍,不用不用做uv unwrapping,但是你想把一个复杂的东西直接贴到其复杂角,美学上还是很复杂的。
所以现在还有很多的工作,就是直接相当于是做这种几何和纹理的重建,包括一些呃有很多方法,比如说他利用这微分渲染,或者现在大量的在用nerf做这种东西,除此之外呢,还有一些工作。
就是我们就想的是能不能有一些更好的方法,比如说example base方法对纹理进行编辑,因为刚才所说的过程是纹理的优势,就是说你可以通过调参数对纹理进行编辑,而你如何去对一个。
比如说我有一张比如模拟图,我怎么样进行编辑呢,也是一个好问题,就是它是如果是只是一个贴图的话,现在的方法可能还是就通过photoshop去做,或者说不过不过从去年或者前2年开始。
现在defusion model了,我们就可以用更加powerful的方法了,但是我们还是希望能够用其他的方法,比如说用一些example base或者说tt base的方法。
而不是通过像是用photoshop一样对纹理的,相当于每个像素进行这种逐像素的编辑,这种像这种效率是比较低下的,所以说我刚才其实也就提到了一些,比如说这种什么diffusion model a i。
其实现在来说,从可能从前2年开始,去年和前年开始这个搭理这种出现,包括stevideusion的出现的话,会导致整个纹理的生成,workflow也会呃产生很大的变化,比如说之前有一个。
比如说这个这个是一个,之前是有一个这个material artist在网上泡着他们的,他觉得他的一个很很不错的工作,也就是原来的情况下,你看它它最左边那个图是他,我我用手机拍张照片,对不对。
然后呢他这一点呢就是我做几个prop,对我crop几个几个几个小小pad之后呢,我把它贴贴在用photoshop贴贴在一起之后,贴贴贴成一个更大一点的这个纹理,对不对,更完整的纹理。
但是有个问题是什么呢,我们需要这个纹理,它是一个title,也就是说他你会你会看出它til完之后呢,他明显可以看见纹理之间存在的接缝,这实际上也是纹理和纹理合成立很大的问题,就是说你没有办法。
有时候没有办法保证你合成的纹理,或者说你得到的拍照得到的纹理,它是一个无缝的,也就是说你可以比如说啊,因为我可以无缝的那一块一块的像砖一样,贴满整个墙面,看得出你的缝,像你比如马赛克这种砖块一样。
你实际上都是有缝的,那怎么办呢,所以说你可以用搭理这种,比如说用打理这种得到一个这种smith smoothness的,就simply output,一般的话你需要消除这些phone。
你可能需要我提二次去做那种pixel based iting,是非常麻烦的,你这样的话,用搭理这种generative ai,去做这种无缝的处理之后呢,我们再把这些东西对不对。
放到比如说substance sampler,这种这种什么材质图转化工具,让它生成这种pp r材质图,也就是说在整个ai时代的话,他这种纹理的这种工作流,实际上来说也会得到非常大的改变。
而且刚才说的这个是,你还是要从真实世界中获得纹理,这个人抄手机拍照,现在包括现在大量的这种text base的,generating model的话。
你完全可以通过比如说这种text from me的进行这种,获得大量这种variation,然后从中挑选自己喜欢的纹理,对不对,diffusion model,包括很多。
还有这种现在的control control net,这种这种都可以去获得各种各样的这种,2d平面的纹理,而且现在还有很多工作在做这种什么,43d上的,直接是获得的文理rett这种工作。
然后我们最后可以把这些diffusion model,得到的纹理,再通过各种各样的材质图生成工具,对应得到对应的pp r材质图,也就是说这种传统的这种。
也就是说我们刚开始说这种pixel base的方法,因为现在ai的工具出现,变得实际上来说有很多的这种可能性,整个wflow也会得到其他的改变,我们再回到我们最后再回到这个过程,是纹理上去。
就是大家有没有想过,就是过程问题,刚才说它本质其实是一段程序,对不对,它是一个node graph,你的node实际上就是一个小函数,对不对,你把你这些小函数的输入和输入连在一起,它实际上就是一段程序。
对不对,但你也知道现在的现在的transformer,可能大家了解ai的话,就transformer这么强,现在包括微软的那个可怕可怕了,这种program synthesis。
实际上也是也是一个非常popular的话题,你其实可拍了,它也是一个非常强的一个a r k,能够帮助人进行编程,那么想看我们能不能去直接生成程序式的纹理,实际上是可能的,就是现在已经只是从去年开始。
因为去年是很多这种generative model的一个爆发期,很多的这种也有我现在工作去研究,比如说gb t架构。
这种transformer架构,我们去直接去生成一段程序,用这段程序相当于是去表达一个计算图,去表达一个程序式纹理,这样的话就相当于是我们不仅仅可以在pixel base上。
做出这种generative ai,我们甚至也可以在这种procedure这个space上,或者在这种program space上做这奖励率瑞文案,所以说我觉得整个纹理生成在这个呃。
这个ai时代也是这个可能性是非常大的,然后我们来到了这个最后的toy,就是啊我们是我们生成了这个纹理,对不对,我们最终还是要把这个纹理用到我们的这个。
实时的渲染管线,或者说离线中渲染中,对不对,然后呢就是问题就是,我们如何去正确使用这个纹理呢,看似纹理的使用其实非常简单,因为大多数纹理的因为纹理怎么说呢,它是一个非常非常basic的一个单元,对不对。
呃,在整个渲染管线中,实际上来说大量的这个纹理的使用,通常来说都是已经被will pad到一个比如a api里面,说,或者说实际上是硬件集成的,或者api集成的功能了,但大家也没有想过啊。
就如何正确的使用纹理,但是的话呃,实际上它跟正常速度的使用是不太一样的,他虽然在表达我们刚才说了,他的他的就是理论上的表达,就是一个速度,对不对,但是我们的我们从这个怎么说,从更理论上来的角度来说。
纹理是表达什么文理,文理文理是表达什么纹理,我刚才说的就是,我们使用纹理去逼近一个自然界中的一个,一个材质,对不对,自然界这种材质的话,它是一个连续的一个东西,对不对,我们认为我们拍照得到这个纹理。
实际上是对这个离散自然界中,连续这种信号的一个采样,对不对,它是储存的,是一个经过什么一个离散化的一个连续的信号,我们对文理的访问的话,实际上是对于这样的一个,离散化信号的一个重建,对不对。
我们是想要去重建出它原来这个连续的信号,对不对,因此的话我们访问纹理的话,我们需要访问是0~1之间的一个小数,通常需要用小数做到sm,因此的话我们怎么样去采用一个,比如说是呃,0。
1的这样的一个一个一个一个一个position,对不对,所以而且一个很重要的问题,就是一个采样频率的问题,就是如果大家学过,比如信号理论,比如说信号理论的话,也知道,就是如果你的采样频率不高不低的话。
你会产生这种走样的一个问题,也就是说比如说刚才这个例子,就是远方的这种纹理上的这种文素text,分布的是非常密集的,就表示了什么,理论上来说他那边的一个信号频率是非常高的。
因为你可以理解为他他那个就相当于他的变化,变化率非常的高,对不对,因为他离得很远,他在这个screen space上的变化率非常高,如果我们只是简单的用点采样的话,它会产生这种走样的信号。
点采样就是啊我们就就踩,就踩那个最最离家最近的样样本,如果你这样简单采样的话,你会导致它这个产生走量的信号,因此我们通常要进行滤波,滤波的话,什么滤波少,因为我们是这种fixed。
就是我们已经是有一个我们的分辨率是固定的,情况下的话,你必须通过滤波的方法去预调一下,它的高频信号,让它看起来更加更加平滑,也就是说你的像面采样的时候就是用滤波方法,你的你的你的远处的时候。
你看起来就是虽然他这吕布对的也不是特别好,但是你可以看见他远处的时候,不会产生那么奇怪的这种pattern,他这种python最直接表示,就比如你这个黑色的线都不直了,对不对,因为他就看成一个锯齿形。
你有的时候会翻译成比如锯齿,对不对,我们所以要我们,所以呢我们所以要需要一种方法去抗对抗,这个剧情,就是抗锯齿或者说反走,要一般来说的话,对不对,我们可以通过增大采样率。
但是实际上除了这个性能会下降之外,有可能你也没法增大采样,因为你是你的这个纹理的分辨率是固定的,所以你一般来说我能考虑到性能,你也不能说实时的去做这个滤波,对不对,那我们是怎么样解决呢。
所以因此的话就是实际上来说,一个在同学里面就是特别实时渲染中呃,呃它的一个一个一个hilosophy或者一个哲学,就是说什么很多东西呢,如果你能够预处理,我们就预处理了,实施的时候我们就把它固定住。
直接去访问就行了,为了它的速度考虑,对不对,一般来说的话,纹理这种东西它通常是跟几何绑定起来,对不对,而且通常来说纹理在运行时不会变,所以它储存的值不太会变,因此我们对每一块纹理我都可以。
我们刚才说的是一种方法,是实时的要去做这个滤波,我们是不是能够提前把这个包给,相当于把这个提前把这个做这个profiting,对不对,所谓的procreator其实非常简单。
就是我们要产生这个所谓的mmap,mamap本质就是做prefering,相当于我用一个filter去对这个滤波之后,他你像因为滤波的话,他会把这个图片做的不一下就不做了,模糊它的分辨率因此会降低。
因此我们会把一张图片之后一张图片做成不同,就是从mamap 0是原图,一直到它最低的,这个mmap可能是1x1的这种单位做,做到一种,其实都是,我们先把提前把这种滤波滤波好之后呢。
我们就可以在new new map上再进行采用,比如说什么意思呢,就比如说像右边这张图,你可以看出,如果是没有明白的话,我们就是最简单粗暴的点采样的话,你会发现这种大量的远方,可以看到大量的这种巨石。
就是因为我们的信号采样频率太低了,我们只是踩那个最近邻,就是那个年年呃啊那个这个最近0度,不是那个就是假设你没有me map的话,然后你又做最近邻采样的话,你会发现他这个全都是锯齿,对不对。
你看起来就是因为你的重建信号屏,你的你的重建的信号是不是有问题的,就是你的重建的信号是错误的,所以你你你你的这个你的这个对面全都是锯齿,而你做trilinear me map之后。
你的你因为你做了一波之后,相当于是我采样的时候,是在me更高等级mini map上做采样的之后,也就是说我实际上是我采样的那个点,它进行了预波,所以它看起来的话。
他就是相当于是把那些高频信号给丢掉了之后,它看起来就是更加的一些,就相当于是更加的smooth一些,你至少可以,虽然这个trainer没卖,并不是最好,因为它比起我们这个一左边所说的,各项异性滤波。
后面会讲到这种最高战力波,它还是有出现这种模糊的现象,但是它看起来比如说它已经不会存在,上面那个图中,就是远处会产生大量聚酯溶现象,而且而且它反而是其实是一个非常光滑的,一个一个过渡。
mini map的话,我之前也提到过,就是整个这个整个这个纹理单元,实际上是一个很well pad单元,就是me map,可能大家也不需要去手动去生成,它通常用api就可以生成。
只是需要占用一些额外的空间,这其实是一个比较简单的一个计算题,也就是说,比如说你你因为我每个mmap,相当于是是原来的一半,那也就是横轴纵轴都是它一半大小,所以它这个第n级的me max d n。
减一级的mimp 4分之一,对不对,当然还有一个问题,就是说你如果要求api生成的话,你需要这个纹理的维度是二的n次方,因为你每次相当于就是你的这个你这个横轴,纵轴减一半嘛,所以说他这个mimap呢。
通常需要额外的这样的一个,1/3的一个储存空间,如果就这样,你这是一个其实很简单的一个计算题了,但是考虑到我其实咱是考虑到,我们这个得到了这个性能提升,就是说你得到质量提升。
就是说这个y3 分之一储存空间,其实还是更加值得,还有一个问题就是什么呢,就是啊我们如何才能知道啊采用哪一张mfi,就是我们刚才说me map采样的,它这个规则,就是假设我们觉得我们采样的那个点。
它那个信号频率很高的话,我们就要采更高等级的命脉,因为那更高等级命脉表示,他把高频信号给滤掉了之后,它是一个它是一个,它是经过了一个profiting的一个一个一个图片。
所以我们要采一个prefer引用的东西,那么我们如何决定我们应该采哪张b map,所以说就是通常来说的话,你可以理解为什么,就是我们要一旦我们觉得他的这个频频率越高。
我们就要踩的这个amy map成积越大。
那么我们应该怎么决定它的,实际上我们刚才看到很多图,就是一个一个一个general的id,就是说你看他离我们,比如说他那个他越密集,对不对,他他比如他的屏幕,他这个text在这个屏幕空间分布越密集。
代表它的频率越高,对不对,通常来说它越密集的,为什么会越密集呢,就是它它在屏幕空间上,它的uv的变化是更加剧烈的,对不对,因此呢我们需要得到它这个屏幕空间上,uv变化的一个剧烈剧烈程度。
如果他在屏幕空间上它的剧烈越剧烈的话,我们就需要去,我们就需要去采用更大层级的mmap,然而一个问题是,我们怎样得到它,这个我们怎么样去描述它,这个uv在屏幕空间上的变化,这个程度呢,就通常来说的话。
gpu管线里面在执行这个pixel shading的时候,它是以2x2降零破的进行并行计算的,这样的并行计算的话,表表示它可以很方便计算,一个叫dd和d d y的东西,就是screen space。
对某一个这个变量的一个一个一个梯度,比如说以x方向直接的话,它就可以硬件就可以帮助计算它的uv,uv纹理坐标,它的这个在x方向这个梯度,比如说du dx dvd d v d x,对不对。
我们当然还有这个d u d y,和这个d v d y等等,从而可以估算这个对应的这个等级,而估算这个方式呢,通常也是同一个经验式的一个估算方式,通常来说,比如说可能是把这两个这个梯度。
相当于是得得到他这个磨磨的log,对不对,我们可以通过比如px,它就是相当于是你可以大概理解为,实际实际中不是不是可能不是,那么不是这么写,因为每他每个a p i写的都不一样,它但是你可以理解。
比如px可能就是计算出来,就是你要采哪个mim等级,对不对,这种其实更最后只直观的这个理解,就是说呢你的屏幕空间这个模越大,对不对,你这个表示它的梯度越大,表示什么。
它这个就uv坐标在屏幕上变化的更剧烈,也就是说我就变化了,就变化了一个pixel,我是我发现我的uv都变都变得很大了,对不对,或者说变得很小了,这就说明他这个频率就是非常的高,因此呢我们就要采采样。
相当于是更大层级或者说更深的这样的命脉,因为更深的秘密,它它就这种filter,就就就相当于是做这种filter的范围越大,因此的话相当于就是说我们能够比较怎么说呢。
比较reasonable去估算这样的一个mamap等级,去从而取得一个比较reasonable的一个采样,就generally说,这可能还是一个比较所谓的physically,plausible的。
而不是一个非常精确的结果,但是大量的实验表明,这这这些结果其实是这都是很reas,都是就是从视觉上来讲是很reasonable,啊还有但是还有一个问题就是什么呢,就是说me map的话。
刚哎就是刚才就是我们看到。
就是它都是正方形的,它要求你me map是二的n次方,对不对,正方形的mmm,他这个一层层计算完之后,它是很多很多正方形mm,但是有个问题就是说什么呢,就是说呃如果像像这个右边这张图的话,可以看见什么。
就说他在这个实际上来说,很多情况下,他这个就纹纹理相当于是uv坐标那个分布的话,它并不是完全真正好好是一个正方形的,对不对,你可以理解为有一个有一堵墙,你开一堵墙的时候,对不对。
你你这种墙体你越远的时候对它频率,他这个他这个uv的分布越密,对不对,你这个时候他你用me map做的是做的非常好,因为它它是它在screen space上,就是一个完整的一个正方形。
但是比如说像这个右图里边,它这个平面就你是斜的,对不对,所以你看这个平面的那个那小块,比如说你像采样那个像素点的时候呢,你理论上来说,你想要sample的是是它一个椭圆形的区域。
而不是说是一个完完全全的一个圆形的区域,对不对,也就是说这实际上来说这就是一个呃,这就是一个很大的问题,就是说你这个mini map uv space,你在做这个preputer。
但是在你这个实际的screen space上,其实是有存在一个比较大的差别,这就导致了什么呢,导致了我们需要一种更加更加好的,一种采样方法,也就是说我们通常来说,现在大多数,比如游戏里面。
或者说实时渲染里面都用的是antopic future,也就是说各项异性的采样,各项异性采样和各项同性采样,就是很明显的区别,就是说你你在看远处的地方,也就是说你看这个特别是斜的地面,对不对,刚才说的。
如果这个墙它没有什么问题,因为你正着看这个墙,你斜的这个地面的话,你会发现你实际上你需要prefer你去,他是个椭圆形的,而是在你的mimap做的时候,它是做一个是圆形的,或者它是正方形的东西,对不对。
所以你看到之后,你如果直接用传统的mini map做那种isle topic,比如说你用training也可以,它的远处都是糊的,就是它它实际上它重建效果是不是特别好的,对不对。
而你用anneal chic field,也就是你考虑到这种各向异性的差异,很简单,就是说我们很很简单的一个思路,就是说你看这个椭圆对,所以我这个我这个sample的这个分布,它是要遵循一个椭圆的规则。
而不是说我就是在一个原型上,这个mmap上做出直接sample,对不对,它这个区别呢就是刚才就是所说的,就是你的view space跟你的texas space差别很大。
你texas space假设你是正着看他的,而你的views space实际上是一个,你可能是在一个很奇怪的一个方法,比如说你是在接近似于近似于90度倾斜,非常倾斜的方向看它的。
所以这就会导致非常明显的区别,因此的话这种各项异性的采样啊,就是变得非常的popular,而各项异性采样往往需要怎么说,就需要进行一些实时的一些sample,就比如说我们需要一些实时的去sample啊。
不同的一样的一个对他sample,这个你你你需要carry,这uv坐标周围的不同的这种采样点,不同数量采样点,通常来说你的样本数量越多,你的你的相当于是你的重建,重建的这个信号就越精准。
从通常来说你这个得到的结果就越清晰,他当然这样的话也会需要你得到,需要更需要有更高的这样的一个计算代价,就比如说下面这张图,从这个没有没有这种呃,就是说最近零开始我们有两个两个采样点,四个采样点。
八个采样点等等,你会看到它这个从业质量越来越好,我们刚才讲的就是纹理的这样的一个一个采样,对不对,我们刚才也说了,你要纹理generation b map的话,你需要需要额外的1/3的容量。
而我们之前讲到文理生产的时候,我们又提到了这个现代的这个呃,这个呃这个游戏啊,或者说电影里面渲染的话,我们需要的是高清材质,这种高清材质的话,它可能怎么说呢,就是你1k2 k是不够,你要4k你4k的话。
我不知道你有没有想过一个4k的一个rgb,如果解压之后是多大的一个一个相当于,因为大多数你储存的时候都是g pg或者png形式,处理,都是压缩过的,如果你完全以数组的方式储存,一张4k的纹理的话。
是多大的,是非常大的,你想想看,你如果是一个三通道rgb大小的话,你需要48兆,而你的main map的话需要64兆,而更加离谱的是什么,离谱的是假设你是hdr贴图的话,对不对,你是flow的形式的话。
你就需要更大,你的命脉的话肯定要占用1/4个g大小,所以你想想看你,我把这么大的一个啊,这么大4k的东西往这个这个gpu memory里面搬,也不是每个人的gpu memory都是那种33080。
3090这种基本mary在1212g,24g的那种,所以就算你10+24,你三秒都多少是这个哦,texture对,而且这还是仅仅是一个rgb 3种的了,我们要储存的可能是一个刚才说的是。
我们tal map也是一种multi channel,你要你要albedo,你要normal,要raoughness,对不对,你要一堆的这样的一个贴图,所以说你不压缩,直接把它展展开。
放在这个gpu里面肯定是吃不消的,所以我们需要尽我们可能去压缩这个纹理大小,而我们刚才说的就是,你通常来说我们这个纹理既然是图片,对不对,不论是lv的或者是normal,它都是图片,我们放到硬盘上。
通常都是这个k g p g r p g来储存,那我们放在这个gpu显存的时候,能还是用这些一般的压缩图像,要是算法的,比如说像这个像右边这种,用gp夹子看起来质量可能也ok,对不对。
虽然你其实仔细看会发现他很多地方都不乐,我们回答这个问题之前,我们首先要理解理解文理的一个重要特性,就比如说我们想设计一个纹理压缩算法的话,这个或者说我们要想设计一个文件啊,就我们要设计文件的时候。
我们要考虑到哪些点,对不对,文理的一个非常重要的特点,就是它需要什么,他需要在gpu中,gpu中用,对不对,它需要随机的一个单点的范围,也就是说啊,也就是说什么我们需要对,我们需要有这样的一个。
非常快速的单点查询算法,这个单点查询算法是能够高度并行化执行的,因为这个这个pixel shader它是并行执行的,也就是说我们要我们要输入,ok我们要作为一个query,对不对。
这个query我们输入这个uv坐标,然后我们要返回这个纹理值,对不对,因此假设你想看我们要有一个压缩算法的话,能够同达到,相当于就是说能够满足这样的一个,随机单点访问的要求,它需要有哪些特性呢。
比如说它的解码速度要非常的快,这个纹理夹纹理解压需要,相当于是你以为你文理的这个query,可能是要query很多次,尤其是你要做一些这种filter的时,well time filter的时候。
你可能要去做query很多次,你不可能说我解码解码特别慢,也就是说你的在解码的速度,应该是非常非常快的,因此对这个他解码的速度要求是非常之高的,而且它是什么,它要求就是随机访问,也就是说什么叫随机访问。
就是说我的解码希望我什么,希望能够就是对这个uv的这个周围,或者是这个local,他有这个local型,就是说他对这个uv坐标,这个这个局部范围内可能进行一些这个,因为毕竟你要你要开压缩算法。
你肯定要推进lol范围内进行一些,其他的一些处理查询,但是你不会说我我访问,比如说一个优惠,比如访问三三这个坐标,我要对非常远的一个这个像素点,比如说100 100,我要去对它进行一些操作计算。
你这样的话你会占用非常大的额外资源,对不对,因为我随机的,我我只希望我每次减压的这个过程中,只需要消耗一点点计算资源,并且我消耗一点点额外的,比如说储存需求是不是,其次的话我们还需要什么。
还需要这个可变压缩比,因为我们刚才说的就是说,我们不可能是认为每一个人的这个,它的这个他这个gpu的型号都是一模一样,而且gpu的gpu在移动端,桌面端都有不同的表现能力,对不对,通常来说的话。
我们假设这个桌桌面积的gpu会有更大的显存,至少是比如8g的,或者说24g的等等,因此我们比如说我们我们需要用质量,而不是说我们一家压缩的更大,移动端,我们假设他这个通常来说,它的gpu没有多大的显存。
对不对,而且显存可能比较慢,因此我们需要提供更大的一些压缩比,所以呢我刚才讲的这种传统的这种g g p g,或者2t p n g,这种有可能比如小波变换,那些类似的这种压缩算法,它是很难。
或者说不太方便去应用到这些纹理中的,因此我们对纹理的压缩需要单独的一些算法,纹理压缩一些算法一般来说是基于block算法,基于block算法,它一个非常大的优势。
就是刚才讲的是local的访问的local型,就是说你不会要求说我解压一个像素点,结果我还要去访问,离我这个像素点离得非常远的,一个一个一个一个小数,或者说一个一个对对,相当于对离我非常远的一个地方。
进行一些操作计算,而通常来说纹理压缩算法有一些吧,比如以前有很多种,比如说这种x3 text compression等等,反正就是你可以在网上查到很多这样的一个,document的去描述这些问题。
压缩算法到底是怎么计算的,通常来说的话,你可以理解,为什么它都是将一个一组像素块去压缩,连一个比如固定长度的一个向量,也就是说首先我们要把一个图片进行分组,然后相当于是把每一组像素块。
都压缩为一个固定长度,然后当时解压的时候呢,就把这个固定长度向上还原,成绩有箱子快,比如说这个a s t c是一个非常呃,其实非常常用的一个压缩算法,就是像不同数量的一些像素亚。
都压缩为这个128比特的一个向量,从而它可以达到不同的一个程度的压缩比,对不对,通常来说它的比特率越高,它的这个读到图片就更加清晰,我们比如说,因为我们比如说可以举一个最简单的例子,比如说一个纹理。
我们可以把它分别为多个数据块,每个数据块你可以理解为就是一个,对不对,比如说这个a b c d,e f g组成的一个这样的一个16 4x4的,16个,一个16个,比如说16个这样一个slot一个数据块。
对不对,我们就可以把这样的一个4x4的一个数据块,原来比如说未压缩的状态,假设我们假设我们一个他一个slot,里面是一个bug,对不对,比如说是一个bug,一个bug可以表示很多,比如说一个儿童道。
对不对,他原来是16个bt,我们比如说我把压缩成什么,压缩成一个八bt,这样的会达到了这个二比一的压缩,二比一的压缩率,我们可以再举一个简单的例子,看看这个东西到底是如何做做这个压缩。
比如说一个非常简单的例子,就是一个b c1 ,刚才也说的那个就是一个d d x 10里面嘛,点d x 10里面的一个标准,这就是我们要压缩一个4x4的一个,怎么样去压缩一个4x4的。
一个r g b的一个数据块,对不对,我们可以算一下4x4的一个rgb的数据块,rgb嘛,对不对,rgb每个是是是888对888,所以是三个bt,所以它原来一共是48个by的,对不对。
48个原来是48的一个bt,相当于是一个对一个16 16x2,是一个16的一个rgb的数据块,我们先把压缩成什么,压缩成像右边这样的格式,右边这样的格式的话,它就是有两个这样的一个颜色值。
它它两个两个颜色值,它它每个颜色值实际上都是经过创k的部分,就是被压缩过的,相当于它的rgb,它相当于是他只用了一个什么呢,只用了相当于只用了16位,去表达原来的一个24位的,一个一个一个rgb数据库。
所以说他现在是每个color是只占用两个bt,对不对,除此之外呢,它还有什么,它还有颜色索引,颜色索引的话就是说你刚刚看的d c b a这种,他这种是16个颜色缩影,每个颜色缩影呢相当于每一个颜色。
所以它占两个bs,对不对,所以他一共这么多,加起来一共只占了八个bt,比刚才我们比较的48个bt相比的话,它达到了一个6米,这样压缩比他怎么样去编解码呢,实际上也是非常的巧妙的一个变解法。
也就是说它不是说有两个颜色值吗,对不对,然后他拍实物颜色每个颜色,所以是两个bt,所以这两个bt呢可以什么表示四种编码,四种编法就是两个by 00啊,对不对,就是相当于是你看到这个右边的这个。
这个这个文字是对应的四个,所以就是00011011,对不对,表达了四种颜色,这四种颜色呢就是由color 0和color 1,两个这种track过这种颜色值所进行排列组合,将来进行blend组合得到的。
也就是说它相当于是整个数据块呢,只只有存在四种颜色,对不对,这四种颜色呢就是刚才就是这四种颜色呢,它分别用这种000101表示,而四种颜色呢怎么表达呢,它比如说零零的话就对应的color。
001的话对应的color 1,比如说一零的话,它就相当于是把color 10和color一以二比一的方法,是blender到一起,对不对,一的话。
他就是把color 0和color一以一比二的形式不到一起,所以说他用这种方式就能够得到一个呃,相对来说比较高的压缩比,而且你像他这种东西,它在解码的过程中,他只需要访问这样一个数据块。
而且他做这个解码操作的时候是非常简单,相当于我只需要去访问这个数据库里面的,我只需要fetch这个color 0和color 1,再加上我fish这什么,我fish这个相当于是我这个我这个格子里面。
表示到底是哪个index,我是零零还是零一还是一零就ok了,我不需要去对很远的地方进行分,而且我占用的这种额外的计算资源,或者储存资源是非常低的,而而而这种纹理压缩呢,相当于除了这个按键的那个bc 1。
对不对,b c这个b c这个价格还有很多种,好像从一到b c,一到b c7 吧,他们相当于为什么他们会对不同的数据类型,有不同的编码方式,比如说刚才说的是rgb的编码,对不对,我们可以说我们还可以。
比如说像这个右图这边,你看他是bc到b c5 ,它是它可以编码为,比如说r g b,或者说它可以有这个阿尔法值,或者说它还有这one compucolor,比如你只有一个r一个red通道,一个红通道。
或者说你还有这个两个通道,比如说八八通道的这种two component color等等,所以说你每一种不同的数据类型,它可能还有这种编码方式,它也可能会会会不断的改变,我们刚才也说了。
就是除了这种b c对不对,还有b c这个家族,还有就是什么,还有a s t c是非常广泛使用的,还有这种什么etc etc,二中各种各样的编码,那么问题就是说什么,假设你有一张图片之后,对不对。
你你你你你这个你你想把这个程序deploy到这,移动端或deploy到你的桌面端上去之后,你你还要额外的相当于压成不同的编码类型,因为比如说你右边这张图显示。
就是说你可能bc的话比较适合dance top,比如说你这个s t c可能比较适合模板,那我是不是说每个东西还要再,相当于说处理两分,你比如说我我我处理两分,我我有我有必要吗,对不对,所以说。
所以说我们希望就是有一种这个纹理,数据交换系统的,比如说这种叫做basis universal的一个系统,它可以生成一种这样的一个中间的压缩数据,并且这种压缩数据能快速的转,编码为其中的其他压缩类型。
这样的话就是导致就是我们可以复用,我们的压缩数据,我们不需要去保留多个压缩副本,而且我们比如说我们在deploy,我们的这种嗯程序的时候呢,我们只要去啊储存这个dog basis,这种储存方式可以。
然后它在实时的在相当于在在运行的时候,它就可以被快速的变解码为其他的这种,这种数据类型,这样的话我们可以说有效的节省,我们这个这个deploy这个存储量,最后的话就是还要提到。
就是你既然都讲到纹理压缩的对比,我们之前讲了一个方法,就是啊这个analytic model。
你还记得大家还记得就是analytical,这个b r d f就是解析了b r d f,相比原来的这种4d的b r d f,你一下子从四维压到了零尾,那我们同样的对不对,你你假设一个2d的纹理。
我能不能直接从2d压到零为呢,是不是其实是可能的,那就是所谓的这种过程是纹理,对不对,因为过程是纹理,你只要储存储存什么,他那几个参数就ok了,或者你只要储存一段程序,就理论上来说,你这个情商来说。
你是fix less的,也就是你实际上是一个零维的东西,就是你人上来说,你最最极限的压缩是被什么,把一个纹理再给你压成零尾的东西,那么那么大家肯定想,那为什么形象中的很多。
其实呃看到这种游戏中实时渲染中,很没有多少人用这种过程是纹理的,就或者说大多数过程中为你用的时候,你都是先转化为材质图,然后再去用,我就说你先bake出bake成材质图,我们在用的,那没有什么。
没有人直接在说uv坐标上直接evaluate,这个过程是为适时的去做这种事呢,大家可以想一下,它其实最本质的就是刚才说的那个纹理的使用,对不对,刚才有可能记得文理的使用。
纹理的使用是你要考虑到你这个纹理,你你的频率的问题,对不对,你如果说你这个纹理的频率高的话,我需要做什么,我需要进行me map,我需要进行批prefer,对不对,这样的话我可以把高频项目滤掉。
看起来更正常一些,你不城市为例的话,怎么做这些做怎么做这种所谓的prefer ing的,对不对,你你每个点都是实时的要进行计算的,对不对,你要实施这计算的话。
那ok那我做实时的实时的filtering的话,你这样的话你的计算单价就会非常的高,尤其是你的这个过程中,文你还变得特别特别复杂的时候,你就会变得很有问题,而且还有一个点。
就是你纹理的压缩的一个一个关键点就是什么,你要做point query,就是让你点查询,对不对,点查询就是说你在uv,我给你uv,你立马给我给我这个结果,但是通常来说。
比如说刚才看到那个软件adobe substance,他没有办法做点查询,因为他他的很多的那种纹理,就是他过程是文理中间的那些啊,操作都是future这个你的filter。
你怎么你filter可以可以做点查权,但是很多情况下你是没有办法说等等,你修好也没法做检查,学他你你你future没法子填上去,你你你你这种东西就是你没有办法做检查,行程之后。
你导致你想要去value的一个pixel的话,你需要把整个图都value出来,我才能知道,这就失去了你这个你这个纹理,这个压缩的这个优势了,你相当于我还是需要把它给expend,到一个完整的图。
我再去主要在于fish到你这个pixel的值,所以你其实是本末倒置了,还有最大的问题就是说你value这个纹理,你你只是一个pretty noise,你做一个query。
也许ok或者你做个checkboy,你也许ok,但是你要做这种复杂的,你这个解压的过程是不是特别特别慢,是不是,而且你还要做大量这种query,所以这个其实都是一系列的一个问题。
就是它不太适合在实时渲染中使用,它优势确实很大,几乎为零的储存空间,因为它只要储存参数,我们对他缺点,就是刚才说的就是解码非常的困难,你没有办法做点查询,你没有办法,这个你每次evaluate的话。
它很慢,你你可以做到什么,你可以做到interactive的级别,就是说你可以在3d max玛雅中,那种什么实时的这种retracer中啊做,但是你没有办法说我在游戏中做重视。
因为游戏中你要做到啊60帧,120帧,这种很难,而且你没有办法提前计算,就me map,你没有办法做prefer,你要做实力波,所以这些导致了你这理论上来说呃,呃很很好很不错。
但实际上来说就是很难以使用,当然了,也有一些方法,也许能克服其中那么一点点的这种装备,比如说有一些研究所,ok我能不能解析的做个滤波对,因为前段滤波的话,我们一般可能就是做一个做一个filter。
对不对,你理论上来说我可以做一个假设,我们假设能不能去做一个把这个filter这color,这个就相当于这个这个这个这个卷积的过程,用一个neo model去表达出来。
也就是说我们提前我们不是value那个点,我们直接去value的那个卷积的那个解析形式,行不行啊,就有些研究就是做做这个做这些方向的,那general来说呢,就是他的这个总的来说它是一个可泛化性不强。
因为它实际上它它是它是它这个它可能对,具体到某一种这个数学形式,它可能能够给你推导出一种,卷积的一种解析形式,但是你说刚才我们说no graph这种东西,你现在没有任何的工具能够实实说。
我能对它做进行解析一波,因为这东西实在太复杂了,对,所以我们今天的这个课程总结,就是讲了几个几点吧,第一个就是什么是文理,对不对,我们主要今年文理,不该说就是它可以表达很多东西。
但是我们这节课主要讲的还是这个纹理和材质,之间的关系,其他的话,然后就讲了两个重点,一个是有纹理的生成,生成就是如何去获得我们的文明,就是就是比如说我们可以通过照片或者啊,或者通过过程式的纹理啊。
或者现在有大量的攻击,用算法a啊啊,对不对,diffusion model啊等等,然后呢,我们最后讲了一下这个纹理的使用和压缩,因为我们生成了纹理之后呢,我们要把它拉到角美学上。
最终我们需要在这个实时管线中进行渲染,而实时管线中呢,纹理单元通常是一个well pc的待遇,大家可能不需要很care他怎么word,但是从理论上来说,我们还需要还是是。
最好是知道他具体应该是实际是怎么工作的,而且我们也非常如何,能够改善它可能存在的一些问题,所以说我们今天讲的文理的使用和压缩,使用的话,我们需要考虑的就是纹理的这样的一个滤波,和他的这个频率问题。
以及我们为什么要做这个mamap,对不对,以及为什么需要做做这个anta,nartropic filtering等等,最后呢我们考虑到这个纹理的压缩算法,就是我们也说这个纹理的压缩啊。
它其实是一个非常重要的,你不能说直接把一个大图片往gpu里面加载,对不对,你必须需要一个压缩的形式放在gpu里面啊,问你的这个解压需要注意哪些点,以及怎么样去设计,以及什么样的压缩算法。
是一个比较好的一个压缩算法,也是一个比较呃有一次话题,而且其实比如今年也有呃,今年比如也有最新的这种研究工作,这项车能不能用这个神经网络,或者说用这种ai的方法去做这个纹理的压缩,比如说你。
你只要能够满足我刚才所说的几个要求,比如点查询啊,对不对,包括这个快速的解压等等,你实际上来说就可以成为一个不错的压缩算法,你如果说你不是仅仅是主动与这个block base的话。
你也可以说设计你自己的压缩算法,所以说今天的课程内容大概就到此结束了,就是同学们如果有什么问题可以可以可以留言,还有同学有问题吗,如果没有问题的话,可能今天就先这样了,我估计也下播了。
好谢谢大家前来观看。
GAMES106-现代图形绘制流水线原理与实践 - P11:11. 着色器优化 - GAMES-Webinar - BV1Uo4y1J7ie
这里面去做嗯,所以啊我们今天讲的这个水准优化,主次机优化,其实是更多的是偏向一些代码优化的一些东西。
第一个说到这个优化吧,它单是有两种优化方法,一个是手动优化,一个是自动优化,我们先讲一下这个字啊。
这这块的话呃,在这个,一个是首先是动机,为什么要做一个缺点优化,这个是由这个SHADER本身的一些性质引起的,像今天的话一些3A游戏啊,一些大的游戏里面,这个虚的其实是越来越复杂的,像这种啊。
这是一个SHADER给他的一个material Buff,可以看到中间的节点非常多,要去取不同的材质,不同的呃一些转移,最后面全部拼起来,那他把他这些一长串的一个逻辑,展开成一个代码之后。
其实一个SHADER代码就变得非常的巨大,这个庞大的代码里面要去运行这个每像素,去运行这个庞大的代码,会造成一个非常大的危害,所以我们只能算是加速这个的计算,就是啊加速游戏的一个整个绘制的核。
心的一个核心,另外呢啊因为这个fragment追的,特别是现在那些第2trading这些,他是要每个像素去执行一次,那我们的各种设备,从当时的IPHONE960640,然后到今天的一群pro。
这个分辨率其实已经涨了不止十倍了,并且它的刷新率也在不停的涨啊,从最开始的30HZ,60hz,然后到这个一生pro是需要90HZ,那甚至有一些啊,像那个奥克斯,这些都是放120HZ去设置的。
那在这么高的分辨率,还有这么高的刷新率下,怎么样去做作,那这个问题,那做这个水准的优化呢啊有几种方法手动优化,首先啊一种是在B阶段去做它的优化,就我们下地方啊给他这个图是一个追随example。
左边是原始,我们写个,右边是编辑编译出来的一些中心代码啊,一些汇编代码可以看到,其实呃一段很短的一个编辑器,一个三个圆点的,或者是那个CD加代码,最后编译出来的是一个非常长的一些指令串。
然后在没串的时候呢,实际上这个冰淇我对这个一些优化给做掉了,这就是编译阶段的一些优化,比如说像这个左边两个coding哦,在这边加了一个,当你的啊一个赋值,然后最后面这两份代码编译出来的。
其实是同样的一个指令集,像这种东西,这个编译器已经帮我们给搞定了,然后T还,大概是有这几大类啊,主要是在一些常量,还有一些变量上面去做一些优化,首先第一个是常量的传播啊,像这种我们一个值X等于一啊。
然后最后面就是它X,如果这个编译器,可以检测到这两个东西的相关性,它可以直接帮我们把这个变量的赋值给拿掉了,然后像一些第二种是常用的折叠,像X等于XY,这两个东西,其实是同样去给到了一个Z做一个赋值。
第一期如果可以检测到这个相关的一些关联性,那他同样可以去做这个啊,一些中间变量的一些折叠或删减,就是他可以做的复写传播,这些传播主要是指一些呃,A复制到B啊,XY先把X赋值。
然后X传播到YY传播到一个return,那这个东西有一个渐进式的传播过程,如果编译器可以检测到这个过程的话,也可以同样把它给消除掉,然后一些公共体表的消除,这种的话是其实是啊一些更高阶的编辑优化了。
就是说他可能会检测到某一些关关系式,它是可以互相替代的,这样是X加Y和Y加X,其实都可以用一个tempt去表达这个关系式,包括这个乘以2×6,他也可以做一些算式上的一些符合,那最后面就会把这个。
这里其实应该是个TP乘以八,这个东西有一个小小的,比如到时候我会改掉,所以这个下面是Z等于time乘以八,然后直接return z,就把这个复杂的一些OP给节省掉了,还有一些是无用代码的消除。
这个我们刚才其实讲过了,就是一些当米扣的这个X值完全没有变化的,那呃可能这个编译器直接帮我们省略掉了,就是比较容易检测到的,还有一些方法的内联或者内啊啊,常见的就是一些cg function。
如果说我们去显示的,去把这个SUBFUNCTION去定义成1line的话,那些内联函数的话,他会直接帮我们把这个sub,function给替换成一件代码啊,最后去放到这个,一听有问题的话。
中间然后呢呃非常复杂的变形的话,那就不去打开了,但是要提一下这个B使用的编辑器,还有我们使用的,而且他的不一定是稳定啊,他有时候会不会有时候不管,养成良好的代码习惯,然后除了编剧的话呢。
我们真正可以人为操作的人为控制的,更多是在这个执行阶段的一个优化,其实现在优化呢,这个我们主要还是讨论一些手动的优化了,既可以优化代码,就是说你可能会把一些复杂的操作简化掉,能把一些啊路啊。
一些循环啊什么的给干掉,这个是代码级的优化还是算法级的优化,算法级的优化呢,更多是去体现在这个用不同的算法去实现,同一个事情,在一些复杂度上的优化,然后这个一些资深程序员可能会很严重。
在这个过程就是更体现你们马力的时候,然后呃关于图像嘛,质量就是说我们去优化一段代码之后呢,大部分情况下呢,它是如果说我们做这个coding优化的话,其实我们的目标是做一个无损的优化。
就是说我们修改这个代码可分的提升了,但是我的性能不需要它不要下降,但是呢有时候也是会有损的,就是我们用一些近似算法,像一些啊,用一些texture去替代一些计算等等,这个怎么样去做这个图像。
就是说我们做一些近似之后,它会损失多少的质量,这个时候通常是一些资深的程序员呢,它其实可以直接去分析出来啊,比如说在一些DEBUTE场景啊,或者一些光的远近,他可以去判断这个事情啊。
当然最后最不济的话呢,还可以去run1些真正的把这个设计的一下,去哪看的,matrix是测,这些都是去评判他的一些优化质量的视频,然后的话最后的话是这个接触性能分析工具,这个东西。
我们前后期的其实各种各样的工具,然后哪些是AI啊,IO瓶颈,哪些是迭代的瓶颈,这些东西其实大家呃,要可以去通过这些分析工具找出来,去做一些优化呃,但是在做出优化的时候呢,其实通常是跟那个硬件高度相关的。
我们必须是知道这个GPU,还有几集的注射器体积的缓冲啊,然后这些每集的啊存储缓冲之间的他一个代价,比如像一些需要的memory是在一个log rather block。
this thread还是可共享的,然后eglobal memory是画block,但是它的速度比较慢啊,然后如果要去读些啊CPU上的一些数据,那就是特别慢的,这些都是大家心里在做的事情的时候需要有数。
这边的话有几个做手动优化的,是一个简单的一些基本准则吧,就是说要去清晰,这个是代码所能造成的一些它所消耗的计算器,比如说像这个我最先是那个MD的dog panzer,给出的一个东西吧,然后他这个V啊。
GP2就是就是这个向量积存T的一个啊,容量最好呢,这个寄存器是少用能少用,但是如果用的话,那就不要超出他的上限啊,它会动态的在做进行,变到了一个编译器代码之后,他会去动态的显示每一行。
它所产生的这个寄存器的消耗,这些是可以看到的,然后一些共享内存啊,LDSS还有shared memory,这些东西其实都可以可以通过一些profile里拿出来,像IBS学院MARY那些啊。
就是更慢一些的一些缓冲了,然后如果这些东西都抄出来的话,最后还会去拿一些嗯显存那些2G显存什么的,直接做QUINARY去造那些临时寄存啊,做一些寄存器来使用,但这种这种东西应该是尽量的去避免。
然后第二个比较重要的准则是减少一些IO,像一些内存IO,一些内核显存,还有寄存器的一些相关的读解,能尽量的减少,尤其是CPU啊,内存到显存的路线,这个是非常要啊,注意到第二个就是一些东西的避免一些空档。
有现在的话他们是优化过的一些,如果老版本的话,还有一些,特别是不是对SA的一些硬件上面有注意,这个读写的话,它是有序读写的,就是说啊简单的一个规则,就是说一个相邻的thread,相邻的那个pixel。
他如果是在一个block,你去读那个memory的时候读显存,还有读内存的时候,音量是一整块一整块来读,如果是交叉轴,它中间有一个pride,已经是非常细节的东西了。
一般是要拼到128给DESTRIDE,然后的话也可以去教较好的读出来,如果是一种完全随机的读写,这个基本上会把你这个整个程序卡崩掉,这些需要注意,然后一些减少代码循环啊,包括一些代码的分支。
if else这些东西尽量能少用就少用了,然后复杂OP在cos这些东西,其实都是用一些特殊的一些指令集啊,它一些特殊的啊计计算单元去实现的,这并不能够用那种简单的LLU去做,所以这种事尽量避免。
如果要用的话呢,呃在绘制里面其实有很多近似的,是可以像sn cos这种数值算法,其实都有一些近似的,就是用查表,简单来讲,它就是会把一个常用的一个数值范围取一,先做好了啊。
这些你可以直接去找这些简化方向,就直接用个卡插板的方式拿出来它的一个结果,这个也可以,如果不需要非常高的精度的时候啊,这些都是足够的,然后去网上的话就是一些啊更一些一些tips,做优化的时候可以常用的。
比如说是常用的一些策略上,其实把这些T移到这个顶点着色器里面去,这个其实在后面会有一个更进一步的展开,因为在那个我们说到在这个fragmentation of pixel,所里面去。
它是要图像素去做一个计算的,那像这个球啊,你可以看到它顶点数量,其实就会比这个像素的数量低了很多,那如果是说在顶点上去做一个图层,上做一个计算,然后把这个颜色传到这个FRAGMENTOR。
是这就可以做很大的一个计算啊,讲话像这个ground lighting,左边给的图是,其实ground lighting就是直接顶点计算颜色,另外还有一些像UV坐标变换的新的坐标计算,有些天赋球变化。
还有物质赛这些啊,不是这么高频的东西呢,其实都可以去移到指定的注射器上面去,对这个画质其实不会产生太大的影响,然后呢第二个TT就是说啊用一些预计算,其实也是查表,就是一些复杂的预计算,可以啊。
复杂的计算可以提前算出来,然后存储下来,像这些一些noise noise啊,啊这些noise如果是要去啊,用一个数值方法去计算,其实很耗时,那一个简单的方法,就是我直接把这些东西生存好。
然后我直接啊只做一个UV的一个采样,uniform采样,我就可以直接在上面去得到一个,比较好的一些noise啊,这个在在simple的时候其实很重要,然后还有一些是像这种复杂的NBACLUSION。
nba pollution的话,由于他这个是你可以认为它就是一个自遮挡嘛,就是个模型自身相关性比较大,所以也可以认为是接近静态的东西,可以先预计算下来,那就不用在实施的时候去做采样。
还有在一些夸张点的话,那就是说一些,其实整个trading的结果都可以预计算,然后去烘培下来,作为一张纹理,这个主要一般是用在一些背景上面,还有一些啊静态光源上面,这样整个场景房间。
如果说我们只需要做一个纹理的读取,然后直接把颜色给出来,那计算量就相当低了,另外呢还有一个措施的,刚好读刚才那个用保存数算是反过来的,有时候是我们需要去用计算机保存的,比如像这个PBR模型啊。
呃有时候是需要去用这个propane和NDOTV,去采样一个LET纹理,然后这样子的话会多一个多一次保存,但是反过来呢,就是说嗯,其实也可以用一些算式的方式去数值函数,去逼近这个东西啊,就像右边这样。
这些就可以直接计算出这个呃卡table看情况,如果是说我们需要减小计算,然后这个访存带宽还比较宽裕的话,其实可以反过来就是比较灵活的事情,还有一些,常数代替一些地方,但是对正确的说法。
就是说这个数值给到编译器,会帮你去把它处理掉,提前弄好啊,然后你不要去给太多的这些影视的,如果是这些数值是确定的,不要服太多的这个floating,不要自作多太多的变量,这个东西不好。
然后的话像一些啊派出派half,half pie这种常用的东西,直接把它啊DEFI好不学,不要在月经时候去计算这些啊,刚才这些东西其实跟那个编译器优化,只是游戏相关的编译器嘛,有时候能兑换。
有时候不能优化到,我们还是尽量养成一些良好的代码习惯,另外再下一个就是一些低精度数,像这个编译器里面啊,SHADER里面float half这些都是可以支持的,像高精度的,然后用到这些像HDR。
还有一些UV啊,还有一些数值,你如果确定可控的话,那半精度其实也够啊,那这个系的话像一些要怎样颜色的话,用fix也够了,主要是在一些mobile device上面会比较敏感啊,剩下几个tip。
一个是右键进行标量计算,就是像左边这个地方嘛,是先把这个有两个标量,一个向量,然后向量乘标量,我们先把标量全部乘上来,再乘向量,那最后面的话其实这个只是有一个三个乘法,但是如果反过来。
我们先把一个标量和向量乘上了,展开了就变成一个三维,这会产生一个三个乘法,然后这个地方再乘一次,又会变成三个乘法,这个计算量加倍,所以的话优先去能够合并的,把一些维度比较低的计算合并掉啊。
这个是一个技巧,然后分支分支是需要避免的,因为我们的GPU其实是那个FI啊,MD就是single instruction,Artical data,就是说啊他的指令控制器其实是非常少的。
机动单点比较多啊,是这样的话呢,呃可能是几个reg,32个spread啊,一个直线,一个指令流程,如果16流程里面产生了分支,像这个一等于一啊,叉叉叉这样的话,那就求他。
其实你可以认为它分支里和分之外啊,两个东西它都分别执行了一遍,因为他只有一个指令串去避免这种情况呢,就是用一些啊并行化的一些OP去替代下的if else,像这if x等于等于零。
其实是可以纯粹的去使用一些数值的方法,就是van equal,这个地方你可以看一下啊,这个其实不难理解,就算一个啊,sin x减Y它如果相等的话,那就会反复返回一个零嘛。
所以就可以直接用这种并行的一个东西,去替代掉,做法就是减少一些20的数量,所以从软件工程的角度呢,摔的不成功呢,刚才那个是会更干净,但是你在试用的时候,其实是又要把一些税的合并起来啊。
不能用大税上面去降低他们之间的一些传输啊,减少pass的数量,这种都是一些,然后除了这种啊这技巧的,我们在上上期空气技巧其实是个人的修,为了看大家的修行,那更多的一些常用的。
可以规范化的一些东西是层次细节,OK我看有些问题分享点,这样就可以,意思是说呃LDLD的话一般就是一个原理,基本的原理,近大原材能有更多的模型,它远地方能更稀疏的模型,就是在Mac上。
然后着色器其实也有同样的一个LD,在近的地方我们用一个啊带有比较的一个LD,就是他的代表他们更强和地图更复杂,但是远的地方可以用一个简化掉的,然后D啊一个水准,所以在我们真的好,这个地方应该是max。
你比如说像那个我们更具体一点的话,像非得上做一个层次,细节大概有什么样的呢,一个就是一个方向模型的迭代,比如说这个LD0啊,一二是最就持续可用不同的光照模型啊,去做同一个事情,GGX是最复杂的计算。
最真实的同时也会最慢,然后可以换成独立,从,然后再远一点的话呢,立风的话有一些MICHAEL啊,一些啊主表面散射一些特征,然后再远点就可能用啊一个uniform的一个采样,就只要有一个简单的颜色就做。
甚至是说啊原地方的就用一些一计算的,可computer的一个颜色,这就行了,然后另一种LD的设置呢,就是说把这个我们最终能光大成在哪,拆分成不同的成分,然后去用这个层次。
那个光的成分去做一个LD最近的地方啊,你可以认为是有一个,我们可以把所有的component都加上来,你把这ACULAR也加上,然后稍微远一点呢,SPECULAR可以去掉,只留DEBU,那这种是一种啊。
还有带远一点的地方呢啊,可以把这个intellect这种东西都可以去掉,链接光这些东西都可以拿掉对,然后这个烘培烘培刚才也讲过了,就是把这些颜色提前放到这个啊。
the text the texture里面去,呃,然后的话因为你烘培弹的像一些集合,嗯嗯啊,那你就随着这个集合的LD,自动的去做一个简化了,然后这个新的看try也可以有一些整理的心啊。
他也可以有LD啊,去做这个4。6,接下来,其他的可能更像个工程,投递一些商品进行学术。
在公司上啊,首先是motivation,就是这个手动优化,其实就是我们刚才讲他是一个非常惊艳性的,非常工整的一个东西,你这个程序员要知道怎么去调整那个代码啊,然后要知道怎么修改这个评估。
这个修改或者代码的效果怎么样,这个也要经验,要怎么平衡质量和速度,所以也需要经验,那常常呢就是说不同的FD不停的try不同TT,其实是比较枯燥和比较繁琐的一个过程,那所谓自动的话呢。
就是我们把这个东西全部给自动化啊,要让计算机自动调整这个代码,计算机器自动评估这个代码在调整之后的质量,然后之后去自动去找这个质量和速度的平衡,把这个整个东西都自动化,那他整个框架的是这样的啊。
就是说螺丝器进行优化,那首先我们的input是一个啊SHADER,一个shader code,一个着色器的一个code,然后除了这个code之外呢,我们一般这个它我们需要优化的场景,Mesh。
还有他的一些permuniform gm,像风暴什么东西,其实也是需要作为这个中优化框架的一个输入,因为从原理上来说呢,这个水井扣会产生在它所产生的结果和L,是跟场景和跟那个parameter相关的。
然后这个格射器呢,他会去尝试产生不同的这个啊,优化后的一个代码,这个优化后的代码呢,他要去评估他们的一个质量,像这个啊通常的话会把它顺出来,这个最蠢的一个办法,但是也是一个智能效果最好的一个办法。
然后我们要找他的一个潘尼托前沿,因为不同的视角,你看啊,我们如果画出来一个横坐标是它的一个误差,粽子表示一个时间,那我们要做一个时间和误差的平衡,那肯定是希望是在这个要么就是使用费用。
要么就是这个啊艾尔之中,就是在这个叛逆多线上面,去找我们所需要的LOD,或者是我们选择需要的一些优化的设置,有时候呢你也可以去培训,比如说像我们就是啊项目里面的话,可能会给到一个误差上限。
就是说我们这个误差不能大点的值,那么点到了这个插上线之后,去找这个时间是U的一个点,那或者反过来给定了一个啊时间,大学我们去找这个补差最low的,这就是它实际的一个用法,然后对于不同的这些东西吧。
像这个怎样去优化这个着色,可以产生一些啊它的一些various,然后有各种各样的工作,怎么样去评估它的一些误差啊,嗯更好加速这个帕尼克现在寻找队友,又产生了另外一大类的工作。
我们会后面会简单的去覆盖一下这些事情啊,这里这些东西呢啊,就构成了这两个方面的研究吧,就构成了这个呃,税的减速简化的一个主要的一些研究内容,然后我们再再来开开心一点啊,就是说这是一段文字器代码。
就是给定了一段总是代码自动器简化,就是去替换中其中一个变量一般是真理,其中的啊有可能是变量或者是随机sample吧,其中的变量,然后尝试对这些变量去做一些替换,或者是做一些简化的操作地方。
他如果是就这个bin这个arrival,他尝试去把它简化成,把其中一个FS给拿掉,所以然后呢,当然DPS拿掉了前面这个相关的一个依赖性,这个PS这一行也可以拿掉了,这就是一个产生一个SHAVARI。
这是其中一个规则,然后给到了之后呢,就可以得到这个变种,然后直接把这个变种拿到这个编译出来,最好放在机里面跑一跑啊,然后他的场景里面跑一跑,这个场景里面需要编辑不同的SYMPURATION,不同的材质。
不同的camera去把这个估计一下这个变种的质量,就可以得到一个普通模式啊,当然还要评估一下他的普通门面是直接跑起来,这个东西啊就可以撤了,就是这样,然后下一步的话这个东西要不断迭代。
就是如果simple这个好,那他可能会更尝试的去做其他的简化,还有其搜一个这个再去做简化,他去搜想试法,第二行啊,做一下解放奥数,第二行把这个后面这些地方也去去,再去做一下普通的c cot。
还有更凶的就是不断迭代,那自己最暴力的情况,那可能就是一个直接给一个ambient,作为他的一个输出,这个整个东西去多去评估遍,话说刚才给他可以给他做前面那中间一些细节,就是说怎么样转换代码。
有哪些型号,然后啊怎么找到最优,这个整个一个框架,就是构成了这个我们真正去做的事情,呃,代码简化,首先是呃嗯这个其实也是编辑的东西了,比如说比如说给定一个代码2×7加三,那呃上个编辑的原理的话。
那应该知道就可以生成一个pass to pass pass key啊,然后他等下一个STG都快抽象语法术,这个是更抽象更简洁的一个表达。
在我们真正做的时候呢,是会把这个学的代码用一些glam,或者是一些啊dire compiler这些工具,它可以帮我们自动的去把这个后的风气,养成一个自身法术,然后还有一个是他的一些内地的对其依赖关系。
和这个抽象明白旅顺或者sample的去尿,然后去修改,然后我们再根据这个rap找出来啊,我,有的东西都不需要了,那我们就会把对应的那个code,从这个dependent ground找到,它也是对应的。
上图扣的也是事情,最后去产生一个新的代码啊,然后这个产生的代码呢,一般来说你要去做一下语法检查啊,这个是比较基本的啊,如果不行的话,可以直接丢到水准里面,坦白去做,有条件差,这个都没问题。
然后群体的一些简化规则,最常见的就是啊也是比较有效的一个就是简化,就是表达式简化,像一个normalize,像这个东西,他可能把这个IDIR拿掉,你可以把LDIR拿掉,就不知道就可以产生不同的变种。
我们这个叫自动计算机自动去看嗯,然后第二个是loop production,就是有一些循环啊,可能不清啊,那个比较小的问题就行了,这个主要是放在一些收敛一些,那个迭代算法一些,比如像你找一个什么最优化。
要有个递归最优这种东西的话,其实可以去降低一下它的循环的次数,然后像一些表达式的替换,把这个替换成这个,或者反过来把这个替换成这个,就是NTL替换成N乘H,这也是它的产生的效果是很多的,H是办法。
你表达的是那个一个物体表面的一个normal,这些东西都是有的。
然后一个比较一些效果比较好的,就是这个把一些fragment的piece的一些代码,从一上移到这个TX水的上面去,这个其实我们刚才已经讲到了,在手动优化的时候已经讲到了,就是一些看一下的话。
就是个pad加DC这个这两块,完全这些基层颜色全部都移到这个去了,然后开始追的算出一个演奏,这就是我们最终的它的呃,从你的意义上理解呢,就是他干了一些事情,我算一个三个点的,三个顶点的一个值。
然后啊啊就每一个兵它的一个重心坐标,每个。
这个顶点的value就是不上,然后啊更进一步呢,就是说我们现在因为有些TELLATION嘛,有些绝对比,就是说如果说我们不是移到一个JM去学的啊,不是移到TX水的,我们移到这个啊。
Technation shader,那其实就是可以做一个力度更细的一个,进行逼近了,因为一个原始的三角面片,我现在可以分成很多个分技巧的三角面片,然后我们算这些细小的三角面片,上面的是纸,你可以看离线。
然后还有一种是做这个呃信号以后的话,就是我们呃用一些看看啊,content的一些量去替代掉它原来的一些呃变量,这样子的话如果是一些值啊,比如说某一个变量。
它在这当前的thing configuration上面,它的变化比较smooth变化不大,那他就可以被替换掉,一种常见的就是说比如说一些背景啊,像一些平庸的群是物体,它的背景离我们很远。
那清晰跟方向相关的变量,其实就基本上方向无关了,这些东西有可能最后都被这个能量去改掉,然后再来的话是一个一些高阶的逼近,就说我们用一些desire曲面啊,或者什么去逼近这个函数啊。
就是这个你可以认为就是刚才那个啊,一些进行逼近的一个进化版,像这个用北大船走了之后,可以做一个非线性的一个高阶的一个baking,这些是一些常见的策略,OK呃给到了这些策略之后,简化策略呢。
那我们有了简化策略,有了简化的SHADER,那接下来就是这个大概就可以连起来了啊,首先是把这个SHADER扣放到这个啊,一些编辑器上面去,ST和一些赖数编辑部居然后就去尝试啊,对。
然后再给你整个系统去输入一些,你这个定configuration,Fighting real material,然后去产生大量的啊。
去尝试去搜索这个SHADER的一些arrival和expression,然后去做apply,各种各样的一些表达式进行简化表达式啊,然后去生存新的一个create vari1些变种水的变种。
然后去评估这个水的变种啊,最后再递归表达对产生各种各样的一些呃,LD等于C的,这里面一个核心的问题就是这个喷是非常多的,他可能有上万个,然后我们在产生的变种的时候呢,呃一般不会去盲搜,因为盲搜的话啊。
或者是那个暴力周的话变得太多啊,特别是我们现在的些现代的追的很长,你这个combination太多,所以会用一些啊,genetic program1些拼音算法计算法的话,我们就给你出一。
就是如果一个父节点,它也产生了一个F1个C的,它的质量和error都比较好,我们还有他的数字简单一个小书,希望去听这个shader violent去进一步简化,就是有这么一个嗯父子的关系。
有优秀的trade van产生更多的一个变种,基于他就是这么一个事嗯,具体的话我就不展开了,所以是不是我们学的这里面的内容,然后的话呃展开评估一个变种吧,因为它也很耗时,我要有各种各样的视角。
各种各样的性能啊,有时候呢我们评估的规则也不一样,是如果说我评估一个,简单的评估一个那个颜色的话,还好一个LQ什么的就行了,但有时候是要评估他这个能耗啊,这个东西就会更啰嗦一些。
甚至有时候要比那七八糟的,那么实际使用的时候去确定的,这个没有一个是所谓的一个最优的东西,所以给他这么大的这么一些复杂的评估方法,评估很很拉,然后变种也很多,那怎么加速产生这个优化空间。
就是啊初中优化的一个核心问题和,所以我这地方啊,今天会讲两个核心的一个问题吧,呃觉得比较重要的一个就是,有一个是两个优化的策略,第一个就是用贪心算法去做优化,我们原来的话会说哦。
产生很多那个啊各种各样的东西去填满,这个空间,好一点的话呢,就是说能够想到连这个潘尼托前沿去,他的一些指点点啊,如果当前这个从这个最难用的一个原始的水准,开始去搜一个啊它的一个最优的一个子节点啊。
哪个地方,然后再沿着这个地方用一个餐厅的方法去搜,下一个下一个下一个下来,当然这个地方啊肯定会漏啊,但是它的时间上,只能是说我们可以做一个时间和质量上的B啊,这为琼州的话,他这个是np hard的问题。
所以啊你用弹性去逼近,那肯定会漏,然后他另外一个策略,就是说他不去直接去啊,评估这个着色器的运行时间,那样你就省掉了一些KY的时间,甚至省省掉了一些这个实际浪的时间,他直接根据商业调查数据。
还有一项能力和操作数,然后去做一个加成啊,平均值来估计呃,这也会有产生一些误差,然后的话对于着色器的误差呢,就是它的绘制质量的误差,它啊弄了一个是这个误差缓冲,它就是说做某个啊,比如说某个表达式的简化。
它产生了多少误差,然后他会把这个,操作和误差的值做一个tale推塔,然后去存起来,那下一次在其他shade van的时候,再去碰到同样的一个操作啊,去删减同样一个节点的时候,就可以拿这个缓冲拿来去理存。
去替代表最真实的run嗯,这个就是它的一个基本的能力,然后这是第一个,然后第二个我觉得比较重要的是这个啊,实时的一个FICATION,所以我们知道这个刚才所讨论,那些虚的简化方法呢。
其实都是一些离线的东西啊,那我们在实施的时候,因为我们在游戏过程中啊,是每一帧或者是每一个场景它在时时变化的,那有没有办法去说我们在边变化,再根据在这个游戏运行的过程中,去优化这个税的。
这就是来到这个呃,Real time shader,Runtime,Shader optimization,它的一个核心思路,就是需要去降低它的一个优化空间,在最开始的时候呢,他会给到一个新的代码。
然后去在第四季是谁的rate吗,Sha sha rummliquan ru,去产生一些大量的水的value,那雷神把这个看啊,完了呢,这个裁缝店的话,他就根据这个SHADER之间的一些相似度。
还有它的一些包括一些东西去做CLAVERING,把这个整个空间变得很小啊,然后在每集上呼吸里面的带,那么在RUNTIME的时候呢,呃我们就不去搜索这个东西了,我们直接拿这个呃简化的空间。
还有一些简化掉了一些啊,SHADOTOMMY去做一个匹配啊,在一些比较小的一些空间里面,去找一个最优的一个解,所以呢就可以达到一个实时的一个实时优化,软优化的一个事情,然后第三个呢就是呃我提一下。
就是这个shader transformer,这个是它这个机器学习水等简化与嗯啊,简单来说呢就是说把这个语言模型啊,我们像因为可以你认为这就是一个全封了,现在是做那个资源源处理吧,啊挺牛逼的。
我们就用shader transformer,就用transformer的结构直接去解读这个呃,Shader code,让他们去网络来估计我们这个学者的质量,简单来说就是这个网络大概是这个结构。
它要接受很多很多很多样的东西,一个是嗯一个fat code,然后还有一些我们的场景configuration,然后经过一系列的全form tension,后面才输出一个优化的一个表达。
这个表达呢是可以做自由的一些删减的,就基我们就去黑,他也没说啊,节点打掉之后啊,群加群就可以得到这个简化,后续啊,其实就是把这个呃把这个C的扣的一个编码,然后映射到他的,他的这个想要控制里面的一个嗯。
那句啊就是做这么个事儿啊,这样的话就可以把这个一些计算误差的,一些代码,那些特别是一些观看的时候,那些这封面图加起来,然后的话到了这个是之后呢,我们来看看结果吧,就是说这个数据的到底有多大的能力啊。
就是说像在这些人的模型等等模型,在这些皮啊,皮肤表达模型上面啊,可以达到不同的,然后这个LD上,对应的是第三个节点,然后,快的,然后后面几天应该,啊那个tank我就不考虑了。
这几个点就会取出来做这个LD,然后这是另一个example,就是同样就是就是多目标了,因为它既优化了这个时间与优化,所以他画出来这个很符是一个啊,那没的一个空间,但同样其实在这个空间里面。
也可以去找这个判断图啊,这个是变的,这就可以做多目标的BO,应该是对今天的这个主课就讲了。
GAMES106-现代图形绘制流水线原理与实践 - P12:12 流水线LOD 技术 - GAMES-Webinar - BV1Uo4y1J7ie
这个题目是流水线的led技术,这个是呃我们更接近一些。
我们在整个流水线的一个简化,去看看,是在呃整个框架里面去怎么去简化这个东西嗯,首先是回顾一下之前我们已经上过的一些课,一个是流水线的优化技术,像这个几何的LD几何的简化,这个我们讲过了。
有一些中间设计一些简便的算法,一些几何距离和网格简化的这些方法啊,就是怎么要把一个非常复杂的几何,去用一些非常小的三角面片来表达,然后啊我们又也讲了这个关于纹理的压缩,包括一些纹理的滤波啊。
纹理map map算法,还有一些呃压缩算法,这个是对一些纹理,还有一些材质进行压缩和LD,然后我们还讲了这个着色器的LD啊,包括给到一个着色器代码,怎么去简化这个着色器的代码,使它在运行得更快。
然后怎么样去预测这个着色器的质量,绘制质量,还要怎么样去做一个着色器的自动优化,这些都是之前的内容,然后啊今天的话呢就主要是讲一些。
在整个呃流水线里面去,怎么样去用上这些LD的东西啊,各类的资产,去让我们达到一个最终的一个加速效果。
所以这里面首先我们去讲一下这个,就说我们在啊,不管是几何的LD或者是纹理的LD,但总的来说这些LD啊都可以有这些分类,像是离散的连续的视角相关的,还有一些层次结构的,这些都是一些主要的一些分类。
像理想LD的话,就是有一些啊不同的框架,也就是说我们会有不同层级LD0,L d e l d r3,这些东西都是分散的嗯,他的话一个好处呢,就是说不管是纹理和几何,它的通用性比较好。
因为我们只需要每个LD之间,他不一定要有一个连续的关系,所以他做什么样的人,资产都可以用这个理想LD表示,但是他的这个缺点就是它会有一个popping的问题,可以看到这个框里面。
不同的LED层级切换的时候,其实是这些啊,模型也好,材质也好,它是会不断的跳变的啊,这些都可以看到,所以为了理想L第一个最关键的一个问题呢,就是说什么96ping的效果常用的几种方法吧。
有些是第一个是延迟这个绘制效果出现,就是不要在这个距离到达这个距离啊,关键点的地方直接就跳变,稍微延缓一些,然后还有一个就是用一些几何motion的方法,下面这个字图,最左上角这个是一个LD1。
然后最右下角是一个LD2,这两个中间的MING是可以通过一个顶点的差值啊,做一个集合的MORING,当然还有一些其他的一些几何mo,另外一种呢就是透明度的混合,像LD0到D2中间的有不同的层级。
根据它们距离的变化去做一个阿尔法布ending,这是另一个比较常见的处理技术,然后呃,下一个的话就是,我们如果有了一些离散的LD去,怎么样去在这个离散LD之间切换切换,这个需要去寻找一些准则嗯。
在真正啊绘制的过程中的每一帧,我们其实要对每一个模型,每一个物体都要去独立的选择它的一个LD呃,常见的一些方法是根据代表点,就是我们在这个LD,在这个模型上选取一个代表点啊。
然后这个代表点跟我们camera的距离啊,由他这个距离的变化去选择不同的,然后另外呢就是还有一种是啊,找一个包围球去代替这个模型,将包围球它的一个面积,在这个屏幕空间做一个投影。
根据它这个面积的缩放去选择对应的一个呃,另外呢还有一种是嗯根据模型的一个包围盒,包围盒的话就是一个四四方框的形状,它可能会比这个包围球更准确,那关于包围盒的投影的话就会有很多种啊,言五花八门的算法了。
因为这个包围盒本身也不是一种,它也可以有很多的呃简化的方法,最后一种是基于面片数,就说我们如果想要维持一个游戏,在一段时间内的一个稳定帧数,那我们可以去给定一个目标的一个面片数啊。
然后根据这个面片数来选择D,这样的话整个的帧率是可以保证的,但是有时候质量就没法保证,会出现一些artifact。
呃第二类的话就是连续LD,连续LD跟理想LD这个顾名思义,就是说它这个每个节点之间,每个LD节点之间,它是可以通过一个啊,一定的算是一定的公式去做一个离散的差啊,连续的差值,这样子的话。
比如像在这种地形的过渡的过程中,不管我怎么走啊,我都不会有这个popping的问题,在每一个position,每一个distance它都会有一个合适的混合嗯,这个离连续LD的话。
它的一些好处是真实感更高,过渡更自然啊,很平顺,而且理论上你走到非常近的时候,它也可以通过一个算式的方式去解,析的方式是给到你一个非常好的D层级,然后下一类就是试点相关的呃。
设计相关的LD是说我对这个LOD的划分,不一定是对整个模型或者整个材质都是统一的,是根据我们视线的方向相关的呃,像这个典型的一个是这个兔子,就说我们从正面去观测这个兔子,那由于它啊跟我们在正面的地方。
我们不需要太复杂的,由于它的几何变形啊,对跟视角相关的这个深度的变化比较低,所以我们的正面地方不需要很啊很细节的几何,但是在这个边缘过度平滑,形成蹩脚的地方,我们需要一个更光滑的一个几何过渡。
所以在这个地方去用一个更高层级的D表达,所以从一个如果我们把这个,从一个俯视的角度看这个模型,那我们看到的就是呃跟这个camera垂直的camera,从这边过来,这个垂直的这一部分。
它的那个啊LD比较粗,然后在这些边缘的地方D过渡会比较平啊,比较细致,这样的话可以自适应的分配给一个物体,分配不同的细节,对于一些大型的物体,像一些呃地形啊,还有一些占据整个空间的一些物体。
这种方法是非常友好的,还有一种就是层次结构LD存储结构,LD的话呃也叫HIROTICALHD,就是说我们对一个场景的表达是首先去分层,比如像一个人的话,我们可以啊对头身体。
手四肢这个分别去做不同的hierarchy,然后手的话再去对他的那个手掌啊,手臂做一下hierarchy,这样子当我们去选择了一个行,这些HIERARK会形成一个竖的柱状的结构。
我们表达我们使用了一个头部,就是我们头部使用一个比较素的,AD表达的时候,那他所有的子note都不会再被绘制,但是如果这个头部我们觉得不够细致,那我们可能去把这个头部做一个划分,去把他的眼睛。
鼻子啊各个部分再分别用一个更细致的表达,这是一个层次的结构啊,这边是一个层次结构,LLED的一个效果图像,这些电缆上面两个是做了层cl d的,所以它可以在一些窗口,或者一些比较细致的细节的地方。
会有一些更好的表达,然后在一些平板的地方,他会可以选择一些比较粗犷的一些LD,但是如果我们啊没有这个层级化的表示,对整个船做,那在相同面片数下啊,这个一些面就会变啊,弄得呃一些平面就没办法保持了。
特别是它会有一些桅杆之类的一些小物体,去引进这些物体啊,会造成整个几何的变形。
真正在我们用的时候呢,上面几个连续的离散的视角相关的,或者是hierarchy d其实是呃会混起来用的,这些技术其实并不是有一个并不可,它并不需要一个非常明确的边界。
那像最常见的一个呃应用场景就是地形LD,因为地形是你可以理解成,他这个mesh是非常大的,它整个模型是非常大的,而且它的那个可视范围也很大,我可能我们在近的地方看到的,就可以看到它的一些树和草。
但是远的地方呢我们只能看到一个轮廓,对这个变化这么剧烈的一个模型,要去画这个模型,那D就是一个非常重要的技术,那它这个呃一些常见的技术包括了room,只有meat maps,Chk l d。
还有一些呃cue maps,我们这个后面都会去讲一讲,像这个room,它其实啊全称是real time optiary adaptive mesh,它就是一种既是视角相关的,又是一个连续的LD啊。
就是两者的一个混杂,它的真正的数据呢,裸的数据是用一个高度图来表达,它这个地形的变化,然后根据这个高度长,每一个点的高度,跟相机的距离去自适应的去构建创建这个网格,像这个啊网格。
你可以看到在这些变化比较剧烈的地方,高度变化比较剧烈的地方,对试点也产生比较重大影响的地方,它可能会有一些非常细密的呃LD表达,然后像这些呃不容易观测到高度变化的地方。
它会用一些比较粗糙的三角面片来表达,而在我们视角变换的时候呢,这些面片会部队不停的细分或者合并,来实时的做一个连续的变化,下面有一些比较更细致一点的一些划分,比如说像这个啊啊这个是一个面片。
它是怎么样做划分的,他会选择一个长边不断的切分,那合并其实也一样,他把他把两个把这个边拿掉啊,那就会合并成一个更大的三角形,这样一个三角形就可以分解出很多,然后每一个顶点的高度。
其实是根据它这个高度图来做一个决定,然后他再去计算这个,确定这个三角面片的合并和拆分的过程中呢,一般就会把这个三角面片所对应的这些,这个区域投影到我们相机的这个呃,相机的这个平面空间。
然后去计算这个平面空间的一个误差,他一个近似后的一个啊,area和他的那个期望的那个高度啊,这些东西是可以直接算出一个啊,误差的一个估计,根据这个来去确定它的分不分裂和合并好,第二类啊,常见的GD啊。
D型LD是two mix maps,它是呃一个视角相关的一个离散调低,他呃他这个东西继承了这个图像,就是那个纹理的一个Mac技术,他对美它会对整个地形做一个分块。
然后每一小块呢它会有一个不同的hierarchy,根据这个地方的细节,还有跟我们相相机的一个距离,去确定我在每一小块上去选择哪一个层次,细节啊,同样的他也会用一些几何变形的技术啊。
motion技术去避免这个popping的问题。
嗯第三类是truman to clip,max climax呢,呃像是一个环状的一个结构啊,就是我距离我们最近的是一个环,然后根据距离的啊,更远的地方变成一个层次,层层嵌套号的一个网格形状。
基本的一个原则就是越近的话,他用更更密集的一个表达啊,随着它这个距离的增加,它会变得更表达更稀疏,然后到了远的地方,如果是说像这种平面空间距离啊,它变成了一个接近边缘的地方,它这些高度啊会有更大的。
对这个视线有更大的影响,这个地方又会选择一些更密集的表达呃,它的一个好处是说他实践非常简单,它不要一个很复杂的一个嗯数据模型啊,数据给结构表达,所以它是比较容易管理啊,我们只要随着这个距离的变化。
移动的变化去做一个整块整块的更新就好了,所以他对这个硬件,还有这个串流,这些东西都会比较嗯友好,对存储的消耗也会比较低,嗯最后一个就是trunk l d,这个是用一个80啊四叉树啊。
用一个层次的四叉树去存储更多的细节,就说我们在最上层的时候呢,啊我们可能是用这个粗的四叉树,然后下一层的话我们就把它划分,然后用一个更细的一个四叉树啊,依次表达,所以它在接近叶节点的时候呢。
它的细节其实是非常丰富的,最右边是一个接近叶节点的地方,它的好处最大的好处就是它啊,不同的区域它有不同的一个密度表达,这样子的话呢,呃其实本质上它可以避免掉,对同一个区域做一个均匀的划分啊。
它只可可能只是根据一个需要,在这个在这某个区域需要的时候,去做一个层次划分啊,然后他在划分之后,它这个本身也有一个密度的一个变化,所以他可以呃更比较大的,应该是是比较好的降低这个面片的数量啊。
减低这个无效的一个渲染,就是窗口的,OK我们刚才讲的是LD1些常见的led技术,就呃总结到这里,然后下面我觉得一个比较重要的,也是近期啊研究的热点呢,就是这个可悲的绘制流水线。
然后可悲的绘制流水线为什么跟我们相关呢。
就是我们先来讲一下这个背景啊,所以是可微的绘制流水线,那首先就要去讲一个,正向绘制和逆向绘制的一个区别,抽象绘制的话是说我们有一个模型,有光照,有摄像机,有纹理,我们去用一个forward的方法。
就是我们的绘制流水线啊,过一边绘制六线,得到一个最后的一个绘制结果,这是正向绘制,那逆向绘制呢指的是我有了一个绘制结果,然后我把这个绘制流水线逆过来啊,我去得到它原始的几何光照,还有纹理。
还有camera位置,这个事情这是逆向绘制,然后可谓绘制呢,呃其实可以认为是逆向绘制的一个变种,或者是一个呃特殊特殊的类型,他是他就把这个正向过程它认为它是一个函数。
那逆向过程就是这个正向函数的一个逆函数,所以有了这个逆函数呢,就可以去求解各种各样的梯度啊,用一个梯度反向传播去把这个正向结果的一个,比如说一个pixel的颜色啊,他对每一个参数不管是材质也好。
几何也好,它这个导数关系就可以直接求出来,那如果说我最后这个地方是一个error,是个image space的error,那这个我把这个image space error,通过这个导数的形式传递回来。
那我就可以去减小这个我去通过这个梯度下降,我减小这个error啊,这这能够减少这个error的一个image space,error的一个方向去改变我的参数量,那这样的话就达到了一种优化这个。
一些场景参数的这么一个目的,所以它用在我们这个呃LD的case呢,就说啊,我如果是说我可以构建一个完全可微的一个,绘制流水线啊,我让这个绘制流程步步可危,那我就可以去通过给定一个budget啊。
给定一个比如三角面片的数量,给定一个纹理的分辨率啊,或者给定一个shading后的,然后我去用这个反向传播的方式,使得啊我这个简化后的这些材质,画出来的结果和我国初始材质画出来的结果,去更可能的逼近。
以此来优化我这个简化后的这些资产,所以呢他这就是另一种形式的获得这个LD啊,啊或者是这种简化资产的另一种形式呃,我们通过可谓绘制流水线呢,目前常见的两种是优化它的纹理,优化的几何。
有时候也会优化它中间那些同main函数,但目前呢通过这个可谓绘制流水线,去直接去优化它的那个水顶扣的,还是一个在研究的内容,因为这个水顶扣的那个变化是比较啊,这个它本身是一个非连续的。
它是一个离散的coding空间,所以它的一个直接优化是比较困难的,但目前主要是用在纹理和基本的优化呃,另外的一个呃,就是呃我们一般来说做这个东西的时候,会给定一个固定的压缩率。
然后啊去尽可能最小化它的这个简化后的优化,不差啊,而不是去这个是它的一个拍卖,最后嘛再申请一个LD资产,然后这个LD资产得到之后,就是用我们刚才提到的一些LD管理技术。
切换技术去在这个游戏或者是实惠之中啊,用上来要做这个全流水线的口碑呢,啊可谓绘制流水线,那就涉及到这个流水线的逆过程,这个是正过程,我们比较熟悉了,就是从一个几何DOTFA到光栅化啊。
到这个FRAMESH的,到color blending做出来图片,那逆过来呢叫做图片一路往回走,在图片呢,当我们有一张图片的时候,这个绘制出的图片的时候,这个逆过程一般是会在这个图片上去计算。
它跟那个广出去图片,就是一个简化后的一个素材,还有一个原始的素材,那个高分辨率的素材,同时画出两张图片,减一减得到一个L误差,会用L2或者SSM,我们要把这个误差往回传到这个color,布兰定之后呢。
一般是会去做这个pass的那个啊混合啊,这个是比较容易讲啊,理解就是说如果说我们有两个不同的,从不同的pass这些东西是要blend起来的,它是一个也是一个解析的操作,所以它的梯度也是可以往回传的。
那传到这一步呢,就要去通过拆这个着色器,去做这个到纹理和光照的一个映射啊,这个着色器的可微,我们待会会介绍一下,咱们在这个着色器语言里面去做一个可微啊,把这个梯度传分别传回去,然后再往上一步呢。
就是这个光栅化的科威画插画的口碑的话,也会有一个A小于PPT来讲这个事情,然后再往前也是一个主色系的口碑和test shader,或者绝对去SHADER,这个跟fragment。
SHADER的这个主色系可微是类似的,最后是通过一个啊一些几何的可微,去返还到这个顶点的优化上面来,这就是整个PIPINE,下面我们来去分别的拆开来讲一讲,首先是这个最后一个阶段了。
就是这个image loss啊,它的监督信息,这个整个优化过程的监督信息怎么来的,在这个我们啊一般来说会得到两张图片,一个是reference,一个是render的音乐剧。
然后我们这个reference是固定的啊,在给定了一个视角,给定了一个嗯场景参数之后,这个就是我们的目标,然后这个是随着我被这些match texture lighting camera啊。
在它的过程中在不断被优化,所以它的render的英语句也是在被优化的,这两个东西可以算一个loss啊,这个loss啊在对这些梯度会在这个阶段,这个貌似是啊,没办法对这些梯度求导,但是他可以往回传在后面。
在一些可谓SHADER的阶段,它是可以去针对这个parameter做一个优化,那我们现阶段只需要得到一个loss,常见的一些lost形式呢,嗯像一些啊L1L2,这个是呃大家可能都知道。
就是然后SSIMSSM是一个结构性的相似性,也是一个常用的东西,然后到了网上传一步就是到这个blending层,blending这个地方呢就会做一些不同pass,不同通道的一个混合嗯。
这个混合呢一般是用一个权重混合了,像这个S的一个权重,再乘上一个低的权重,这个S加D通常等于一,就是一个阿尔法不能定嗯,根据这个,我们得,如果我们可以先,甚至计算到各pass的一个混合系数SD。
那我们就把这个loss乘上这个S和D,因为我前面这个C,然后我用这个东西对这个cs CD做一个偏导,那其实就是得到一个混合的系数,把这个混合系数乘上这个loss。
就可以把这个梯度往回传到不同的pass上面去,第三步呢就来到了这个着色器,着色器是一堆coding组成的一个着色器,所以的话我们首先要把这个coding去解析成,一个图,一个graph图啊。
这个跟我们之前讲到这个呃,coding的变成STR之类的,这些东西其实是相似的,他这个只是这个地方把一些语言变成了更啊,数学的一些符号,做这个computer graph这个计算图的一个依赖那些。
比如说像有一个Y等于DE啊,如果我们要对这个D做求导,那它的导数就是E,如果要对这个呃反过来要对E求导,那它导数是D啊,就可以,然后D又可以在另一个函数里面,可以找到他地点A加B。
然后就继续去做这个求导啊,就可以一步一步的把这个导数传进去,这个是比较啊容易理解的,但是呢呃真正在这个呃自动微分呢,在这个嗯绘制税的程序里面呢,它其实有些特别,因为税的程序呢有很多这种嗯。
非连续的函数或者非连续的OP,所以用自动微分呢就要去考虑这些非连续的OP,这里有一篇工作也是非常新的一个工作,西部数量2022的就是奥托蒂夫,怎么样做一做一个水的一个语言,做一个自动微分哦。
下面几个PPT我会去讲一下,然后除了这个自动微分呢,当然也可以手动微分啊,手动备份的,那就是需要啊,这个程序员去挨个的对每一个算子啊,去做一个拆解,去给出一个显示的一个求导表达式。
他对这个coding或者计算的要求啊,数学的要求会更高一些,但是它的效率会很高,而自动微分的话,因为它啊要用这个工具去搜索整个呃计算图,所以说所以说它通常它的那个内存消耗啊,还有一些啊计算消耗代价。
这些是比较大的,你只能在离线的状态去做这个事情,然后我们接下来啊,继续讲这个奥特蒂夫这个工作,奥特蒂夫这个工作,它是专门去做这个啊SHADER的一个自动微分,他一个因为之前嘛包括一些呃有像我们刷分啊。
D啊,还有有已经有DVG,已经有各种各样的那个啊微分器啊,自动微分器这些自动微分器呢,为什么说它还要提出一个新的自动微分器,就是说他认为这个呃,这些现有的变频器都处理不了这个C的。
或者说这个绘制过程中的一些OP的不连续性,比如说像这AD是一个啊,它最常用的,像那些PYTORCH类似的,一些反向梯度传播的发育性最强,但是它其实是对这个连续的部分,它是没有做一个很好的处理。
呃很难用于水的扣,然后DF是FD是有限差分嘛,这个是他对于连续部分比较好,但是呃它在这个它的非连续部分,它有一定的性能表现,但是非啊在连续部分,它的性能其实是呃比较低的,然后呃像这TGDVG嗯。
还有都是针对特定问题的,就TPT呢,这个自动微分器是针对这个pass racing的,然后跟SHADER的code也不太一样,所以他在这个他们定义了一个DSL,用这个语言去啊,涵盖了这个税的扣。
里面常用的一些算术表达式啊,包括一些变量,常量的变量,还有微分的目标参数啊,加法乘法操作,三角函数啊,原子操作这些东西,这个是具体来讲呢,呃它的一个核心的思想,就是他要去解决这个事的扣。
程序语言里面的不连续函数的一个积分问题啊,对于呃因为我们对于这个SHADER来说,trading来说它是一个积分问题啊,这是跟普通函数啊不太一样的地方呃,它对于不连续的它的一个核心的一个处理原则。
就是对于这种heavy side,这种不连续的跳变的地方,要去解决这咖啡赛的一个积分,那他去用了一个D啊,box微波和,用这样的一个积分去替代掉这样的积分,这两个东西,如果说我们做一个梯度啊。
这个heavy side这种突然跳变的地方,它的积分就是一个无穷大的值,因为它的这个跳变的范围是无限逼近零,然后相反他这个我们做了一个ed box滤波之后,这个地方的梯度就会变成一个有一定范围的。
一个finite,一个啊一个一段一个聚一个分段函数,所以在这个区间内,这个整个滤波就是积分就是可以进行的,包括这梯度的积分,这样子的话,他这个梯度的传播就可以,这个积分函数的梯度传播啊。
就可以往传播下去啊,上一步去进行进一步传播,嗯然后是他这个整个工作里面,他的一些定义的一些梯度的法则吧,有几样啊,像这个地方啊,G和小大G,小G和小H表示这个自动微分程序的一个入口。
然后这个party k表示吊塔A和AD,两套不同的积分的求导规则,这个第一项,第一项的话,就是刚才我们上面讲的这个分段函数啊,或者是这个跳跃函数的积分的表达呃,他的积分规则已经列在这里了,对于传统的话。
他直接就变成零,但是对于这个呃一个处理过的函数,有一个boss滤波之后,这一段它是有梯度的,然后第二个OP就是对于这个呃加法的一个求道,java求导,因为积分的加法跟那个呃。
把那个拿出来之后再做积分它是不变的,所以它的积分规则跟这个AD也是一致,然后对于乘法法则呢啊有一些不同啊,它是由于它要考虑两个连续两个函数,比如G和H都不连续的这么一个可能性,呃。
如果不考虑这个会产生一些呃累计误差,所以他这个地方就用了一个跟第一个函数接近,它就有个H啊,用一个正负,用一个呃ed box去逼近,在整个对整个做函数域所逼近,这个是对跟这个AD的一个不同啊。
它其实就是你可以认为就是这个东西的,进一步泛化,它考虑了呃,在这个H是一个分段的,或者是G是一个分段的,分别考虑了这个case,然后对最后一个地方呢呃就是函数的求导法则。
函数的求导法则其实就是跟那个AD相似的,他只是说前面有一个H的一撇,他有个这个H的一撇,他又要考虑这个H1撇,如果是不连续的case了,所以对于这个H1撇的梯度,它是用了类似于上面这种方法。
去做这个H1撇的一个梯度,用一个啊可以连续的一个方法,然后刚才我们讲的都是D的case,就是一维的一些非连续函数的一些处理方法,他对于二维的话,他的处理法则啊,就是说像一杯的时候啊。
我们做一个D的box滤波是这样的啊,2D的时候,2D的话,理想的话呢就是要做一个2b box的一个滤波,一个核函数滤波,那它在但是呢真正在啊工程化的时候呢,这个东西效率会比较低。
所以他们用了一个近似的啊,一地绿波荷,就是说在某一段选择对X轴对Y轴立波,然后某一段选择对X轴滤波,然后取它的一个最大啊最优,所以这个是一个工程上的一个实现,除了这个我们现在刚才已经讲完了。
这个C的扣的一个流水的扣的一个口味,那税得扣再往前一步呢,就是这个光栅化,光栅化同样需要做口碑,如果我们记得一些光栅化的过程的话呢,就是说我们有三角面片啊,三角面片做一个乘上它的一些变化矩阵之后。
往这个屏幕空间做一个投影啊,得到这个像素的颜色,是一个从三维和几何空间,投影到这个屏幕空间的这么一个过程,那反过来呢啊口碑的光栅化就是要解决,从这个屏幕空间我有了一个颜色。
然后去怎么映射到这个三维空间的,这么一个过程,当前呢已经有了不少啊,不同公司公司还有一些研究机构,其实都会做过各式各样的一些呃,可微光栅化工具,像soft raptsd啊,Deep r。
然后emd rust就是media的这些都有,但是他们在一个性能上,还有一些实现,细细节上都会有一些呃细微的差别,但我们这里就选一个啊,去讲一下他这个代表性的,去讲一下它这个基本的原理就可以了。
呃像这个地方就是一个呃公式化啊,对那光栅化的话是呃他要去做这个东西的话呢,就是说呃我每个三角形,我分别去投影到这个camera plan的一个投影。
然后这camera plan的投影得到一个pixel pizza,然后做一个blending啊,final做这个不同的plan之间的final gai,那逆过来的话呢,就是说我从这个东西往前走啊。
我知道了他在这个走这一步呢,他要去,因为这一步其实是根据深度去做混合的,前面这几步呢都是直接可微,从这个投影啊投影,然后变成pixel这些东西,是这前面这几步是非常容易的,直接可危啊。
但是他的比较复杂的部分呢,就是最后面做这个GAI,把不同plan的一个image混合起来的时候,他就要去做一个深度的混合,它要做一个啊,每个颜色,每个像素都是取离我这个屏幕位置最接近的,一个像素。
就是找一个啊Z值深度值最小的值去赋值,所以这个地方就是一个不连续的函数了,它因为是一个if else,所以他在这个地方要去做解决这个问题呢,他呃一种方法就是其实就是类似于有限差分。
他去嗯得到这三个平面之后呢,他去他去做这个做震荡啊,他去做这个深度的微小的一个扰动,然后这个微小的扰动呢就会得到不同的一个啊,混合的一个值啊,不同的这个像素不同平面blend出来的一个值。
然后根据这个值呢,他去啊,它就可以计算不同三角面片,关于这个深度的一个梯度,因此去得到它最终的一个,把整个啊为这个三角面片的微分啊,把它口碑化呃,它的一个所需要的一个结果。
其实就是这个smooth aggregated map,有质的aggregated map,其实就像就可以当像我们刚才做alpha blending一样,去把这个屏幕空间的像素颜色啊。
倒船回到每一个具体的三角面片上,这这个是类似一个阿尔法blending的一个嗯过程,他在优化的过程中啊,在计算这个梯度的时候就去啊,每个学画一遍,然后得到friend buffer。
Friend buffer,再做一个扰动啊,深度上的一个扰动,去把这个啊关于这个深度的一个梯度啊,给它求出来,这是它的一个核心的idea,得到了这个光栅化之后呢,再往前。
那我们就已经可以把这个所有的数据,倒推回到一个具体的三角面片上了,那我们得到三小片片呢,呃就可以就已经把这个error,从像素回归到顶点上了,然后最后一起做一个顶点上的电话,或者平移也好。
或者loss也好,这个就是啊比如说我把这个三角片片缩小放,把没把它边缩小,或者把它顶点呃挪动一下啊,如果是挪动边的话呢,就要去算这个编的loss,关于这个边边的话,之后对这个颜色的啊。
关于这个像素的error loss顶点的话呢,同理就说我这个做一个顶点啊,在各个方向上平移做它的微分,对于这个像素a loss的这个微分啊,同样也可以得到它的一个嗯啊梯度,有了这些东西之后。
就可以通过这个loss去改变这个三角面片了,那整个嗯刚才要介绍的这个pine就打通了,呃,目前呢这个关于这个又通过这个可谓,绘制流水线呢,做这个啊D资产的生成,也是近一两年才开始兴起的一个话题。
但是呢在这个像那些腾讯啊啊一些,还有一些是呃比较aggressive的一些公司上面呢,其实已经开始用起来了啊,也会取得比较好的效果,今年的话GDC2023的话,这个呃也会有一个专门的top。
如果大家有兴趣的,可以去做进一步了解。
OK那我们去讲这个最后的一个呃,今天最后一个topic是基于图像的一个简化,他这个同样也是一种D。
而且是它是一种非常极限的LD呃,大家可能听过一个技术知识,广告牌,广告牌它既不是优化几何,也不是优化纹理,它把几何纹理一起拿来优化,我最后就是用一张图片替代了几何加纹理的。
整一加SHADER的一个整的一个绘制结果,所以它是一种最简单粗暴的,最暴力的最极端的一个方法,呃常见的一些呃侧脸,像这个呃RTIBS,image ibs表达的是image base的啊。
学历啊啊简化simplification,有些BB啊,Image cat imposter,这跟我们回头再依次的分开来讲一讲,首先是这个广告牌,bb b boss可能是大家最熟悉的。
如果是对这些游戏技术有一些熟悉的话,这个东西在我们2000年初,或者更早一些时候就已经有了,它主要是用来表达一些户外场景,像一些树啊,云啊这些东西,尤其是云啊,因为BB的话它所产生的物体它只有一个面嘛。
然后这个面那你们知道如果他只有一个面的话,我的相机从这里走到这里的时候,那可能他这个面,我就可以看到它侧边的一个边缘了,那就露露馅儿了,那所以呃他这个语言呢,这个广告牌呢其实是说我们在走的时候。
他也在转,他始终面向着我们这个观察的相机啊,相机走到这儿,它就转到这儿,嗯这样的话呢就造成一个问题,就是说他其实对于一些呃有两个,一个是说如果这个物体离我们这个视线比较远。
那我们走动的时候呢啊假设是无穷远,那我们走动的时候呢,它的方向基本就不会变,所以他对一个远处的物体,它的表达会比较好,不会不容易露馅,另外呢就是说呃,他对于这种人这种比较模糊的物体呢,啊他你看一下别人。
有时候你不会知道他那个正面侧面,反面的一个区别啊,主要是一团啊比较模糊的一个表达,这种东西的话也适合用,就就算他跟着我转,那我也容易不容易露馅,所以说对这种云雾这种东西是效果比较好的,然后再进阶一些呢。
就说如果我一个面表达它容易穿帮,那就用多个面来表达两个面啊,甚至是N多个变N多个面叠加起来之后,我在走的过程中,它就会在走到不同的角度,它就会显示这个广告牌。
就会给我们显示不同角度上观测到这个image啊,以此来增加它这个细节的,这个啊随方向变化的这个细节,通常就是多边形越多,效果越好,也随着这个视角变化呢,它可以在视角之间做一个差值,呃gbt bot啊。
进阶的一个技术就是BCD他啊跟那个BB不同,它并不是用一块板去表达整个物体,它这个地方呢会去把物体做分块,每一个区域每个零部件啊,它分别都用一个BBB去表达,提供了很多很多块板。
所以这些板在结合起来之后嗯,在某些特定的视角下,它的啊跟原始的物体其实相差就很小了,而且由于这些板,它啊也提供了一定的视差的变化的一个容忍性,因为我在这一面,我看到的是他这个啊相机脚架的这个板。
然后但是我在上面看,然后这个板可能就会被其他的板遮住,所以这样子的话就提供了一定的那个,视差变化的一个哦可能性,这个是比广告牌更精度更高的一种方法,下一种需要讲的是新曲cash。
英文学cash呢就通常是用在一些大物体,像地铁地图,它对整个场景,但是它也不仅仅限于地图了,它可能包括地形上面的一些各种物体啊,背景都会去,可以用这个BSP数据catch的方法去做。
它对于它对整个空间啊,根据物体的密度,还有各种事情做一个啊,四差数或者八叉树的一个划分,然后随着我距离的变化,我在这个这是我的一个camera的观测位置,我在最距离的地方,最近的一些节点上呢。
我是用一些真实的几何去绘制这个部分的区域,然后在远地方呢会这些红块所包裹的地方,它会用一个广告牌,像这些黑色的横线来表达,用这种RGBA的图像,广告牌图像去替代掉整个模型。
最后去做这个广告牌之间的阿尔法blending,得到为远景的一个效果啊,他在真正用的时候呢,随着这个镜头的不断移动,一些红的地方会变成黄的,然后一些黄的地方会变成红的,像这种蓝色的地方呢。
就是说被我的视线直接卡掉了啊,不需要进行绘制的地方,所以他是做这么一个层次化的一个嗯,毕竟,最后是讲到一个image poster,因为ge poster是最近更新代的一种技术啊。
也是非常常用在真实的游戏,现在我们常见的一些U15啊,U14啊,这里面这种会比广告牌更常用一些,它呃不容易露馅,他会做一个球啊,半球或者球吧,这是一个常用的方法,然后我们在球的每一个顶点去下一个摄像机。
去把在每个顶点上观测到这个物体存下来,存成这么一张纹理,不同角度,那我这种纹理的话,其实你可以认为它就包含了对从360度,每一个角度去观测这个物体,所能看到的一个形状,那我们真正在画的时候。
在用的时候呢,就直接去做这个啊,这些预计算好的一些结果的一个差值,混合和读取,就替代掉它的一个真实物体了,你可以看到下面是一些呃示意图啊,像这个这边这个树是一个真实的。
然后这边是一个EPOST的一个差值混合,它在不同的角度,不同的angle上,他因为都记录了一个对应的一个视视觉信息,所以它是可以混出来,通过一些up blending啊,或者是某一些混合基数去混合出来。
但中间呢其实会有一些跳变,这个是可以看到啊,细微跳变,然后下面是两张图吧,啊像这个使用了传统的HLD,还有使用这个imposter impose,看起来这个数的话是会更圆滑一些,效果会更啊平顺一些。
另外呢还要讲一种就是texture depth map啊,Tetra desmap,就是呃我会把一些远的一些distant dream群,像一些头一一些楼全部都呃。
根据他的一些depth map去做这个几何的合并,把这些把一些离散的模型去变成一个连续的快,然后把它的一些烘培的结果,一些衰减的结果烘培到这个连续的块上面去,最后形成一个完整的一个。
最后把这个再把这些depth map做一个三角化啊,得到一个类似于IMPOSTER,但其实它不是IMPOSTER了,它你可以认为是一个简化后的几何加纹理,这么一个东西,用这个来去替代远景。
这个是啊今天讲的这个texture depth man,最后一个技术可嗯,所以今天整理时间差不多啊,因为今天我们开始直播稍微有点晚,所以中间过程讲的稍微快一点,所以刚好是三点结束啊,差不多给大家看一下。
有什么问题要提的,我们可以讨论一下,K我们soc再坐一会儿到那个三点钟,三点钟,如果是大家没有什么问题的话,那我就结束这个直播,回放是有的,上一节课也会有回放的,呃这些是这些今天讲的这些内容呢。
就是真正我们在啊游戏公司,或者是说在实际开发中,会真正用到的比较常见的一些技术,但是你可能需要去联系前面几节课,前面几节课是讲这些材质的混合啊,这些LD材质的一些资产的一些生成,然后到了这节课的话。
最终我们其实也会有一个作业,希望大家把前面学到的内容都整合起来去做,这个前面我们会有一些纹理,还有一些税的,还有一些呃几何的LD之后,我们希望都在一个统一的系统里,让大家去做这些LD的切换。
去优化整个绘制管线,这些这个就是真正在啊一些实际的应用中,最常见常用的一种技术了,实时绘制里面,好的嗯,看来就可能没有太多的问题,那我们今天就先到这里。
GAMES106-现代图形绘制流水线原理与实践 - P13:13. 流水线新进展 - GAMES-Webinar - BV1Uo4y1J7ie
嗯我们今天的课就开始吧,嗯今天是最后一节课了,就是我们要练最后一节课,所以啊我们的不会有太多一些技术的问题了,我们真正的在实践中用到的一些技术,从上节课你给LD之后基本上就讲完了。
这节课更多的是讲一些啊,可以研究下的一些新的动态,一些新的发展趋向的一些问题啊,所以是对一些新工作的一些介绍会比较多一些,这个题目是这个样子,就是一些新进展,然后呢嗯一步步来,首先是第一个是先看一下。
就是现在我们用的多的一些行业的标准啊,比较传统的就是这个实时绘制流水线,这个是基于光栅化来做一个绘制,它的主要的思想就是把这个面片啊,投影到这个屏幕空间,然后再去逐像素的去做一个上色。
然后呢另外一个比较重要的一个行业标准呢,就是或者是一个常用的一个范式,就是这个啊延迟绘制,我们会用两个pass把这个东西画出来,第一个pass先去把这个几何的GBA画出来啊。
第二个bus再从这个几何的G8分呃,几何材质的本地去做这个lighting的计算嗯,它当然也是有一些缺陷了,就是它只包含一些空间的信息,那当前的话会有一些方法去做这个补救啊。
比如说像那些light pro啊,还有一些VUE的全局光啊,当然这种东西都不是一个,质量特别高的一些方法,所以我们我们会在后面去讲到一下啊,有别于这两个非常重要的范式,一些其他的在实时绘制。
还有在这个光栅化啊,在流水线里面去做绘制的一些方法。
首先第一个是我们来讲一下,这个一个比较重要的趋向吧,就是这个虚拟纹理和虚拟几何呃。
虚拟本领的话其实是一个更早的事情,在已经好多年了好几年了,它的最初的一个inspiration,就是从这个虚拟内存来的,那虚拟内存大家知道,就是说我在真实的内存或者显存,我没有这么大的一个空间。
但我想要去做一个很大的一个文件,或者是一个算法,它需要很大的空间,那就开一个虚拟的memory的空间,然后他去映射到这个physical memory,如果hit的话,那当然就可以直接取回来。
如果不hit的话,那就去从这个地方再去找硬盘,把这个相应的配置啊换进来,这就是虚拟内存,但这个虚拟内存的想法,然后就延伸到了这个虚拟纹理,因为在我们现在3A游戏里面啊,很多大的游戏,特别是大的场景。
其实是需要一个无线一个,比如说一个场景,一个城市,那它的纹理是无穷大的,然后我们想在作为一个游戏开发者,或者一个程序开发员,想去取这些纹理的时候,如果是一个个是分散的话,那就不好取,一个是不好取。
第二个是呢啊,我说所能用到的一些纹理的大小会受到限制,所以就产生了这个虚拟纹理这个东西,虚拟纹理的话它会有一个virtual test啊,一个非常大的,其实会超过我们的那个GPU的显存。
然后再加上一个啊page table,然后我们去游戏或者引擎里面,去访问这个虚拟显存啊,虚拟纹理,虚拟门店去查这个表,如果是有个hit到的话,那就直接取出来,不信得到的话,再到这个不管是内存还是几何啊。
硬盘里面再去把这个具体的取出来,它有几个好处,一个是支持一些高清的大规模纹理,然后在动态的去调度啊,按需调度,这样子可以减少到他这个内存和带宽的消耗,而且统一去访问这个虚拟纹理的话。
就可以避免很多零散的分块的一些小纹理啊,这个有意义和拼啊利益并行,然后呢他的一些虚拟文类的几个重要的点,首先是它的一个概念啊,就是说虚拟纹理它是有一个LOD结构的,它跟虚拟内存不一样啊。
它像这个是啊分辨率小的mp,然后就是分布率高的mp,他们是相互对应的,但是它在每一块里面它都是分配置啊,像这个地方的配置跟这个地方的配置,它的size是一样的。
所以可以用一个呃page table去做一个索引,这边是一个啊适应的一个东西,像这个每一个虚拟的配置table里面,它会对应一个具体的物理配置啊,再去通过这个虚拟的配置table去索引。
然后呢不管是rookie tt,主要是这个虚拟这个配置table,它都是有个D结构的,而且不同层次之间可以互相的去做一个啊查找,所以这样的话就很方便的,在我们呃实际使用的时候去做这个LD。
而且当我们找一个低层级的呃虚拟纹理,它如果我们在这个物理纹理没有加载进来,我们可以很方便的去找它的上层LD,用一个低分辨率的纹理先去替代掉啊,这个是它的一个用的时候的一个方便的东西。
嗯但是呢虚拟门里就有一个啊小小的缺点吧,就是说他需要去做这个啊是用到的内存,做一个用到的纹理,做一个提前的预加载,因为像一个呃一个这是一个快,它这是我们实际要渲染的一个画面。
然后它每一个区域都可以是一个虚拟纹理,这个虚拟纹理它有没有存在在菲斯克,在这个GPU里面其实不知道的,那个时候呢,我们就要去先去做一个啊debug rendering,先去尝试去用一个pass。
先去尝试取一下这个纹理,如果不存在的话呢,它会自动的去到那个硬盘里面去取啊,这样子作为各种加载的事情,然后啊最后再去把它画出来,所以就多了一个啊feedback gry的一个pass。
但是这个pass呢因为带宽其实很低,所以对我们实际性能影响也很低,但我们真正使用的时候呢,啊虚拟纹理啊,刚才我们提到的还是一些软件的虚拟纹理,然后近几年的话,这个硬件的虚拟纹理。
其实都已经被厂商去集成在了我们的API里面,open gl meta都有一个对应的一些接口,去直接去做这个虚拟纹理,我们只要在里面用就行了,呃它的用法其实也差不多啊,那个实现的方法其实也差不多。
也是有一个先去找一个配置,然后再去怎么样去把那个物理的具体的一个test,再加载进来,但是具体的实现方法,在硬件层面上的实现方法啊,各个厂商都会有一个自己的差异,这些也不是完全公开的嗯。
我们最后再来看一下这虚拟纹理的话,一些这是给到的一些example啊,你看在这个视角变化啊,每一小块就表示一个虚拟纹理,所以在从由远到近,这个它的一个LD或者它实际加载的快,是不在在不断变化的。
你可以观察到在非常远的地方,它有可能是一些远啊,有一些纹理它是一个固定的块,然后我们走近了之后,这些块会被分成一个小的东西啊,这个时候不断的去变换,左边的话,这个是一个啊id啊,对对,但是意思是一样的。
右边这东西好看一点,这就是虚拟纹理的一些概念,然后虚拟纹理啊,基于虚拟纹理的一个概念呢,近景近一两年呢,最重要的一个事情就是虚拟几何,也就是U15的NO that the night。
这个事情呢啊说起来很简单,他就是说OK我们之前是能力很大,现在纹理通过虚拟纹理,我们可以把这个游戏里的纹理做的很大了,那下一步就是说我们要把几何也做的很大,也有很大的三角面片。
这样子我就不需要去加载一些很高清的,Normal map,而且我们在啊是那个艺术家做出来的ZBRUSH,画出来的一些高清的集合,我也可以直接垫一些素材,我也能直接加载到游戏里面。
那是一个非常理想的一个状况,所以就产生了这个虚拟几何啊,虚拟面片啊,串流这么一个概念,它的一个基本的idea,其实跟那个虚拟纹理会有点像,但是它实现起来会复杂很多,因为几何的合并啊,几何的存储啊。
这些东西会比纹理更复杂,嗯比如说第一个缺点缺啊,第一个区别像虚拟纹理的话,他可以用一个那个呃呃feedback pass,去确定看哪些部分要加载起来,但是呢虚拟几何啊,我要真正的做一个光栅化。
把东西都投影过去,我才知道哪些地方是要的,哪些地方是不要的啊,啊才能产生这个非如果我走这么一个pass的话,才能得到啊,我想要加载的东西,那在理论上如果说我用这个fee for的pass。
那我就先要把所有的几何都已经放到JP里面,画一遍,那整个虚拟几何的意义就失去了,所以做虚拟几何的一个啊,配置的加载是通过这个主要是通过一些遮挡,剔除剔除的一些方法,就说我前面有一个depth map。
假设我有个depth map,然后我可以通过depth map,直接去对这个物体级的,对每个物体做的bounding box做一个快速的剔除,这样子,如果这一个物体或者是这一块的一个一群mesh。
它的距离都小于这个DEP,肯定他就是被遮挡,就不需要去再做了,呃,所以在UE5里面,他们真正做的是这么一个结构,它是用了一个hierarchical的zz buffer啊,去做这个遮挡剔除。
他会把那个面片分成一簇一簇的啊,用一些视锥还有遮挡,去把一些这些粗快速的剔除,而不用去处理具体小的面片呃,他的剔除还用到了针尖的连续性啊,他做了两次,首先是他把上一帧可见的。
它是假设我们大部分移动呢都是两针,之间是有一个关联性的,所以上一针可见的面片呢,大部分大概率这一帧也是可见的,所以先把上一帧的面片在这一帧都重新画一遍,产生一个啊z buffer。
用这个DEBUFFER先去把那个整体的那个嗯,大大部分的面片都给剔除掉,然后剩下之后呢,再把剩下的一些面片啊再画出来,这样子啊作为一个补充,把一些新增加的细节补充上去,由于他相当部下之间的相关性很好。
所以他这个catching的这个catch heat的准确理解,是在实践中是非常高的啊,在另外一个重要的一个技术点呢,他们就是去啊做了一个光栅化的一个加速嗯。
由于光栅化,我们知道它是一个跟那个面片的数量相关的,一个事情啊,那假设是说啊如果有很多小面片的话,那我买个小面片都往那个friend buffer里面去,那个写一份的那个g buffer。
那他这个带宽会非常大,所以他这个地方用的一个策略,就是对一些小面片呢,他我不去具体的写他的那个G8粉,像这种传统的G8粉啊,他的call他的那个呃通道太多了,我去写他的一个id,传言狗id。
一个class id,再加上一个depth,我的那个我在他那个国啊光栅化的时候,我写出来的东西很少降低它带宽,然后我在那个真正绘制阶段呢,我再去通过这些ID去读取这个具体的GPER。
所以这个就是一个软光栅的一个做法呃,他用的是一个混合的技术,在大面片呢还是用一个传统的光栅,因为这个效率硬件做车八粉丝效率还是更高,然后对一些小面片呢用了一个软光栅啊,效率会比这个大面片啊。
会比硬光山啊更高,用这种符合的方式,然后第三个比较重要的点呢,就是说啊,他会把这个啊还是保留了一个几何的高低。
他这个几何dd啊,他还做了一个比较复杂的一个串流的机制,去把这些东西啊,非常大的一些几何场景去实时的串联进来,它会根据一些class一个错误,去做这个做这个面片的划分,然后促之间呢。
它会再去构造一个LOD的一个结构,和ARCHICALLD,那所以像右边这个地方啊,它就会形成一个数啊,像这个复节点,它包含两个子节点,但是呢就是说这个复节点像一块面片。
或者是可以认为这个复节点可能就是整个兔子,然后它的子节点呢就是它的两部分啊,这样子依次划分,最后面我们看到的是一些叶的块块,所以我们真正在画的时候呢,如果我画了附节点。
那它对应的子节点其实肯定就不需要画了嘛,因为都是表达的是同一个东西,只是说他用了不同的那个mesh数量去表达啊,所以他会去根据他的一个事情的变化,去维持一个cut。
它确保它在那个显存里面有这么一个cut,这个cut的话啊,所以在如果说我们随着属性变化啊,这个cut我们走到了最右下角的直接点,需要去画一个附节点的子节点的时候,那在加载的过程中。
它就可以用这个复节点去暂时的替代,它的子节点,这样子的话就可以保证,在任何时刻都能画出一些东西啊,正确的一个东西来,虽然细节可能不对,然后再去用一些我们之前讨论到的一些呃方法。
去做一些呃popping的几何,popping的一些blending,这里主要是类似于一些ta的策略啊,这个可能不是靠ta,是类似于ta,它就是做一些针尖的一个复用啊,Blending。
然后这个是虚拟纹理啊和虚拟几何,那下一个呃比较重要的点呢。
就是大家值得关注,就是这个从光山到光追的这个变化,首先是我们看一下这个光追和光栅的区别啊,光栅化我们讲了大概很久了。
然后剩下的是光锥,光锥呢是一个更物理的方法,它不是把这个面片往这个相机上投影,它是直接从相机上去发射光线,去对场景的面片或者是它的几何做求教,然后求交之后呢,有预知的是一个光线吧,我知道了来的方向。
那我就可以算出它它反射的方向,折射的方向就可以在场景里面做豆子棒子,然后直接找到这个,直到找到这个LISOURCE,这就是一个更物理的一个计算方法,所以啊他这个棒子的过程中呢。
收集了整个场景的多次BS的,一些功能的传输的变化啊,可以更好地描述这个全局光照呃,可以看到上面这个是传非常简单的报销号了,当然现在我们会有一些方法,去在这个光栅化的方法里面啊,流水线里面加入JI。
但是这些呢通常是需要大量的艺术家去调,而且它的物理真实性呢并不一定准确,像这种反射折射的东西,像这种镜面反射,其实就是啊在这个光栅化的方法里面,是很难去做到的,呃。
retracing呢它可以或者pass tracy呢,可以保证所有的feature都是尽可能的,逼近我们真实世界物理的一个结果,我们这个地方提一个呃,稍等我在地方提一个。
就是Python tracing或者retracing,其实他也不是所有feature都能做啊,像有一些COSTA,还有一些啊类似的效果,其实它也是需要一些更进一步的啊,特殊的一些算法才能做。
所以但是对于我们大部分游戏场景来说呢,啊,pass transon产生的一个结果会比这个lost rization,这个传统流水线产生的效果要好很多,这些是一个一些效果图吧,像这个是啊。
没有一个pass racing guy或者retracing的一个结果,然后加上retracing之后,可以看一下这个效果的变化啊,像这种光泽度都出来了,然后有一些阴影的过渡也出来了。
就整个真实感或者是写实感就会好了很多,所以呢基于这个现象啊,但是呢呃工商化为什么我们现在用,然后呢RETURING用的少一些,就是因为工商化快,但是RETURING慢啊。
所以人们就想办法去把这两个东西混合起来,有一些啊,有一些effect效果,它是用光栅化才能做的比较快,那就用光栅化做,然后有一些效果呢必须算retracing才能做啊,只能做得好。
所以就用retracing,所以就产生了这个和绘制流水线,也是现在一些相当于游戏啊,会把两个技术混到一块不同,很多人用不同的技术去做,这个是最主流的一个方法,这些直接光如果是阴影的话。
那个光栅化可以做的很好啊,软的话呢它可以做一些逼近,但是也没有这个retracing好,然后进阶啊,lighting其实可以,我觉得更多的是用这个realization。
Global information,global information呢就是用这个光线追踪等等啊,还有一些呃nb inclusion,还有一些透明的,还有一些post processing。
Reflection,这些都有不同的啊,component做一个混合,然后就看一下这个它们的实现啊,上面是一些方法啊,KONO混合很简单,但是真正去做这个管线的混合就很复杂了。
因为retracing的他整个硬件的管线呢,跟这个rust horization是完全不一样的,rotary gj啊,这个rust horization是啊光栅化,然后做那个举报9man只睡的。
然后fragment shader是这么一个PIPINE,但光是追踪呢是发射一些光线啊,就是光线去放到硬件里面去做一个整病,然后重新调节,然后去跟这个场景求交啊。
然后得到一些焦点去做一个计算trading,然后在棒子做一个迭代,这两个东西是完全不一样的,这个retracing的这个PAPI,是没办法放到现在的rust,RIZATION的这个框架里面的。
嗯具体说来就是这么一个过程,它的逻辑很简单,就是生成锐,然后去场景的便利啊,它有三个case,碰撞到或者没碰到,如果碰撞到的话呢,它会有两个case,就是说他找任意一个碰撞,或者是找最近的那个碰撞点。
然后这个做一些对应的处理,这个是它的逻辑逻辑很简单,但是它的这个计算非常复杂,它的计算是因为所有的瑞在场景里的便利,还有它的方向,这些都是啊各异的,尤其他在场景里面去做求教的时候呢。
SGI其实还是属于那个传统的嗯,s gr它是在school en space去做了一些GI的东西,但他并没有真正去做到这个全局光照嗯,然后做主要是这个ray的SCHEERING。
还有做这个merging啊,这个东西在硬件上同步是非常复杂的啊,这里要做这个硬件的,要做光线跟窗口的香蕉,还有这个BBH的那个便利,这个是很难做并行的,所以啊在实现上NVIDIA去做了大量的努力。
也是搞了十几年。
他们做这个optics,其实从10年就已经提出来了,之前有一些更早的预言,那就不清楚了,但是经过这么多年努力吧,现在终于是能用上了,在我们民用市场,但是它的实现上其实也还是非常复杂啊。
一部分是要去硬件上的支持,一部分是要去软件上的支持,包括硬件上的支持,它像内心的这个ADA架构里面的话啊,要做一个真正的这个实时的光线追踪,需要用到天使扣,用到article,还用到了一些特殊的一些呃。
像那个micro mask,micro mashes这些特殊的一些硬件啊,他也是专门为这个光追加加速的,一些定制的硬件,它具体实现了目前是还不可知,再加上软件的支持,软件的支持呢。
目前常用的一些API的话是像啊optics,然后这个van directx,他们都有一些做光追的一些接口,这些我们可以直接调用,然后厂商再根据这个东西。
不管是media art media的卡还是AMD的卡,他们都可以直接去做这个,直接的做一个软件接口和硬件的一个结合,季节我们就不需要去关心了,但总的来说这个是一个很大的发展,呃。
但是呢这个光栅化或者是光栅化,加这个光线追踪这个混合流水线呢。
目前呢还是有很多的不足,首先就是这个即使有了硬件加速,光线追踪还是太慢啊,像这个这张图去说明一个问题,就是说因为光学追踪啊,它的那个呃它是一个蒙特卡洛积分,它这个积分过程呢就要采样。
采样积分的话就一定会产生这个noise呃,像这个如果是有ESPP啊,那这个noise就非常的大啊,DSPP的时候也很大,然后这个越黑的地方,就直接光越难接触到的地方,它的noise嗯就更大。
所以这种结果是没办法直接去,放到我们游戏画面啊,或者是拿到任何的应用场景里面去的,那就去产生了很多用AI的方法去做一些后处理,或者去做一些加速的东西。
那个呃micro mesh这个东西目前我们还不知道啊,要AVIA的一些做一些细节的,他自己他其实呃,密达对他这个包括天字库它的实现哦,RT库它的实现他还是比较保密的,没有任何的一些paper的东西啊。
这是他们的一些核心的什么核心技术吧,核心竞争优势,然后的话回到我们做一些AI的,我们做一些AI的一些东西,就说降噪一个基本的技术是做降噪,就是说嗯我给到一个nose的图像,但是我有他的g buffer。
我可以做一些鲜艳,然后去把它像就像一些平滑做滤波啊,然后就多带一个光滑的东西,30SPP就是noisy的结果,然后滤波后就可以导给个光滑的东西,这个就是一个很promising的一个技术。
我这地方会去介绍一些非常啊,重要的景点的一些工作吧,大家如果有兴趣的话,去可以接着这些工作去挖掘啊,去看到一些他的一个脉络,但由于时间关系呢,我们这个地方就笼统的过一下,给大家一个印象,像这个啊。
这个是17年这个迪啊,迪士尼啊做的一个这个corner prediction,他会去用神经网络去预测,为每个像素去预测一个这个滤波和,然后啊,它这个预测神经网络的输入是我们的G8分啊。
这样子的话一个基本的筛选,就是说啊,如果这个物体是或者是一些颜色空间上,它本身就有一定的连续性,那它这个我们这个filter,可能就会去更好的去拟合这个物体的内部啊。
就比如说这个车就是跟车内部的pixel,作为一个滤波去做一个卷积啊,车外部的就不要去卷了,然后再加上呢他还输入了一个noisy的input,也是作为这个滤波后的预测啊,有了这个boss的预测之后啊。
他们在对这个noisy的import做一个卷积,就可以得到一个nose的结果,然后他在他这个run,他在这个网络里面它有两部分啊,component是分开的,它有一个它是可以跟LBL去解耦开来啊。
把一些高频的颜色给拿出来,直接重回去,不需要这个神经网络去学这个lb do,这是可以自由的拆解的这个lb do的component,所以就不需要这个神经网络去学了,然后用了大量的一些预训练的呃。
配合对像一些noise的image,加上那个高SPP的无noise的image,这两个东西学一下,做神经网络可以学的很好,这个是一个图像玉的一个降噪,然后图像与降噪。
那肯定会延伸到一个路径空间的一个降噪了,就是说因为每一个pixel它不仅是pixel嘛,它那个其实有很多棒子的这个boss的啊,就是我们的这个所谓的光线的路径啊,然后路径的话由于它这个路径空间太高了。
他比如说boss一次它就有啊,Normal,有这个test,然后有这个颜色,有position,这个维度太高,我没办法放到神经网络里面去,所以这个大概21年的时候,就会有一个工作去做这个pass。
把这个路径的信息,想办法用到了这个DEMING上面去,那就是他对这个路径做了一个embedding啊,找了一个流行这个高位的路径空间,找个流行,然后去对这个feature做一个特征学习啊。
math的学习,去让这个这个是学习后的一个特征空间啊,这个是学习之前的,学习之前的,你可以看到学术前的话,他那个同一个pixel,就是同一个像素紫色上面的一些呃一些特征,它是很零散的。
是没有一个相似性的,但是在做这个特征在这个空间学习之后呢,嗯math的学习之后,他整个特征空间就变得更SMOS,所以这个也是在路径空间降噪,然后另外的一些策略就是做采样,在采样想办法在采样上去做文章。
就是我们啊像刚才的滤波,那时候我们采样完了,有一个noise的结果了之后去做后处理,那我们也可以在想办法去让它采样的sample,去更符合一个能得到一个更好的结果,这个地方就讲到一个摩托卡拉采样。
就是蒙特卡洛采样的话,他需要我们采用的这个sample,如果越接近他的那个重要PDF,越接近他真实的那个积分,那他这个积分的noise就越低,所以就会有一些不同的方法。
用神经网络的方法去想办法去逼近这个采样呃,像这个09年的这个top是UINPUT的separate,它是用神经网络去分段啊,做一个分段的那个啊PDF函数去拟合这个函数,然后这个函数就是我们所需要的那个。
真正的这个呃,他就假设这个神经网络拟合的这个函数,可以去不断的优化,不断去逼近我们的这个光truth的PDF啊,或者国truth的啊,入射时辐射度嗯,所以它的一个核心思想就在这。
那除了用这个神经网络去逼近这个,概率函数之外呢,嗯另外的话还有一些就是说用一些啊sample,就是用一些智能的方法去做一些sample。
就是说用一个强化学习的,这个是20年的一个工作,其实用一些强化学习的方法去像,比如说大家可能会有一个比较直观的,就是这个呃那个呃阿尔法go啊,阿尔法go其实就是一个策略。
用强化学习的方法去做一个策略的选择啊,投资吧,那在这个做这个separate的时候,其实你也可以去理解成类似的一个事情,就是说我有一个很大的一个采用空间,那我往哪里踩,可以达到一个最高的效率。
那这个其实也可以去封面成一个强化学习,这是一个策略啊追溯的这个问题,那所以就会啊这个方法就是去训练一个q network,一个价值网络,还有一个策略去做一个策略的一个选择。
不断的在这个这个非常大的钱空间里面去投点,这是另一种另一个经典的一个工作,然后到了啊在下一个东西呢就是这个combiner,就说我们前面的一些录播的结果啊,就是这个DNOSE的结果。
可能它那个结果它是有偏的,然后在DSTP下他效果好,那高s app下,那可能pass racing,这个不偏的结果本身就已经更好了,已经就比这个node noise过的结果误误差更低了。
那这个时候他就没法收敛啊,在高斯pp下滤波的结果就没法收敛,制片工作就提出了一个是combining,把这个滤波前啊,把这个摩托卡罗降噪前的这个呃,按bias的结果和这个降噪滤波后的BS结果。
再用一个神经网络去把它混合起来,做一个加权混合啊,得到一个最终的output,这个方法的话在一种像这种非常光泽的玻利亚,还有一些GRISPEUA的一些东西上呢,它啊可以保证它这个整个最后结果的更好的。
收敛性质量更高,而且理论上它可以做到这个consistent,就是说你在SP无限增加之后,它最终可以收敛到这个数学上正确的一个结果,然后呢还有就是时空样本重用,施工样本重用呢。
就是说啊我们真和simple啊,它就两个地方,一个是时间重用,一个是空间重用,时间重用就是针与针之间啊,一些我们上一帧彩蛋的样本,这一帧其实也可以拿来用一下啊,来增加我们这一帧的这个呃样本的密度啊。
然后空间成就是相邻像素之间的样本,也可以去做一个借用啊,就这样子同样去为了增加我们这个样本的密度,所以他做了一个池子啊,他把这个空域和时域的sample,一个像素旁边的simple都放到一个池子里面去。
然后我们正正在去从这个池子里面去random的,重采样一部分sample,来增加这个嗯,我们得到的这个noisy input的一个质量,然后这个东西呢啊重用完之后呢。
还可以跟这个D东西呢结果做一个混合啊,作为comb最后得到一个结果,然后这个其实也是呃,AMIA也这个东西也是AMIA在推进,所以很大概率上他也会用到了他的DNS啊,或者是降噪的一些东西上面去呃。
另外的再往下就是有一些工作像真预测,就是说我们换一帧太慢,那我们其实就可以用上一帧直接去预测下一帧,下一帧我就不会了,嗯这个东西其实不仅仅用在这个retracing或者,PEETRATING的结果。
对光栅化的结果啊,也是可以同样去做一个预测的,嗯跟那个图像预的,你我们知道在视频上呢其实也有一些真预测啊,或者是侦查值这些工作,但是啊跟他们不一样,在绘制的过程中呢,我们有我们可以用很低的代价去获取。
下一帧的那个g buffer,所以我可以去基于下一帧的G8粉,去跟上一帧的绘制结果,去预测下一帧的那个绘制结果,这样的话预测出来的结果质量呢,其实会比原来的video的那个会好很多,呃。
中间呢还加上了一些呃,像那些用上了些motion vector啊,去做这个peace键的win,还有这个逆时针的两三针的一个连续重用啊,这就是真真预测嗯,OK这里有一个小小的bug。
那到时候我再修这个东西,然后最后还有一个就是这个超分辨率啊,神经超分辨率的话,就是同样的我们可以画一张小图啊,分数很低的小图,然后我们去把它做一个上采样,用的也是用神经网络的方法啊。
这个也比较容易理解了,就像上面这些是低分辨率的,然后上采样之后,他会把这些断裂的一些东西啊,怎么样啊,还有和一些细节不足的东西都给补全了,这篇的话主要是用了这个历史针啊,若干针的一个信息去做一个叠加啊。
这样子的话他以他的想法是说,可以去把这个通过历史,用食欲的一个信息去填补,他空域信息的一个缺失这么一个东西,然后这个分辨率超分辨率呢啊用的很多啊,像在这工业上的话,现在DNS还有非常FX。
他们都会用到一些插帧,和超分辨率的一些混合啊,这些是工业级的一些应用,但是呢DLS是没开源的,所以它的具体实现呢是没办法去讨论,这个是混合绘制,然后在下一个趋向呢,我觉得可以我们来讨论一下。
就是这个从一个物理驱动到一个数据驱动,的这么一个变化嗯,半影视gber bin是g buffer,你可以这样理解。
就是他对那个g buffer做了一个encoding啊,之后再去用来预测。
所以是是这个意思,然后回到我们这个问题啊,物理驱动到数据驱动,物理驱动我们知道这是一个图形管线啦,就说我们一直在说这话吧,在rendering rendering的话呢。
是从那个数字空间往这个虚拟空间里面去啊,画就是把这个数字资产画出来,那真正我们在在一个图形的完整的管线里面,我们还要考虑这个数字空间的资产怎么来啊,他有可能是从那个真实世界里面来的。
通过一些啊CP的方法感知或者重建过来,也有可能是啊人手动建出来,但是在我们真正的一些符合的应用,相当于自动驾驶啊,什么东西,他其实需要去从这个真实世界到数字空间,到虚拟空间,我们的理想的状态是。
整个pi都是去通过计算机来自动的完成,不需要人的太多的一个概念,嗯那就涉及到这个cg和CV啊,先行和感知和绘制它们中间的一个关系,就说好像我们刚才讲了很多啊,Rendering。
我们讲了整个课程的rendering,其实都是一个偏啊,物理驱动的一个计算,它和我们有个物理模型啊,我们去真实的去做这个场景中的光线啊,或者什么样的一些其他的物理的一些过程,的一些呃追溯。
然后打到物体上的时候呢,要去对这个部位的物体的表面材质,BRD建模啊,把这个过程搞出来,这是一个知识驱动的一个过程,然后呢他们做感知或者是CV作者感知呢,其实现在更多的是有一个巨数据驱动。
就是我我不知道中间这个模型怎么建啊,那我可以有很多数据集,有很多标签,我把这个数据集和标签,让一个神经网络去自动去拟合一个规则,这就是一个数据数据驱动的一些过程,呃原来这两个东西是比较泾渭分明的。
是区域区别是非常明显,每期的嗯,在最近呢,其实这两年这个东西混合的啊,这个趋势也是非常的明显,尤其是在这个一些影视表达,神经影视表达内核出来之后,我们这个两个东西有不断的有很多交融。
比如说像这个一些影视表达,那就是用一些神经网络的方法,去做一个呈现的任务啊,然后像这种defence inverse rendering,Differentiable rendering。
其实就是用一个神经网络的方法去建模,反过来,所以会产生各种各样的一个不同的pet,在这个里面呢,我们可以提到一些比较重要的事情啊,就说我觉得,可能将来我们对这个传统的图形管线呃。
可能会做一些fundamental的变化,就是说我们原来都是用一些显示的面片,几何去做这个啊物体的表达,然后把它绘出来,但是最近的话就是有一些乐福这些工作啊,同样可以画东西,它的一个原理是说嗯。
我用的神经网络去表达,这个物体或者一个场景的这个啊辐射场,然后我在任意的XYZ位置,我去输一个XYZ,还要加上一个方向的位置啊,这个几个坐标查询,丢掉这个神经网络去查询,可以返回一个这个位置的一个颜色。
就是它的密度,所以那最后有了颜色和密度,我就可以用一个volume render,一个我一个光线,我用一个volume rendering的方式去积分,这个光线上的一些呃密度和颜色。
就得到它最终的color,这个跟那个体会制啊,或者是那个医学图像显示的体会制结,原理是非常的类似的,然后它这个好处就是说,它最终优化这个呃矿场的表达,他直接用我们拍到的图片照片去做表达。
所以它就可以画出一些真实感极强的一些画面,这种如果说我们要用一个艺术家去做一个模型,重建小,然后各种调色,就画出真实感这么强的一个场景啊,做这么一个建模其实是很困难的,但是对于一些影视表达来说。
他就很容易啊,而且呢它可以直接对啊现实场景的东西,直接去把它做一个模型的转换啊,做一个演示表达的一个转换,这样的话就极大的丰富了我们这个素材库啊,现在有很多引擎,包括UE5啊。
unity其实都已经提供了,把这个NF这种影视表达,直接在这个场景里画出来的一些功能,那以后我觉得他会对这个管线,还会产生更多的一些影响,然后就是介绍另一个重要的工作吧,就是NGP嗯。
NGP的话跟刚才那个NFT呢,在他在体积分那一块其实是一样的,只是说他他在这个表达上它会有一个区别,他不是他对这个影视需求的表达呢,他不是去用整个VMP去做啊,他是搞了很多hash breed啊。
做一个小块小块的一个表达,然后在每一个位置,一个局部空间的那个breed呢,呃是做这个做这个pixel的,U15的话是可以直接画的,他没有转那个外旋转,卖水的话是SDF啊,我会后面会讲到。
因为note转卖转卖水是质量是很差的,然后的话它是用一些局部的feature做一个产值,然后再去解绑,所以它这个是更有一个局部性,得到这个影视的feature之后。
通过一个非常小的mp就可以去解码出来了,它那个颜色和密度了,这就是这个伊斯坦NGP的一个好处,由于它在更新的过程中呢,他只需要去更新一个局部的参数,所以它的速度非常快啊,再加上其他的一些工程优化。
所以基本上有了NGP之后,这个NFT的绘制和训练就可以接近事实啊,像这种,你可以看到这个像一个物品级的重建的话,那几秒钟就可以做好,这也是一个比较重要的一个进展,然后呃接下来就是说到SDF吧。
就是SDF跟那个NF会稍微有些区别,呃NF的话它是表达的是密度场,然后SDF呢他也是用神经网络,但是它神经网络表达的是一个SDF厂距离差,这个距离就是每一点每一个pixel,每个survive位置。
它可以查到,查询到从这个点到最近的一个surface的距离,那它的一个好处就是说啊,这个是一个可视化的一个呃SDF厂厂啊,等势面,它的好处就是说我可以找到这个临时面,然后去用那些match cube啊。
这种方式去把这个几何的面抽出来,这就是说我们重建完之后,我重建之后,重建的过程中,我还是用volume rendering啊,去做这个可微的体的优化,那么优化完之后,我有就有了一个SDF厂。
我可以直接把这个面抽出来,那所以就有了几何,那就可以跟现在的一些工程化啊,一些传统的管线就直接搭上去了,这就是sdf base方法的一些好处嗯,然后除这个东西呢。
一个好最重要的就是说他手工建模的做工建模,那现在可以用一些扫一些照片,然后就直接把这个卖水溅出来了,这个就是一个很fancy的一个事情,同样的也有一些方法。
这个是啊physical base inverse rendering,他是不去优化这个surface厂,他不去优化这个SDF厂,他直接优化这个面片,也是用过通过一些可微的这个啊光线追踪。
去把这个导数传回到这个具体的面片上来,去改变这个面片的位置啊,然后同时呢也能去改变它的面片的那些纹理啊,材质这些东西,这些都是可以优化的,那最后就是他像捏小球一样,一开始是有一个初始化的这个小球。
一些大量三角面片,然后他就不断优化优化优化,根据一些图像去优化,得到各种各样的一个shift,这就是另一个方向啊,自动建模或者自动场景优化的另一个方向,然后下一个我觉得很重要的是这个IPC呃。
刚才说的东西还是一些2D到3D的em,九多妹的这个视觉抖妹的一些转换,那ABC啊,横祸,他其实已经可以做到些跨模态的一些,资产生成和资产转化,像是这个准确,他说一段文字啊去描述一个东西。
我有一个兔子在一堆这个啊松饼上啊,在在一个盆子上,然后它就可以生成一些啊卖血呃,这个其实还不是mesh,它其实是一些影视的啊,场景和物体表达,当然这个东西可以最终可以转成mesh。
这个是另一个topic,所以这个东西啊AIJC,那你这个就有很大的想象空间了,那也许说将来我们建场景或者建一个东西,我们直接去做一个描述,或者给一些照片做一个参考,我就可以把整个场景建出来。
然后画出来这个游戏其实不仅是游戏,虚拟现实,还有我们大量的应用,都会产生很多的想象空间,除了模型生成呢,其实我们注意到就是还有一些其他的像啊,纹理啊,模型有了,那就是纹理,就是说有一个模型啊。
那我怎么把这个纹理贴出来,这个也是一个最新的工作,好用一些文字描述,然后这个白膜是需要给定的,然后它直接产生的是这个上面的test啊,这样的话就更适合我们的管线啊,在游戏里面的管线啊。
或者各种各样的case下,我们啊这白膜可能好搞,然后再去优化它的这个纹理,去做一个很精致的贴图,这个是很费劲的一个事情,但是我们能自动生成,这就是一个很好的,第三个呢啊我这地方漏了一个东西啊。
第三个就是灯光生成啊,灯光生成我们前面讲了模型,讲伦理,那灯光现在也是有一个最新的工作,也是可以做到它的一个自动生成,就是说这个是坐在一个室内场景的一个,更多设计呃,给了那个场景之后。
然后就用神经网络去不断的生成,除了生成灯光的亮度之外,它还生成灯光的样式啊,不同的位置它应该摆什么灯啊,摆什么样的,像这个室内场景的话,它是有一些装修的规范的,像屋顶上去摆吊灯嘛,你不能屋顶上摆开灯。
这些都是一些规范,但是神经网络可以帮我们去做掉,呃下一个进行最后一个事情了,就是AI计算啊,从图形计算转化到AI计算,这个是呃我觉得是一个挺明确的一个东西,就是我们现在传统的做一个绘制,还是用GPU啊。
还是基于GPU啊,主要的两个核心的计算单元是谁的,就是computer啊,Computer shader,然后再加上这个如果是出光追的话,就是用article啊,但是呢啊这种东西GPU嘛。
其实还是一个专用硬件,那有没有办法直接用一个更通用的,更简单的一个结构,就是NPU去直接画图啊,做一个全局公告的绘制,那最近也是有些工作把这个事情啊做出来的,就是他把流水线的逻辑去改了一下。
穿透流水线呢它是基于面片化啊,像光追光栅化是基于面片去画,然后基于光追的话呢,它是以光线为单位去化,所以这些是比较物理的一些呃,力度比较细的一些啊,绘画单元,然后这个工作的是把这个流水线变成了。
按物体去画啊,先去画一个背景,然后我在上面就画一个物体,然后再把这个物体对这个场景的那个影响啊,预测出来啊,不断的叠加,所以就像一个流水线一样啊,每一个每一个层次,每一步骤去增加一个物体。
那最终去把整个场景画出来这么一个框架,那它的核心的一个难点,就是说要去找一个啊方法,神经化的方法去把这个物体对这个场景的,他这个光能传输变化啊,这么一个抽象的一个函数区,把它存下来啊。
这就是这个物体做到一个事情,嗯那基本上啊今天就刚好50分钟,那我们基本上就讲完了几个事情,然后可能稍微总结一下,就是今天讲了几个事情,一个是这个从虚拟的纹理啊,到虚拟几何,做这个一些虚拟化的事情啊。
做这些大高精度的材质的一些载入的事情,然后另外就是讲了这个呃,光栅化到这个光线追踪这么一个趋势,然后最后面就是这个啊,有一些物理驱动,到更多的AI驱动这么一个事情啊。
然后这也是我们这个106的最后一节课,然后谢谢大家一直以来的一些观赏。
接下来是一些可能是一些讨论的时间,好不对,看看大家有什么问题啊,今天的话题其实比较发散啊,没有讲的很细,都是一些嗯方向性的一些讨论,嗯那现在人比较多嘛,然后再顺便提一下那个助教那边。
好像是说那个到了几何优化这一部分,大家上传的作业就相对数量就变少了很多啊,是不是会有一些难度啊,或者是需要一些解答,这个东西可以拿出来我们探讨一下,然后的话如果说在某些作业不感兴趣,或者是不想做的话。
其实也可以去跳掉啊,去做后面比较感兴趣的一些作业啊,这些都是OK的,看一下OK,没有其他问题的话,那我们在等个几分钟,那我们就结束掉今天的直播,好的嗯,OK那今天的话我们就先到这里吧。
GAMES106-现代图形绘制流水线原理与实践 - P2:2. 图形绘制流水线的基本原理与实践(一) - GAMES-Webinar - BV1Uo4y1J7ie
好,那么时间也差不多了,这边就开始正式上课吧。
首先,我也非常感谢能够有机会来参与上Games106现代图形绘制流水原理与实践这个课程。我先自我介绍一下,我毕业之后一直在,声音太小了,我看看,我的麦应该已经放到最大了,那我讲的大声一点。
我毕业之后一直在,在相亲科技这边工作,我跟上一节课的霍老师不太一样,我一直在工业界里面待着,所以我讲的东西会更注重实践一点。OK,然后下面我们就正式开始了,然后绘制流水线原理的第一课时。我来大概讲一下。
我这个第一课时主要应该是从Woken这个API,大家想要通过我学习Woken,然后来可以实践上手来学习。这个课一开始来上的时候,跟我说有个目标就是说,我们之前上了很多课,有点高见龄。
想要更从实际的角度来让大家学习到如何学习绘制,如何实际的从RTI接口来学习。然后,这样子的话,我也大概以这个目标吧,设计了一下我的课程安排。然后,首先我来讲一下,大概是谁比较适合这门课。
就是一个是刚刚开始接触学习RTI的初学者,比如说你之前可能已经有过了解了OpenGL或者第三第11或者别的什么之类的,你对于这整个绘制的东西,你还想要更深入的了解这个是对的。然后。
假如说你对于Woken这个东西其实已经比较熟悉了,对绘制管线什么之类的都非常熟悉了,这门课可能,我觉得可能意义就没有特别大了。然后,第二个的话,我们是有一个前置的要求,就是你必须首先要学会C和C++。
然后,这门课在我上的时候,你可以收获的东西,一个是Woken API的一个基本使用,第二个是对于Woken的绘制管线的一个相对来说全面一点的一个理解。最后。
是因为我一直在从事的都是移动端上的一些应用开发,然后会讲一些针对,可能会提起一些针对移动端的优化的实践。然后,我为什么区分移动端跟那个桌面端这个事情呢?
是因为RHI接口其实是一个面向硬件开发的一个一种接口吧,也就是说你的同样的代码,你在不同的平台,他们的硬件给你设置的那个bar是什么样子,其实不一样的,这个就类似于那个木桶理论,你的木桶。
影响你速度最高的木桶是那个最短的那块板,然后PC端上的一些就移动端上可能是一些问题,在PC上可能它就不是问题了,比如说移动端它的我相信GPU的那个计算性能其实还是可以的。
现在但是移动端上的它的瓶颈是在于带宽上面,所以说我们在PC上可能会很容易的用一些算法,可能跑得非常好,但跑到移动端上就发现就特别的卡。原因也是出于这种东西,所以说在,OK。
然后所以说这个东西可能要区分不同的硬件平台,哪怕同样是移动端你安卓ios,或者你哪怕是安卓,你是高通的卡,还是说是华为的那个麒麟或者联发科的,其实对他们都要做有可能有不一样的那个针对性的优化。
那么你这个才是一个高性能的一个东西了。OK,然后下面我再讲一下我们的课程安排,然后我的课程安排呢,总共是有三节课,那个。第一个呢,是我可能基础架构,然后它的汇集流程,然后基本上大概这门课的安排。
就是说我大概会讲一下你这个我可能会去怎么去架构它,然后怎么去处理化一个我可能的。我可能一个application,然后第二个课呢,是我们我肯定我们最关键的一部分,你要去绘制它。
然后怎么去创建我可能会是对象,然后内存管理以及最重要的事情是它出问题了,你要怎么去调试,你要还有一些工具的使用,我们离不开这个我可能工具,然后最后一节课会有一个我肯就是我们所说的现代图。
新管线的一个很重要的一个事情就是多新的同步,然后还会一些基于移动端的一些场景的优化,还有实践,然后最后是看一下那个作业还有反馈。OK,然后这个是我们的这个课程的一个目录。然后第一个我可能的简介吧。
我可能他跟OpenGL比较类似,毕竟一开始在提出我可能这个东西的时候,他的名字不叫我可能是叫既有next,就是下一代的OpenGL,然后所以说他也跟OpenGL一样。其实是有一个官方的标准。
我们定好了一个统一的接口,统一的标准,那么具体的实现OK,你们每个硬件厂商自己去实现,并没有一个组织去审核你的这个硬件实现的能力。
然后这个区分于苹果的Metal或者D3D1112不一样的地方就在这个地方,你在D3D1112当你的文档标准里面说他支持这个功能,或者说当你query出你的硬件,你的设备,你的系统能够支持这个功能的时候。
那么他一定是有的。但是在我可能的或者OpenGL我可能的那一套里面其实不一定的,这个事情是要打个问号的,就是说他们可能很深层了我支持了我可能现在是1。3吧,可能他们可能深层了自己深层了我可能1。1,1。
2的核心功能,但是他并不是真的所有的核心功能他都实现了。你只能去经过一些测试,他才可能某些功能是OK的,某些功能其实是不OK的,然后还有一个是我可能相对于那个他做一个开源的那个。
他做一个开源的那个框架的话,主要还有一个特征就是扩展,每个平台会有一个自己的扩展,比如说在rejection刚出来的时候,就在N卡上,我可能N卡有一个rejection的扩展。
然后这个扩展在AMD上面你是用不了的,你在手机端上也是用不了的,因为他是针对于那个N卡的扩展,然后在后续的你的扩展可能就会被吸收掉。可能就会被吸收到了那个那个我可能核心的功能里面。
所以我们在看到我可能很多函数的时候会经常看到一些后缀,两个函数几乎长得一样的,就是VKXXXXX,然后NV或者VKXXXXX华为OK,这个是华为做的扩展,然后VKXXXXAMDOK,这是AMD的扩展。
然后VKXXXANDROID是安卓上的一个扩展,所以说我们我可能在开发的时候,这个是需要注意一下。你想用他的某些功能,那个功能其实跟你的那个硬件设备或者说你的跟你的显示平台是非常强相关的一个事情。OK。
然后后面我们要看一下,首先我们去Wacom的话,学习的话,首先我们要去下载。OK,首先我们去Wacom的这个平台上,我们去下载他的SDK,这个SDK的话,我们可以看到Windows,Linux跟Mac。
Windows跟Linux都是官方出的一些版本,但是Mac并不是苹果出的,是那个V色做的一个。
是V色做的一个功能。OK,弹幕有人说是KHR是什么后缀?这个KHR其实就是那个可以制造Wacom的那个官方组织做的一个扩展的一个后缀。你可以把它理解成一个类似于官方的扩展功能吧。对下面有弹幕有说。
就是对,这是就是这个组织做的一个。你可以把它理解成一个官方的后缀,OK,我们接下来聊一下,然后我们安卓的那个那个功能的话,Wacom是那个是是谷歌提供的,然后跟你的NTK,随着你的NTK。
然后附带的数据,然后最后的实现了,但是根据你不同手机硬件来实现。
这个这个事情会有一个,那个大家可能会在桌面端开发,我可能时候会体验,可能会更好一点,为什么呢?因为我可能桌面上只有三家那个驱动厂商,NVIDIA,AMD,INTEL,只有这三家,然后这三家呢。
我的经验是大家尽量如果后面的作业啊,大家尽量如果手头上有独立的显卡,那么尽量用独显来实现来去完成这个作业啊,不要用那个。INTEL的核显。如果大家的作业有兴趣的话,也可以在移动端上跑安卓或者ios。
然后安卓的话,我的经验是。呃,会有录屏的。对后面会上传的,然后安卓的。有人问那个老师有录屏吗?这个会有会有录屏的。然后那个安卓的那个。那个驱动化就是每个手机厂商会有的不同的的实现。
你华为手机就是华为实现的,OPPO手机就是OPPO,但是有些像小米可能也是小米实现的,然后高通的手机小米OPPO他们使用的高通手机,高通会有一个提供那个一个大概的一个驱动,然后华为跟高通就不一样。
然后我的实践就是可能高通的手机,它的兼容性,鲁巴性会更强一点。华为或联发科他们的这种支持可能就会弱一点,就是会出现我说的那种核心是支持了,但是其实他手机并不支持,比如说,呃,有一个叫msa的功能。
你在我们开了msa,你会发现很多比较低端的什么高通六系四系手机可能就跑不起来了,直接就崩溃了,但是你把这个msa一关OK了,那么他只能说我只能是理解成他的驱动对这个知识是有有有一点问题的。OK。
然后我们下面是讲一下对比那个传统的HI跟现在的HI,因为我可能data v12跟mantle我们可以按照说是现代的HI,然后传统的HI呢,其实他们会有一个叫做上下文的一个概念,他们是一个状态机。
这个context可能有像openGL会有个影视的context,它说你的现成是保定的,然后第三第三地呢会有一个显示的context有个叫什么第三地divorcecontext的这么一个对象。
那个最对象里面呢,他也是一个状态机,当你改了一个什么东西呢,他就会有个状态会记录在这个里面,这个状态机的这个东西有什么不好呢?我可以举一个例子,比如说我们开发了一个SDK开发的功能。
用openGL开发的功能,OK,我们自己去管理了一些设备,管理一些图片的纹理,我们自己去建了,然后假如他刚好有个图片纹理,他的就是字母12,因为openGL他那个图片对象,他就是一个整形嘛。
我们就是12,然后呢,外面在对我们这个SDK的人呢,他们就外面会也会有的,也会有他们的对应的图片资源,那么假如运气非常不好,他的代码写的有问题,他们很久以前就是有一个纹理ID叫12的这么一个纹理。
已经释放掉了,但是他并没有把这个12这个给置为0,然后等到后面呢,他再想去回收资源的时候发现,哎,我有个纹理12,好,我想去把它给释放掉,然后他以为释放的是外面的对象。
但是真正释放的是我们提供的一个SDK内部的一个纹理,然后就会发生一些不可控的或者无法预期的问题了,可能程序会崩溃,或者是黑屏,或者怎么样,然后对接我们的SDK的人就会觉得,哎,你这个SDK有问题。
你看我用了你的SDK,你看我的这个程序就崩溃了,那么好吧,这个问题你要去解决了,然后这种东西我们我们也知道这个很难查,因为这个逻辑并不是我们能够控制的,是外部的错误调用来导致的。
然后这个就是传统RHIOpenGL会带来的一方面的这种问题,它的状态机管理,你外部如果修改了,我内部其实是无法感知的,然后第二个事情是驱动会帮你干很多事情,传统RHI的驱动,它会帮你做同步。
比如说你想OpenGL你想从那个文理里面去把贴图读出来,那么这个时候呢就会有个隐私的同步,它会让把那个所有跟输出到这张贴图的渲染相关的所有的命令都结束了之后,OK。
那我再去从这样贴图里面把这个东西给读出来,然后这些事情都是驱动帮你干了,你在写的时候你会感觉好像这个那个那个函数我没记错叫GeoRedPixel,这个函数会特别的慢,但这个慢并不是它卡住了。
或者说这个函数很慢,只是因为CPU在等GPU在干活,就是CPU在喊,GPU你快点干啊,我就等着要你的结果呢,然后GPU说好好好,你慢慢等,你慢慢稍微再等一等,我马上就好了,OK,然后等到GPU干完活了。
然后CPUOK,再把这个你干出来的结果给拿出来,这种东西呢,好处呢,就是说当然你在写代码的人,开发这个功能当然是非常开心的,因为保证的结果是正确的嘛,但另一个方向你想要写的非常高效,你就没有任何手段了。
然后所以说呢,所有OpenGL的驱动呢,一般来说就会比较重,他们或者第三第11的驱动就会很重,因为他要保证你的所有的功能都是完善的,所有的东西都是OK的,然后所以他帮你做好多事情,包括同步啊,竞争啊。
比如说你这个东西你谁先用,然后又有谁先读之类的,假如你还有了写的多线程,那我们OpenGL也是可以写多线程的啦,之类的事情,他驱动都要帮你把这些事情都解决掉。
那么当一家驱动的公司的软件开发能力比较弱的时候,比如说个别的显卡,他们的表现可能就不太美妙了,然后当一家公司的软件开发能力驱动写的非常好,然后他们的表现就会非常棒,你会觉得。
然后这个时候对于用户的感觉就好像,嗯,你这个设备就不太行,这个设备就可以,我不同的同一个游戏,我在这个显卡上,明明你的算力更强,但是好像我跑起来会更慢,大概就是这种原因,然后我们的现在的RHA呢。
这个东西就是说驱动层会做的非常的薄,驱动只做最核心最核心的事情,然后呢,就会依赖于你的开发人员,需要你对这个你的程序是非常清楚的,你想要干什么,你这个你这样图,你比如说你画了一张图出来。
张图后面你是否会需要再被复用,你这样图后面会是怎么样,然后你这样图是不是画好了之后,直接画到屏幕上去,你都是得清楚这个事情,包括我们的那个更新机制,然后然后再包括你这个东西是否要多线程同步,还什么的。
这些都是需要那个开发人员是知道就知道了非常清楚,你的各种依赖关系,也都是需要开发资源的依赖关系,然后包括你的那个资源分配,嗯,OK,这个讲的资源分配,的话,这也是我可能和他12需要一个做的事情。
你在写第三第11或者欧明杰的时候,可能是永远没有思考过,当我去开,就是开辟一块纹理的时候,我还要去思考这个纹理的内存是怎么管的,一般来说,我就直接开起来,是你快空间,就像牛的一块空间一样,OK。
这样就可以了,但我可能他不行,我可能一般操作是你首先要申请一个足够大的一块内存,然后呢,你自己去分配,这块内存的哪块区域给这张图,这块区域给另外一张图,OK,第三张区域给第三张图,OK,所以说。
我可能的话,你需要去这种内存分配的事情,你要自己去考虑,自己去管理,当然这种东西是会有一些比较友好的第三方库,然后他对于多线程也是友好的,因为他的接口的话就是是基于command buffer。
就是一条所谓的command buffer,其实就是把所有的GPU的命令,我扔到了一个对列里面去,这个对列,你在往里面塞的时候,那个GPU其实是并不知道的,然后等到我对列我都已经定义完了之后。
我再去扔给GPU,他是通过这个方式,然后可以实现多线程友好,然后我们再回到回头看传统的IHI的时候,因为有一个上下文的概念,有个状态机,那个时候我如果要实现那个多线程的话,就会很麻烦。
比如说OpenGL,我可能有三个线程,每个线程我都会需要绑定一个context,然后每个context之间他们资源的那个需要做共享,然后还需要,嗯。
最后想然后每个context我还要去管理好他们的生命周期,每个context他们有一个自己的状态机维护,这个就会非常的不友好,然后这两个区别的话是现在IHI驱动,其实等于说不帮你,不会帮你干任何事情。
然后是你要自己去理解你的程序是要干什么,你才能够写出更好的东西,然后第二个是你如果用现在IHI你可以很容易的就写出高性能的,不是很容易写的高性能程序,是你有能给他提供了一个你可以写出高性能程序的能力。
但是你要真的写出的高性能能力,你是需要一个非常强的一个熟练的情况,你对你需要对你的硬件,包括你的程序运行的逻辑都非常了解,你才能够写出一个非常高性能的一个东西,所以说熟练的程序。
你就可以体验那种掌握雷电的感觉,你可以充分调配你的那个GPU的硬件的资源,然后还有一个比较大实现的就是说当你的程序卡在了CPU调用上面,而不是GPU上面的话。
那么你用现在的IHI的这个情况就可以非常好的提升,我刚才提到的一个Component Buffer,Woken的Component Buffer是能够复用的,这个什么意思呢?
就是说有的时候我们每一帧画的东西是一模一样的,你OpenGL的话,每一帧你都会去调用那些绘制的这些东西,每一帧都要重新重复去调用一遍,那个OpenGL的命令,然后Woken的话。
你只要建好那条Component Buffer,你如果确定你后面的所有的调都一样的,那你就把那条Component Buffer你复用就可以了,你每次都是把那条Component Buffer给扔进去。
然后我这边的话,推荐的学习的曲线的话,我是不太推荐直接上来,就是你什么都没有学习过的话,就用Woken来学习,或者DXD12,我是不太推荐的,因为他们的概念太多了,你需要知道的事情就很多。
特别是Woken的初始化,这个你去理解一下,我觉得就挺费时间的,不像OpenGL,你只需要有一个库,比如说GWFW,或者什么之类的,你的上下文就直接建了。这个我提一下。
OpenGL的上下文其实也是每个平台是有一个自己的实现,你Windows上有个Windows的实现是WGL,你手机上的是EGL,然后苹果上就是EAGL,然后什么Linux是。
Linux我有点不记得那个叫什么了。所以说我觉得上手你们可能最好的选择是OpenGL,因为它真的非常简单,而且上手特别容易快,特别快,你可以很容易的去写出你想要做的各种效果,这个只是学习的状态。
当然你想要更深入的时候,你想要做一些效果更好的事情,OpenGL其实就显得有点薄弱了。然后第二个是我想要推荐的是D3D11,我是蛮推荐大家学习的时候是从D3D11的来入口。
因为D3D11虽然说也是有个状态机的概念,但是它接口基本上都是基于OOP的,所以说你对于它的汇职管线的理解起来会更容易,你直接看它接口就知道了,OK,我的汇职管线是长这样子的,不像OpenGL。
OpenGL每个都是状态机。曾经我在初学OpenGL的时候,我的老师就有这样子一句我觉得非常有趣的话,就是说当你学习OpenGL的时候,你去看OpenGL的教程,它实现这个功能。
可能会按照OpenGL的函数一行一行的写下来,在你不太理解的时候,你就一行一行的抄下来,不要觉得某一行是没有用的,然后把那一行把它关掉,都是有用的。这个我举个例子,最近我有个同事在做的一个工作呢。
就是Wacom转换成OpenGL,就Wacom的一个图片转换成OpenGL在手机上,它的路径就是Wacom转换成一个叫Adobe Buffer,一个安卓的一个对象。
然后再把Adobe Buffer转换绑定到一个OpenGL的对象,这个听起来挺容易的,对吧,但是呢,它就是一直在手机上一直都画不上去,它也很奇怪。而且画不上去呢,也不是所有手机都画不上去。
是那个小米手机是OK的,能画上去,但是华为的就不行,然后就去查,查了后来之后呢,看了之后呢,结果呢,是它OpenGL在创建纹理的时候,就省掉了那个教程中的一些设置采样期的这一步,它就没有设置了。
然后它觉得这个好像你设置或不设置总有个默认值吧,它会觉得,但其实不是的。这个完全是看驱动的,华为的驱动就没给你设置默认值了,那么就画不上去了,但是那个小米的高通芯片就帮你做了这个事情,就可以画上去了。
这个就是我说的,可能这个真正的实现很依赖于你的硬件设备,所以说当你要写Working的时候,尽量按照标准的方式去实现,按照标准的流程去实现,不要去省略一些东西,然后当你想要某种扩展的时候。
你从硬件里面query出来说,Working支持这个功能扩展,其实是不一定的,这个是要打个问号的,你需要一些别的手段来去验证,它是否支持,这个问题在OpenGL中存在,Working中也是存在的。OK。
然后下面我来讲一下这个课程的主要的内容,第一个是处理化部分,然后第二个是渲染的组成环,然后最后渲染完了之后,我们要推出一个推出程序,这个是我们可以看到我们的作业,我们作业homework1。
homework0的我们汇集的一个三角形。
OK,三角形,然后它里面其实也是,这个就是我们的一个初始化,这个是我们的初始化,设置窗口,然后创建资源的对象,这是我们的初始化的一个阶段,然后这个就是我们的组成环的阶段,OK。
然后我们对应的代码部分是这样子的。
然后后面我们初始化的话,首先就是要做三个东西的初始化,一个是创建窗口,创建窗口的话,我们一般来说会用那个GoFW或者SDO之类的一种第三方库来做吧。
我相信应该没有人会愿意用Win32的Native API或者Linux的那个什么叫VLAN的或者是X11去来创建窗口,那个太复杂了,然后OK,第二个是我们要初始化Vulkan。
初始化Vulkan其实是等于说你程序,我要跟,首先我要去载入Vulkan的这个驱动这个库,然后第二个是我Vulkan,我要跟我的显示设备来取得联系,告诉我这些命令得让哪个硬件设备来帮我渲染。
然后最后我渲染完了,计算好了,最后的结果我要呈现到屏幕上去,这个就是我们的第三步,要创建一个叫SwapChain的一个对象,这个SwapChain就可以让Vulkan跟显示的窗口取得联系。
让我最后的东西汇集在窗口上,这个是初始化的部分,OK,然后后面呢再是渲染的组成环,我们要渲染呢,我们可以把我们的渲染想象成一个函数,函数的输入是什么呢,几何信息,还有一些Uniform。
Uniform其实就是CPU端传给GPU的那个参数,我们的想要的,我们程序想要输出呢,就是屏幕中显示的画面,然后我们的这个逻辑呢,就是着色器,着色器就是之前那个霍尔之,霍老师应该有简单的提过,OK。
然后我们的组成环呢,大概就会有三个阶段,第一阶段是更新CPU端的业务逻辑,一般假如说大部分游戏,可能就是游戏的业务逻辑,我们这边可能就非常简单,可能只是一些UI逻辑,或者一些鼠标。
比如说控制相机之类的这种逻辑,第二个阶段就是你要把你的CPU更新好的数据拷贝到GPU中,OK,然后第三个阶段就是你要生成的你的Command Buffer,把你的渲染任务扔给GPU。
然后让GPU在屏幕中显示出来,OK,然后后面最后当你这个组成完结束了,你现在要退出程序,OK,然后把所有的资源释放,然后关闭你的程序窗口,这样子就结束了我们的整个流水线,就像这个。
这个我就跟我们可以对照着我们的Homework1的Hello全口来对比一下这个流程,OK,然后首先是初始化我们的Worker,第一个我们要用,调用API来实现窗口。
这个地方其实跟OpenGL不太一样的地方就是你的窗口类型Worker是需要知道的,比如说你的窗口是Win32建的,还是XCB建的,还是VLAN建的,还是Mac上的那个什么东西建的。
因为Worker后续在创建Swapchain的时候是需要这个信息,然后来选择对应的扩展,才能建出我们想要的Swapchain,然后比较好消息的就是SDO,GFW这些窗口的窗口可以很方便的获得这些信息。
包括窗口的类型,然后包括怎么去建Swapchain之类的,它都可以帮你去实现好这些功能,可以帮助你去初始化Worker,这个就像GFW里面去初始化OpenGL的上下文一样。
其实你真的去看一下它的OpenGL上下文创建的话,其实还是挺复杂的。
然后我们再来看一下我们的Worker的一个整体的一个概念,我们的Worker的话,我们先看我们这个右边这张图,有一个应用程序,建了两个Worker实例,第一个Worker实例建了两个物理设备。
然后第一个物理设备有一个叫逻辑设备,然后第二个物理设备建了两个逻辑设备,然后每个逻辑设备有一些对列,然后我们的汇字命令就CMD的buffer是扔到了对列里面去了。
然后这个是一个相对来说非常复杂的一个Worker的使用情况,很多时候并没有那么复杂,很多时候我们只有一个实例,一个物理设备,一个逻辑设备,然后对列可能也只有一个或者是多个。
但通常情况下其实一个大部分情况就是够弄了,但是呢我们要回头想一下,我们Worker它在设计的时候为什么要设计的这么复杂,其实你想一下也比较容易理解,就是说当你有四块4090,你想玩游戏的时候。
就是我们以前说的四路泰坦,现在就是四路4090,你怎么去操控,让这块显卡干这部分的活,这块显卡干这个事情,让另外一个显卡干另外一个事情,让每个显卡都能够尽可能的去充分利用这个事情,你怎么做到呢?
OpenGL告诉你,很困难,但是Worker呢,你可以对应的物理设备,当你有四块4090的时候,其实你一个实例能够query出四个物理设备,然后你这个实例可以去选择这个物理设备,比如第一块显卡。
第二块显卡,第三块显卡,第一块显卡,只用来做物理模拟,我拿它物理模拟结果扔给了第二块物理设备,第二块物理设备纯粹做渲染的某一部分,然后第三块再去做某一些事情,然后最后的一张显卡,那张显卡专门只做渲染。
把那个渲染出来的结果给画上去,那么这个时候你所有的显卡都会丢了。然后有人问为什么一个物理设备会出现多个逻辑的Working Device,这个一个逻辑设备你可以想象成其实是对于一个对立的实例化。
或者说我们真正的API其实传输给的是逻辑设备,并不是物理设备,所以说你的逻辑设备的时候它会去,后面我会提到它那个对立其实是有不同类型的,我这个逻辑设备的对立只用来计算图形管线。
那么OK我所有的图形管线命令我得扔给它,我后面的对立假如我只是用来计算computer shader,就是那个什么计算注册器,就类似于CUDA之类的这样子的东西,那么我所有的命令可以扔给它。
但通常情况下我们并不需要去做这种事情,因为我们只需要有一个这样的多个就可以了,也有人问有多个Working实例的用处是啥呢?这个目前我也不是很清楚,因为我也没有遇到过我需要多个实例的情况。
大部分人来说游戏场景或者人类场景OK我一个实例就足够了,不需要那么多的实例了,所以说我们只需要考虑这种情况就可以了,但是Worker呢我们要明确知道Worker它是设计的,可以让你来做这种事情。
然后有个说那个一个逻辑设备有一个逻辑设备不是可以有多个对立吗?对这是可以有多个对立的,但是每个对立它会有它的功能的限制,不是所有的功能都可以在所有对立里面去运行,对立跟对立其实不一样的。
这个我下面的PPT会讲,然后我们再来初始化Worker,然后初始化Worker呢这个会有一个比较重要的概念叫layer,就是层,我们一个application。
然后我们要去载入Worker的库会有个load的过程,然后这个load的过程不是直接载到那个驱动上,而是要一层layer一层layer,然后载下来,然后最后再到驱动层。
然后所以说我们见到Worker instance这个东西其实是初始化Worker库跟我们的程序之间沟通的一个桥梁,然后我们需要指定的一个通常我们需要指定的是swipe chain的一个类型,这是个扩展。
有些时候其实你的Worker是一个离线渲染的功能,其实你就不需要这个功能了,然后第二个比如说你有些扩展你想要去做调试,你想要给每个Worker起一个大家能懂的一个名字,比如说它叫纹理一。
这个纹理叫颜色纹理,另外一种叫什么法线贴图,当我想要给它们设置这种功能的时候我们就需要开启一个可调试的一层,然后最后是比如说我检查我的Worker调用错误了,在OpenGL下面就非常麻烦。
只会通过一个GL error去猜你可能调了什么错误,Worker它会有一个错误调用的回调,然后这个回调函数也不是Worker的核心驱动中的一部分,它也是一个外置的一个检查的层。
然后这个地方比如说我们可以看到Worker将很多功能拆到了不同的层里面了,然后我这边下面举了两个例子,一个是用于检查错误的一层,它的名字叫这个,然后第二个是renderDocker。
它是一个Worker的一个GPU的调试工具,它不光能调试Worker了,它的第三DOpenGL它都是可以调试的,然后这个是它自己做的一个层的名字,那么换句话说,其实我们每个程序你如果可以的话。
你都可以自定义一个自己的一层layer,然后把那个layer让你的Worker程序跑起来,然后这边是我们的相关的API跟作业代码。
后面大家如果课后大家有兴趣的话可以去关注一下Worker的这些API跟它的命令,然后我们刚才说了层,那么这个层到底是干什么的呢,我们的想要初始化Worker有一个函数。
比如我们这边要读那个function ABCDE,那么layerA它是重写了ABC三个函数,layerB它是重写了CDE三个函数,然后最后我们的接口肯定也是有ABCDE这五个函数。
那它的意思是当我loader我去调用了worker function A的时候,我还是取C吧,它调用了worker function C的时候,它会首先调用layerA的function C。
然后layerA的function C它的程序结束之后呢,它会去调用layerB的function C,然后再去调用最后真正的接口的那个function C。
换句话说其实我们可以把这个layer当做函数重载来理解,它其实是提供了一个函数重载的一个方式,或者叫hook,对,OpenGL的hook会很麻烦,对吧,然后WorkerOK,你们都喜欢hook,好。
我提供一个官方的方式让你们去hook,这样子你们所有调试工具,你们就可以很自由自在的去hook了,你们调用的所有的Worker命令都可以记录下来,你们自己想做任何检查都可以了。
以前在OpenGL调试的时候,经常喜欢干的事情把你所有的OpenGL的命令把它打印出来,包括时间,线程什么之类的,这个时候如果你有兴趣,你就可以自己建一个自己的layer。
然后把所有的OpenGL的命令把它hook做,根据线程,根据时间戳,然后把所有的Worker的调用给打印出来,当然官方是有提供这样的一个方法,这个就是Worker的一个layer的一个概念,OK。
然后下面我们Worker的instance,我们已经跟Worker的驱动区的联系了,后面我们就要跟显卡,我们真正的硬件设备区的联系,这个时候Worker需要有一个去query出我们硬件设备的一个功能。
然后Worker可以去query出我们硬件设备的名字,你这是什么显卡,什么显卡,然后你的能力是能干嘛,能干嘛,然后比如说我的电脑是两块显卡,一块是AMD的显卡,一块是英伟达的显卡,OK。
那么我现在直接运行一下。
好,这个时候我们query出了我们的第一个显卡,这个就是我们第一个显卡的名字,一张3070的显卡,然后这个limits这个对象就是它的所有的限制,它有多少张,它图片的分辨率最多是多少。
或者是所有的vertex的输出是多少,这个每个的限制一般来说会不太一样,然后官方会有一个就是会要求你的驱动支持的最低,就是最低的限制是多少,不过这个最低限制呢,话说回来也是看驱动实现的。
不是每个驱动都老老实实的按照这个最低限制来做的,有些它声称它实现了,其实并没有,然后我们再去query出我们的第二张显卡,好,我们query出了我们第二张显卡,第二张显卡是AMD的一张核心显卡。
如果你仔细去对比的这个限制的话,你会应该可以看到它这个限制是会比那张独立显卡会小很多,也就是说它的能力会更弱,然后我们在程序提供的时候。
我们要根据我们的能力然后选择合适的显卡,当然我们在做游戏的时候通常会喜欢去调用那张独立显卡,但也并不是所有的都是想要调用独立显卡的,比如说我的程序比较轻量级,为了节约能耗我可能就无所谓。
我可以选择一个我的核心显卡也是可以的,然后刚才有一个同学就问,有去问那个这个对立,明明我一个我一个实际化也能用很多对立,那么为什么我还需要有这种有不同的那个逻辑的,是因为对立的那个它会有一个处。
它那个或者叫做quick family,这个处呢,它会它的能力是不一样的,然后每个对立处呢,只能允许部分的这的操作,比如说它可以实现那个渲染功能,实现计算折射器相关的功能。
它这个可以支持复制复制buffer,就做拷贝的这个功能,然后并不是每一个对立都能够实现所有的功能,有些对立只能实现部分,有些对立可能是另外一部分,有些对立是另外一部分。
也有可能它这个硬件实现就没有一个对立能够实现所有的部分,这个时候你就需要根据这个对立不同,然后来做这个操作,然后包括你的那个swap buffer,就你汇集到东西画到屏幕上去。
也需要那个对立处来实现这个它能够支持的功能,你才能够用那把这个命令扔到那个对立处的那个对联里面去,OK,然后我们这边可以看一下我们的对立处。
接触。嗯。嗯。嗯。(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中)。
(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中)。
(自言自語中)。
(自言自語中)。
(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中)。
(自言自語中),(自言自語中),(自言自語中),(自言自語中)。
(自言自語中)。
(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中)。
(自言自語中),(自言自語中),(自言自語中),(自言自語中)。
(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中)。
(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中)。
(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中)。
(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中)。
(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),(自言自語中),[终结]。
GAMES106-现代图形绘制流水线原理与实践 - P3:3. 图形绘制流水线的基本原理与实践(二) - GAMES-Webinar - BV1Uo4y1J7ie
我们进入到那个会诊流学原理的第二课时,然后我们来回顾一下我们的课程安排。
我们的第一课时是讲那个working的,基础架构和流程绘制,然后第二课时就讲working的创建对象,对象创建内存管理以及调试的方法和工具,然后我在下周三的这个时候,会讲那个walk的多线程同步。
还有一些基于移动端的一些常见优化和实践,ok然后现在呢我在群里面看到有反馈,大家对于这个作业会有点疑问,然后我现在就把那个作业讲一下,我们的这个第一个作业吧。
然后大家可以看一下我们的作业框架,啊,大家记得在下载那个呃代码仓库的代码的时候,记得把那个敲一下。
那个git的时候,把这个sone video也一起拉一下,就是git 3红酒update,把这个我们的那个有些第三方库,是以那个3v6 的方式然后拉下来的。
然后我们来看一下那个homework一的那个作业要求,然后这个要注意一下,是我们要去下载一个这个文件,这个文件应该有有同学在那个有同学在那个games,那个我已经在qq群里面已经放了,ok已经放了。
大家把这个东西解压到这个data文件夹里面去,然后就可以了,然后呢,我也把那个我们需要做的模型给倒了进来,大家可以看一下这个模型,如果真的乱起来的话,大概是会有一个这样的动画,这个模型是带骨骼带动画的。
ok然后大家如果有兴趣的话。
也可以看一下这个模型的原作者是在呃,然后在这个网站上会放一个页面,我等下再来看吧,这里网速好像有点慢。
然后我们的作业要求呢是有那么几个,第一个是我们的homework,一只是读了一个g o t f,但是个静态的,而且只有颜色作为那个材质,而且他的那个比亚迪f也不是pr的,比亚迪f实现也是有点问题的。
只有一个一个有一个方向光,所以说我们这个作业要求呢,第一个是嗯,一个是jo tf已经上传到我们这个地方去了,然后我们的代码也是有。
然后我们运行一下我们的代码,我们看一下,现在我们运行起来的是,是一个怎么样的效果。
ok我站的是个静态的,大家可以看到这个动画低帧的那个效果,然后这个贴图也是不正确的。
因为我们的这个材质里面是,就没有实现对应的功能,它的写着,我们可以看到,它只实现了一个非常简单的一个嗯,应该是个缝魔性吧,对看起来应该只是个简单的缝模型。
然后所以说我们这个要支持gtf的那个,骨骼动画,然后第二个呢我们要支持就tf的pp r材质,包括他的那个法线法线贴图,还有一些p8 的那个贴图,然后这个作业要求呢。
一定要在那个homework一的基础上做修改,然后不要提交其他框架或怎样的代码啊,这个我呃毕竟这个是个课程作业嘛,我因为我们在第一个课时提交作业的时候,就发现有同学上交代码,其实是来自于其他框架的。
那啥框架,可能有一些框架已经做了这个事情,但是嗯我还是不建议大家去直接用吧,然后然后直接上来就可以了,然后最后呢这是一个进阶的作业,增加一个一个tom map tomap函数,就是下面写的一个函数。
输入是一个颜色,输出是一个颜色,ok然后函数是直接在这里已经有了,大家就增加一个额外的一个lpass来实现,不要把这个函数写在我们的这个fragment,shader的最后面。
就不要在我们的这个函数shader里面去去实现,就这里增加一个通,对,不要通过这种形式,一定要。
然后我来增加一个pass,ok。
这个就是就是这个模型真正产之后的样子,就是他用了pp材质之后,还加上黄金光,有一些必要的方向光,然后有阴影,但我们作业没有要求有阴影,有兴趣的话,大家也可以做一下,ok然后。
可以啊,然后下面的一些是一些实现这个东西的,一些资料,大家可以在这些一个人口里面去找到,找到我们的代码,看一下对啊,找到我们的示例代码,然后这个jo tf skin,就是那个如何读骨骼的一个示例代码。
然后有个p p r base,是基于那个方向光的一个p r的实现,然后还有一个example的p b r i d l,是实现那个p br的那个环境光嗯,然后怎么运行这个代码呢。
可以在我们的这个作业框架里面。
把这行注释给解掉,那么它就会可以出现,那么我们的那个座椅框架里面,就会就会出现有对应的那个sample,然后运行对应的sample就可以了,在这边会有对应的sample。
我们去运行对应sample就可以了,ok,我再把它改回来嗯。
大家可以在这边有些截图,可以看到一个p bi的i b o环境光,一个是p bi的那个方向光,它这边是做了四个方向光,然后还有geo tf的skin骨骼动画,ok那么这样子的话。
我们的课程作业就基本上完整了。
然后你还增加一个额外的一个pass,专门用于颜色的那个线性颜色的转换,ok这个就是我们的那个作业的安排。
跟那个作业的那个要求对了,然后关于g o t f里面的那个骨骼动画的描述,我们可以在那个p br里面的。
我们可以去看下,极有天赋的官方文档,里面有关于那个谷歌相关的描述,这里就是谷歌相关的描述,还有关于材质相关的描述,嗯所以说我们下面的作业要求,其实就是在这个地方一个载入,就贴服里面的骨骼相关的。
然后发现自发光,还有等一些批量的贴图,然后再次扩展一下我们之前的那个渲染的shader,然后再扩展加一个额外的pass,ok这就是我们那个嗯homework 1。
这个homework一工作量应该是有一点大的,我觉得但是嗯大家如果是写一遍,再有就知道这个working的具体代码怎么来实现了,ok然后再回到我们的这次的课程,然后这些课程是创建working对象。
然后walk内存管理,第二部分,第三部分就是working的绘制,然后第四个就是相关的调试工具,还有一些方法,然后我们来看一下working的对象创建,这个是上一个课时就会已经讲了。
我们整一条那个渲染管线,然后这个渲染管线呢,我们每一个阶段呢,其实都会有对应的那个working的对象,就可以主要是这些对象,一个是free buffer,一个是buffer和image的。
一个给那个线绘制管线的输入,一个叫pipeline,是用来定义你的嗯,你的这个绘制管线,还有一个提高标的叫做描述及describe,described set,这个东西就是用来传递你的uniform。
给那个绘制管线的,ok然后我们来看一下walking的这个函数接口,working,后面所有的函数基本上都是这样的一个风格,比如说嗯他们一般来说你要创建一个对象。
那个函数都是vk create object,object,可能是个buffer,可能是一个memory,也可能是可能是个buffer,可能是个tag,image,可能是个pipeline。
所有的都是有可能的,然后然后在我们创建的时候呢,它会需要有一个描述符,然后来定义你,你去声明好,你聊创建的资产的内容跟一些信息,然后它的倒数第二个参数,一般来说就是一个自定义的一个。
allocal的一个对象,这个不同于open 9,open 9,你去申请了一个对象之后,这个对象的管理其实是驱动帮你做的,然后worker呢你可以用一个默认的一个,就是我可提供一个默认的那个构造器。
就是你传一个空指针进去了,就是在这个地方,就是这个你可以传一个空指针,那么它就是一个默认的一个一个对象方法,当然你也可以自定义和内存管理,那么所有的worker的总体内存的创建和释放。
都是由你自定义来的,然后下面这个流程是我可能如何创建一个buffer,的一个流程,然后创建image跟buffer那个流程是一样的,只不过image他会稍微复杂一点,因为image有miss map。
有一层一层一层的这种概念,ok,然后我们这边的buffer呢是建了一个uniform buffer,大家可以看到嗯,啊大家可以看到一个是uniform buffer,然后然后还有buffer的大小。
这个其实是声明你这个buffer是干什么用的,然后你这个buff想要多大,然后这样子去获得了一个buffer的句柄,然后它的返回也是把那个buffer对象的指针,作为最后一个参数传进来,也就是说。
其实一般来说,你所有的worker的创建者对象的函数,都是基于这样子,第一个参数是呃,local device,就是vk device,然后第二个是你要创建对象的那个结构,描述它的结构体。
然后再是一个自定义的那个构造器,然后说自定义的内存分配器,然后第三个就是你真正要创建那个对象的,那个句柄的地址,ok然后我们在这样子创建了一个buffer之后,然后第二步我可能是需要你去知道你这块内存。
可能会占用了多少,这个我为什么要说可能呢,那有同学就会问,我明明已经在buffer里面声明了,你这块buffer要有多大,那么为什么welcome这个地方会还是需要去,或许他真正需要占用的很多时候。
其实是因为我可能为了一些嗯,优化它那个内存是需要对齐的,一般来说像uniform buffer,至少是四个浮点数对齐呃,也就是说你的这个办法,可能只是一个浮点数的大小,比如size of 5。
一个一个float,那么真正申请到的其实是四个flow的一块大小,当然这个也是有可能,只是可能具体实现要看working的,具体的驱动的实现,ok然后第三块,当你获得了这块buffer。
需要创建的内容有多大的时候,第三个就是你要去分配这个内容内存,然后你去分配内存的话,你要去实现好你的那个分配内存的大小是多少,这大小就通过那个vk memory呃,呃requestment的。
它的返回的结果告诉你这块内存有多大,然后后面的第三第四个参数,可能暂时大家不知道,后面我会来详细解一下,在后面的那个内存分配那个模块,我详细写一下这个语音是什么意思,然后这个知道了要分配多大内存之后。
我们就可以用walker的这个命令去分,去创开辟这么一块内存,然后最后是把我这块内存跟你的那个buffer,去做绑定,这里的最后一个参数是offset,因为这边的内存跟那个八核大小是一一对应的。
所以说这边是零,ok这个就是working创建一个对象的一个方法,在他的这个逻辑下面,就说他返回的这个relt,是跟类似于open geo的g o check error,以及它如果这个函数调用成功了。
他会告诉你这个他的result是成功了,如果有错误,他会告诉你对应的错误,然后他们所有的返回,都是通过你的那个句柄的指针对象,给它塞到最里面去,然后作为返回的结果,然后所有的对象的创建。
你都可以自定义那个内存和微信,也就是所有的内存你都可以自己来管理,这边我说的内存是指的是cpu的内存,并不是gpu的内存,因为gpu的内存我们可以看到在这个地方。
他并他的是他这边的gpu的四方申请的时候,我们用到了这个内存分配器,其实指的还是指的是这个walking divorce memory,这个对象的创建和释放,并不是gpu的那块。
然后gpu那块我们会在后面讲到,我可是有一套的一个方法,ok然后接下来我们看我们的第一部分,我们的render pass,然后我们render pass这个这个对象其实主要是混乱,pass。
这个定义的是描述的这个pass中,绑定的learn target有哪些,然后有哪些绘制阶段,然后需要值,然后让pass需要执行哪些操作,比如说就是上节课时我们讲到的这个,我要会知道哪些纹理上去。
这个我是否要写进去,然后纹理出来的东西我是否要漏的。
或者discuss掉,ok我们现在可以回过来看一下我们的代码,我们直接运行我们的homework 1,我们homework一的那个prepare,the working example。
prepare里面有一个这个set up和repass,这个阶段就是嗯初始化learn pass的地方。
我们可以看到嗯,我们看到我们render pass 7主要是定义了两个东西,一个是attachment describe,然后一个是定义的那个south past descript。
然后attachment described,其实它定义的就是你的输出的,你的输出通道的格式是什么,到底是rgba的还是什么深度还是什么y u v,然后你的输出是load的还是还是嗯是要漏的。
还是store还是discut,还是要clear,然后在渲染开始前后的一个布局,这边会有个概念比会比较重要,叫working vk,imajor layout,这个布局它其实是一个内存的布局。
就说我这个我这张图在不同的纹理,不同的作用下面,我的内存布局其实是不一样的,嗯但是我但是我们在作为一个初学者的时候,我们就可以简单的把它当做一个状态来考虑,就可以了,比如说我现在在绘制。
那么我在绘制是有个布局的,我现在要做内存的拷贝,我在内存拷贝下面我是有一个状态的,有个布局,然后我现在要把它画到屏幕上去,ok那么它又是一个一个状态,一个一种一种layout。
然后sub pass described呢,它是指像具体的你的这个shader的pass,它的输出到底是多少,它这边会有个指针指向了那个通道,纹理通道的输出的数组,指向这个纹理通道的输出的数组。
然后然后他soul pass呢,其实那么它的名字叫soul pass,其实理论上来说它是以他也是一个数组,它可以有多一个pass,我们在一个乱的pass下面有多少pass。
好多xcel pass之间呢其实是有依赖的,你嗯是我可能有些依赖,我可以自己算有些work,依赖呢我可不能自己算,那么这个时候呢就需要有一个依赖关系,用它来证明,来用它来声明那个working。
多个pass之间的互相的依赖,嗯当然我们现在我们的课程的homework 1,每个learn pass里面只有一个操作pass,所以说这个其实并不需要,只要设置一个nn就可以了。
ok我们回到我们的代码,我们的那个homework一它里面的会有两个,我们可以看到它,attachment是设置了两个attachment,这两个attachment。
第一个attachment它是个颜色,也就是我们画到屏幕上的一个颜色,就是swab chen的颜色,其实是我们可以直接看到他这边,swab chen的颜色是rgba的。
然后第二个是它那个深度的一个颜色是d,32呃,s float s8 u int,然后我们可以看到他对于定义了一个load,op是clear,意味着我这张图我在会之前,我什么什么都不要,然后stop。
我要把它写回去,当那个深度也是这样子的,然后他这边就会有一个叫做layout lout,这个东西,就是说我在这张图的状态,进入到我这个lunpass之前,它是个什么状态。
然后我扔了80之后又是一个sevi的状态,ok我们这可以大概的吕布下,这是我的所有的image的状态,这是利奥特叫做其实布局,但是我建议大家以一个状态的概念去理解,这个这个东西啊,不要去布局。
事实上它不同的状态,它的内存的排列方式是不一样的,但是我还是非常建议大家,以状态的方式去理解它,而不要用我可真正的那个他设定的,那个官方说的那个布局去设定,因为我觉得这个布局设定太太复杂了。
大家要需要walking的概念已经很多了,需要化繁为简一点,ok然后我们这边定好了我们的attachment,然后再定义sao pass,然后sao pass呢。
他们这边呢我们有一个颜色的attachment,它就会引用我们的sao pass的数组第零个,然后绑定作为颜色,然后深度的the attachment,绑定了我们数数组里面的。
是下标为一的一个attachment,然后它的深度就是一个深度的一个一个选项delete,然后我们再位置好差pass之后,我们再把它可以绑到我在pass里面,这个learn the pass的创建里面。
ok然后他这边也定义了一个相互依赖,但事实上我们这个pass其实非常简单,其实可以不用设定,然后但是我的框架是写了这个,这个相互依赖的一个关系嗯,ok然后这样子我们就可以去创建。
我们的walker creator ra pass,我们可以看到它也是一样的,第一个参数是logical device,第二个参数是那个创建的那个描述符,第三个是内存分配器。
你可以设置为空用默认内存分配器,第四个就是你想要那个创建的对象的句柄。
ok,这个就是我们的render pass,这个概念呢在open g下面,其实就是我们的render target是羊杂,然后第二个就是我们的缓冲区,呃这个呃不对,run pass,这个东西。
只是讲的是我这个pass的输入的一个格式,但不是真正的那张输出的东西,然后framebuffer,它定义的是你真正需要输出的那个东西,它定义的其他蛮简单的,就是说我的输出的那个真正输出是谁。
image view,然后我想要输出的宽高layer,这个layer其实是指的是多少层,这个东西在2d的话,一般这个layer就是一层,假如说是张3d纹理,那么它可能是有多层的一张3d纹理或者qq吧。
立方体纹理,那么它的内页就是六层,ok我们再来看一下我们的代码是如何去建立。
我们的,是如何建立我们的lpath,如何创建我们的free buffer,ok他这边就我看的非常简单,然后他这个free buffer里面,如果有一个在switch里面本身就有的一个深度图。
一个一个深度的那个texture,然后绑定上去,然后设置好宽高,然后设置好你对应的runpass,这个run pass,就是一般来说可以用,我们刚才创建的那个render pass。
ok然后你再把那个颜色根据那个深度,就是swap chain里面的那个,一般来说是双缓存或者多缓存的方式,然后把它绑上去,然后我们这边可以看到这份实现的话,所谓chain里面的他image有三张图。
也就是说我们swap chain里面是个三款存,然后所以它会有个三个framebuffer,然后每个framebuffer的绑定了attachment的,第一章的image,就是颜色,那张图。
就是对应着每一个swap chain的每一张图,那么我们这边对应的也是有三个free buffer,我们在这边成功创建之后,会有三个free buffer的一个大小,ok大家可以直接就可以看到。
在这边会创建出三个free buff的句柄对象嗯。
ok然后下一个下一个是我们要下一个阶段,我们要绑定wett跟index buffer,那么这个操作时候,我们就会用到一个叫做八分的概念缓冲区,那个缓冲区区,然后缓冲区如果创建,我们在刚才已经有提到了。
就刚才在这块区域区域里面有提到,如可能创建一块buffer的一般的流程,然后呢,你这个buffer呢,有的时候要真正的被用到的时候呢,它需要有创建一个叫buffer view的一个东西。
buffer view就是巴赫的视图,就是说你这个东西buffer其实是一个数据块嘛,但是你这个数据块要如何在被着色器中被使用,就需要一个view,它是同样一个数据库内存,你可以把它当做。
比如说都是r g b a8 位,你可以把它当成一个八位的整数来看待,你可以把它们当做归一化过的整数,就是说呃0255对应的,01这么一个概念,025对应01点零。
这个就是归一化过的一个一个buffer,当然你也可以这样子去作为一个试图去看,然后这个事情呢在纹理image上也是同样的的的的,方法,可以去你去去去做,然后他还可以去定义说你这个buffer。
只是说我在shader中作为一个输入,uniform,还是说我作为一个输出,还是可以作为输出,一般来说输出的话,在我们的图形绘制管线是不会有的,一般来说是在那个计算绘制管线里面会用到,可能它是一个输出。
然后最后最麻烦的,最后一块是我们的working的pipeline的创建,ok这个walking pipeline,我这个图我不知道大家能不能看得到这个图,我是直接从西瓜那边去拉了,就就没有自己做了。
太太麻烦了一点,大家可以看到他就是我,我在上一个课时就讲到了,我们的整个绘制管线有这么多阶段,有这么多描述,那么你的papa里面,要把所有的这些描述都定义清楚,包括你的那个你的vertex的输入。
包括你的这个vertex输入,然后包括你怎么做光栅化的这个深度,怎么检测,怎么颜色混合,然后你的papa layout,就是你的那个uniform是要怎么设置的,你的render pass。
你的这个pass的格式是什么,这里我给大家提醒一下,我肯拍拍案里面的learn pass,虽然说他这个对象,但是其实他并没有用到这个pap rpath里面,所有的属性。
比如说run pass里面会定义那个,你经过这个pass里面的纹理的layout,然后你的这个呃load operate或stooperate,你的load operate或stoperate。
然后然后去去就是你的这个进入这pass里面,在文笔的撩的是那个布局是什么样子的,结束这个pass之后,你在文理的布局是什么样子的,这些都是会忽略掉的,这些不会侵入到我们run pass里面。
ok然后我们来看一下我们的代码,我们来回看一下我们的pipeline,ok我们来看一下我们的pipeline,这个是怎么实现的,放大一点,呃其实基本上就是说把cc所有的状态,你都要写下来。
然后你得清楚的知道这些状态是干什么的,然后根据你的那个pass的需求,然后来设置好你的状态,然后这边它定义了是呃,有那么几个可能我之前没有讲过的,一个是你的这个顶点顶点的排布。
这边它定义了有四个顶点属性,然后每个顶点属性都是绑到了第零个,然后我们可以直接看我们的实现的shader,这定制了四个点属性,分别是位置,然后法线uv,然后颜色分别是三个三个两个浮点数,三个浮点数。
ok然后就是在这边会定义是rgb rgb rgb rgb,那么有同学就会问了,你的那个uv明明是只有两个浮点数,为什么你这边需要三个浮点数呢对吧,这个,对吧,这个,嗯这个东西的话其实是没有那么严格。
其实就是只要你声明了是这样子的,然后告诉你这个偏移是多少,然后然后你我们的这个shader,只要是能够对它做兼容的那个其实就可以了,并并没有说我那么严格,我外面cpu端写了两个字。
再说里面也一定要是两个,它它它并不是这样子的,你外面可以写三个浮点数,然后你现在里面写了四个浮点数,它们对应的其实也也是可可可以的,ok然后下面的你我们定义好了,这个vtx的输入样板。
然后我们再编译我们的shader,定义好了shader之后,定义好我们的shader stage,就是我们的vertex shader跟我们的那个flavender。
这两个shader呢是他已经是那个翻译好的,那么就呃有没有翻译好,这个是等于说它编译好的一个系的呃,但是我们自己要改了我们的代码,我们框架呢是没有,这个就是生成这个新的binary的这个流程的。
然后我这个时候要,哦有同学这个问了offset不会歪吗,这个地方我大概也好,我得我得详细的说一下,这后面的offset其实指的是你的cpu端的offset,只要你cpu端的数据。
他告诉你这边是三个浮点数,三个浮点数,两个浮点数,三个浮点数,至于你的那个嗯,他这边是第三参数是什么呀。
你的formula其实是关系没有特别大,你只要是对得起的,其实就可以了,嗯然后我来看一下大家怎么去呃。
怎么去编译这些的,然后大家应该都已经装了那个worker的,呃walking sdk,我们在working sdk的这个32位。
64位,我们在64位,这个地方可以找到我们的新的编译器,ok这个地方我们有一个叫做d xc,这个是听名字就知道他是一个direct x的编译器,h l s l,它是一个微软开发的那个地差的一个编译器。
但是呢我们也可以用它来编译那个walker,然后我们就拿我们的作业一的这个,这个写的来编译一下,然后这个写的是一个d差的h o s o,然后我们只需要d叉c,然后声明那个入口函数。
我们这边的入口函数是may,没然后再把路径给打进来,然后再是一个t告诉你这个是个什么,写的是我们这边是用的pixel,然后用sm 60的标准,这个标准其实是一个那个d差的一个标准,然后我们再加选项。
spi告诉他,我们是需要编译一个working的一个shader,ok这样子出来我们就可以看到了,我们编译出来的working的这个蝎子,这个蝎子看上去这是它的那个嗯,当然我们真正要用到的是个二进制的。
但这个是一个文本的方式,然后来展现给我们,然后这个写的看起来就不是给人类阅读的对吧,其实它跟汇编很相似,它每一行是表示一个命令,基本上你可以把它当做一个汇编来看待了,然后我们这个时候呢。
我们要升级成二进制对fo,然后,ok那这样子,这个就是我我现在生成的一个walking的binary,我们可以二进制看一下,其实这个就是我新生成的那个。
用h o s l c生成了一个shift的binary,然后我们再来看一下我们那个vtec学的,怎么去生成呢,也是一样的,我们把那个打开我们的vtec的,它的入口函数呢是叫也是may。
所以我们这里就不用改了,我把这个改成mtt杠t,然后是是个vs,我才学的,ok然后这个就是我们根据他的这个系统,生成vtx的,然后呢我们在这个工具里面呢。
其实还有几种新的编译器,一个是g o s o c嗯,还有一个是gsl了,这两个系列的编译器的话。
大家是编译h o s j s l的,有兴趣大家可以看一下他的那个文档,它的文档怎么看呢,其实你只要把它编辑器拉出来,然后杠也取一下,哎,我操杠一去下就可以看到它这个是怎么使用的,一般来说也是这样子。
你要定义好它的函数入口,然后定义好它的那个是个什么状态的写的,然后定义好它的输出,一般就可以了。
ok然后我们提交作业的时候,要记得把你编译好些的也带上,然后这个时候呢,我们就把我的这个papi给创建了,这个拍拍烂呢,我这边就不细讲了,因为课时的原因,大家具体有什么状态。
完全可以照着这个walking的这个结构体去查看,反正他有这么多状态,每个状态里面又会有,每每个每个结构体里面又会有,包含着别的其他的结构体。
但是一层套一层的套娃吧,我觉得这个呃太复杂了,但是嗯实时汇集就是这么复杂,ok然后这个是图片,然后图片的时候创建的这个图片呢,跟那个buffer创建是一样的,是类似的,你要去描述好。
你要去创建这个图片的emit,包括它的格式图层,然后图片的用途是只是在shader中被采样,还是说可以作为颜色作为一个tk的输出,还是说是张深度图,然后你在用的时候,也是需要定义图片的那个布局。
然后这个图片布局跟那个pua的布局,其实是一样的,它在内存中确实是排列是不一样的,就是同样的一张rgb的图,你在不同的布局下面,它的内存排布是不一样的,然后呢,但是嗯我建议大家按照中台的方式去理解。
比如说我现在这个这个image,我是要作为一个颜色输出,那么它就是一个颜色输出的状态,我这个image我要做一次拷贝,另外一张图拷贝到你,但我的这张图的时候,它就应该是个拷贝的状态,然后他要被采样。
那么他就是应该是个被采样的状态,用这种方式去理解这个布局,我觉得会更容易一点,然后最后是然后我们开辟的memory,跟那个颜色的图要去绑定,然后呢我们这个颜色图片呢也是有个视图。
叫做wordk的image view,它呢也是需要你可以定义你这个被采样的范围,比如说我这张图的米卡是有个16级呃,409,比如说我这样是4096的图,可能有个十几级,但我并不需要那么多。
后面几集我不需要,我只需要采前三集前三等级就可以了,或者说呃我的这个图片我定义的是一个嗯,rgb a8 ,用int,但是呢我去卸的采样的时候呢,我想去踩的是它是个整数,我想用这个整数的方式去采样。
这种时候呢就可以通过那个image view的方式,然后image view呢,然后一般来说会有两种image view,一个叫s r v,s r v就是shader resource view。
它就是作为一个输出,然后呢还有一个叫u a v u v v,他的这个视图的意思呢,就是说我是可以做一个输入,当u v这个东西在绘制管线,gravc的排放量里面,一般来说是不会被是没有要求的。
他们一般来说是在计算管线会被用到,ok然后最后呢是管线布局,然后管线布局这个东西呢具体是干嘛的呢,就是说我们要去呃传一些数据给working,传给worker的shader。
我在外面我要定义好你的管线的set,就是一个lt它下面有好多个set,好多个set,然后每个set里面呢又有好多个好多个,每个set里面又有好多料,就是它有个好多个槽位,每个槽位呢会需要有一个呃。
描述说我这个槽位放的是什么东西。
然后我们直接看我们的我们的代码,我们的vertex学的嗯,比如我们这边vtec的他就绑了两个uniform,第一个uniform,它是一个一个场景的相关的一个uniform,他的set是零。
然后绑的也是零,然后就是说我是第一个描述符的set,然后绑定了的示意啊,它的下标就是槽位是零,然后第二个是就是push constant,它不是通过通过squier set去来更新数据的。
然后我们再来看那个,呃在哪里啊,ok我们第二个的uniform,他这个set的下边就是一describe set是一了,然后他的绑定的那个槽位是零。
那么这个时候大概的视图就是像这样子,我们有一个pipeline的lt,它要定义好美不同的描述符集,然后描述符集呢对应的可能是个第一个槽位,可能是个图片,也可能是缓冲区,也可能是个采样器。
然后第二个描述也是图片,也是缓冲区,那么就同学可能就会有疑问,为什么我这个管线要区分描述符集呢。
呃我们回过来看一下我们这两个东西的性质,其实是有点偏差的,第一个叫做那个相机相关的一个uniform,它包含了灯光位置,然后你的相机位置,还有你的project矩阵矩阵,那么这一切都是场景相关的属性。
那么我把这些属性设计成一个描述符集,那么我在更新的时候,我只要更新这一个描述符集,我这个描述符集可以绑定给我们拍拍line,所有的对象使用,然后第二个描述腹肌呢是材质相关的,这个材质相关的。
我这个材质相关的,这个跟我这个mesh的呃,它的一个呃我们的一个mesh其实是有好多个啊,这个场景里面好多子mesh,每个子mesh里面呢会有很多不同的材质,每个不同的材质呢我会有一个不同的材质附件。
那么我mesh会就是我们的那个叫tf 5的模型,会维护一个材质,一个一个那个负,一个那个我的模型会维持一个那个描述符极,然后我们的场景也会有个描述符节,那么我们再来看我们的代码。
在我们的代码也是有这个体现的,找错地方,ok在这个地方他在创建command buffer的时候,他这边的j它绑定了第一个描述符极,我们看到它描述负极零,这边他的第一个描述符集就是是uniform。
就是场景相关的,相相机相关的所有的那个uniform,ok然后在jo tf里面照的时候,它会有很多节点,其实对应的很多不同的材质嘛,然后每一个不同的材质,它就要去绑定不同的材质相关的描述腹肌。
那么这个也是更有利于我们去做更新,因为很多时候我们的材质这个东西,其实很多时候是不会变的,我们也知道我们玩游戏或者怎么样,你的材质一般是固定的,但是呢它会跟着不同的模型走。
我们的相机属性呢一般来说是会每帧都会变化,但是它又是一个全局的描述负极,所以我们通过这个他的描述无极拆分的方式,然后我们可以更友好的去构建我们的这个东西。
ok然后最后呢我们再我不去,然后接下来我们要看那个walk的内存管理,我们刚才讲到了wk在创建对象的时候,你是可以呃去实现一个自己的那个那个内存分。
配,内存的分配器,当然我们的working也是我的gpu内存,其实working也是提供了你这个能力了,你这块内存你其实想见到哪个里面,然后我们现在看一下我的working的内存分配。
当然离不开我们的那个硬件结构,硬件结构呢我们一般来说是有块cpu,有个gpu,然后我们一般的台式机会通过一个pci e的总线,然后去连接他们两个,当然如果是你是呃苹果m1 。
或者手机的一些s o c demi 4,把gpu和cpu做到一起,然后共享的内存,那么这个内存池可能会有点偏差,其实他们共享同一份内存池,并没有明显的cpu跟cpu的访问的速度的区别。
但是在台式机其实这个区分是很明显的,我们的gpu的内存就是放在gpu里面的,cpu内存就是放在cpu里面的,然后下面就是一个简单的一种情况,比如说我一个内存,我要从cpu拷贝到gpu,然后我要从cpu。
我要从cpu拷贝到这个gpu端,然后gpu再读回到cpu,然后下面大家会有有点好奇,我这边写了什么叫host visibility,什么叫什么divorce local什么的。
这些东西其实是内存的一个属性的一个,一个flag,然后这个flag会决定了,你这块内存的那个使用的速度,跟你该如何使用这个姿势,然后我们的gpu的地方会有一个什么vtest buffer。
index buffer的什么cos,然后run target,然后什么深度u a v,什么s r v,这个文件写的我该打错了,这个是这应该是个s r v,然后ok然后呢这个项目下面我们再来。
我当然是个比较抽象的内存的一个情况,我们直接来回过来找一个直接的例子吧,左边这张是我的3070的电脑跑出来的,这个内存分配的情况,大家可以看到呃,他告诉我有五种内存的情况,第一种嗯什么选项。
什么flag都没有,第二种他告诉我这个是个divorce lolocal,就是gpu的,然后第三个他告诉我他是一个host visibility。
然后host这边的host指的其实是cpu device,指的就是gpu,然后host你可以去访问是一致的,然后他告诉我第三个就是一个这样的一个情况,然后第四个是一个这样的情况,然后这比较特殊的。
大家可以看到二这个内存,然后下面我们再看一下,我们具体的真正的内存的情况,然后第一块内存块是一个divorce local的,其实就是gpu内存派,他大概这个数据呢是个八个g。
然后第二个内存块呢它是没有任何的flag,其实是一块cpu的内存块,他这边的内存呢其实也是八个g,那并不是说我的ccpu端显卡,是不是cpu内存条给我插了八个g啊。
嗯应该呃有同学问c b v是costa 8分,其实应该不是const buffer,这个是我打错了,应该是s r v,呃然后呃就是我我我我我,我的原意是想打那个sr v的对。
然后那个然后大家会可能会疑问,那么我的第三块内存,它叫明明叫divorce local,而且也不大,大概只有200多兆,那它是用来干嘛的呢,它其实是我们可以看到它的属性。
divorce local表示一个gpu可以用的host visibility,也就是说我cpu呢可以对它做读写,然后第三个属性保证了那个cpu,跟那个gpu的内存一致,我可以不用做同步。
那么大家想一下这个内存cpu能读写,gpu也能读写,而且他们内存可以快速访问,也就是我cpu写完之后可以马上,gpu马上就能呈现到了,我不用做一次跑pci e总线的这个流程,那么它能干嘛呢。
当然是可以给我们非常快速的同步数据,从cpu同步到gpu上去,那它的作用就是这个,当然这个是我的3070的桌面显卡,并不是每台显卡都是这样子的啊。
这个我可以讨好我现在用的笔记本,虽然说也是3070的,但嗯我看一下他在哪里啊,我看一下它query出来的这个内存是是什么。
可它快乐出来了一个快内存,这个是gpu的8g,然后这个是16g的,我的cpu,这16级确实是我的cpu的那个那个存储的,我的内存条,但我的内存条是x 16 g的,这是我们的共享gpu内存。
这共享gpu内存,其实你可以把它理解成是一块内存条上的,然后我的一个专用的gpu的内存8g,然后16g的那个共享的gpu的的内存,就总的gpu可使用的内存是23个g,23g。
然后上面呢就是说我每块内存呢,它的所有的属性能干什么干什么。
然后我来给大家解释一下,这些每个属性是干嘛的,divorce local就很简单,能够被gpu访问,你就可以理解成他就gp访问的特别快,然后host visibility就比如说它能够被cpu访问。
但速度怎么样呢,不一定,然后第三个呢就是保证了我cpu把这块内存写到,把这个数据memory copy到这块内存上去,我gpu马上能够做到同步,然后还有个cash,cash的话,它的类型其实就是说。
我的这个cpu会被缓缓存这个内容,然后经常会用来做cpu读取,比如说我从数据从cpu到cpu的时候,从那个divorce到后host的时候,我需要这样的属性的内存来去获取。
然后还有一个叫还有一个是比较lazy的是呃,就是说你的working,你在开辟这个内存的时候,我不会马上开辟,等到后续我会去使用,我真正需要它的时候,我再去去分配,那么这种东西呢,这种设定呢。
一般来说会在那个内存不足的设备上,比如说移动端一些手持设备上会这么做,一般pc电脑可能是并没有这样的,这样的的内存款,然后然后我们再来回顾一下,我们刚才的那个申请内存的一个结构体。
我们需要定义你申请的gpu的大小,然后呢,你也需要定义你申请的那个你的那个memory的类型,诶这里我又做错了,其实是这样子的,他的memory类型我们这边有五个,那么你要去申请到哪块类型里面的。
哪种类型里面的那个内存内存池里面去申请,然后但是呢我们这边会有个限制,那个walking申请内存次数是有限的,它一般来说,working的标准是要求你不小于4096次呃,那么我们会想一下。
我们绘制一个场景的时候,我们的所有的buffer或者image对象,真的只有4096个吗,当然不是,我们可能会有几万张图或者几万个小的办法,都是可能的,那么我可能这个是什么意思呢,就是说我很希望你自己。
尽量减少申请内存的次数,你申请完了之后,基于这块内存,你是给哪个buffer用,那么你自己来解决这个问题,你自己来判断,我们回过来我们看一下。
我们绑定其实有个叫做walker帮顶buffer memory这个操作,他的操作就是我申请好的这块gpu内存,我跟我的buffer要做怎样的绑定,我这个buffer要做跟这个申请好了。
这个gpu做怎样绑定,那么working的gpu的管理其实是这样子的,那么大家现在想一下这个流程是不是很复杂,我得知道清楚的知道我的每一块buffer,它是被干什么的,然后呢我还得去根据它的使用用途。
我得去找我的需要的flag,然后呢我根据我需要的那那些flag,然后我再去从硬件里面query出来的,那个内存池的类型里面,去找对应的那个memory的tap,然后找完之后呢。
他在我可能再帮我去从那个memory的hips里面,去申请对应的内存,这样子我才能创建,那么这个事情我觉得嗯过于复杂,我觉得应该我觉得每个人上来都会非常迷惑,我到底怎样的buffer。
我该用怎样的flag,怎样的东西,比如我只是一张图,一张纹理图,我该用什么样子呢,直接用divorce logo是不是可以,还是说host v的编写是否有,是不是也可以,然后这个比较好的。
但是呃md感谢md,他做了一个叫walker memory alloco的一个库,他可以用更容易理解的方式来管理内存,它甚至帮你做好了内存分配,也就是说你并不需要自己去管理你的这块,开辟出来的内存。
给你哪块buffer用在这个库已经帮你做好了,包括它的使用途径,他也帮你做了,它大致区分为了123456,这么六种使用途径,我们再对比一下之前的这些flag,它的六种使用途径,相对来说就更容易理解了。
一个是gpu only就非常简单,就是在gpu中读写,我在gpu中读写,一般来说可能初始化的时候,会从cpu端拷贝一次数据到gpu,那么你就用fm格式类型就可以了,那么什么会用呢,一般来说图片就是这种。
然后第二种呢是cpu only,就这个图片我可以被cpu端频繁的读取拷贝,然后第三种就是我这块新厂,我这块内存就是用于cpu拷贝到gpu的,然后下面是gpu拷贝到cpu的,哦有同学问老师。
为什么前面ppt为啥是不少于呃,4096次,这个4096次呢是working的标准,working的标准提供了,说你知就是说你申请的次数是有限的,但是你申请的次数到底是多少,靠驱动实现,然后呢。
这个working它定义的标准是,你这个申请次数的最小值是4096次,就是说你的申请次数,这个东西不小于申请,也就是说你申请次数最大值的最小值,最多申请次数的最小值,是这个意思。
然后下面就是cpu copy,其实就是你要需要频繁的把gpu,数据拷贝到cpu端重用,然后最后一个就是slazy了,那么就是后续连续分配,这个比较好理解,然后我们讲一些常用的情况,大概哪种东西会需要。
然后gpony我们的run target,我们的输出一般来说就是gpu only,因为它只是gpu中做独显,然后cpu到gpu,我们的一般来说uniform image,vertex buffer。
这种呢需要偶尔的一次从cpu拷贝到gpu,也就是我们材质,当然我们的image你可以根据使用情况,我的image呢你可以根据使用情况,有的时候呢你这个image其实不更新,也就是说只需要读一次就可以了。
那么你可以用gpu y也可以,然后呢像a test buffer,如果你们也是一样的,只是一次,后面不会更新了,那么ok我也用gpu only,但是呢像uniform buffer。
基本上可能每帧都会更新,或者是偶尔有变动的时候,它会更新一下或者若干帧跟你讲,那么你需要申请类型就是cpu gpu,ok然后呢这边呢会有一个叫做cpu only的这种类型。
专门用于创建叫stage buffer,cg buffer,就可以更高效的来,来做那个cpu跟gpu端的内存拷贝,然后下面我们来看一下switch buffer是怎么用的啊,左边这块,左边这块呢。
就是我们一般来说你要创建的一个类型,就是cpu to cpu,然后cpu拷数据呢通过map on map再写入,map的操作是把gpu的内存块映射到cpu端,然后cpu端就能读写了。
on map呢是把cpu我写进去的数据呢,然后再原封不动的把它拷回到gpu,所以armp是一个同步的操作,然后这个memory跟那个working buffer做绑定。
然后再把这working buffer做渲染,这个就是我们一般意义上的那个那个uniform。
的一种更新,我们的课程其实他也是这么实现的,我们可以看一下我们的课程里面的渲染,有个update uniform buffers,ok这个数据直接拿过来,然后这个是八核的对象指针。
然后直接把这个数据直接就拷拷贝上去了。
嗯然后接下来呢就是我们的那个渲染,嗯然后我们的switch ber是怎么做的呢,一般来说我们的去建一个gpu的buffer,是gpu里的,也就是它不能最好不能被cpu读写。
但是呢我们去额外的去建一个stage buffer,然后switch buffer用于那个那个用于cpu读写,那么这款stg buffer就是我们之前对了,我们这块显卡的quite nature。
其实指的是,那么这块内存可以被cpu gpu同时非常高效的使用,虽然它比较小,那switch buffer就是开在那块区域里面的一块buffer,然后我们cpu端去读。
去读写这块switch版本就非常高效,然后所以说我们一般来说,如果是每帧都要更新的一些高频操作的,数据更新,就会通过一个stage buffer。
我们先把tv端的数据拷贝到stage buffer里面去,然后switch版本呢通过那个worker的那个copy命令,拷贝到那个gpu的buffer里面去,然后gp的buffer。
然后再真正的去到渲染,然后再做有什么优势呢,第一个是可以减少cpu跟gpu之间的传输次数,那同学就问了,明明每帧都是要传啊,为什么说减少传输次数呢,是因为你的不同的内存块。
中间的那个cpu端对它的读取次数是不一样的,我们的c9 buffer,其实是直接就写回到我们的gpu那块了,然后gpu去访问它也很快,但是如果用一般的更新根据方方式呢,其实它是需要一个同步的操作的。
你吸pu端把它写进去的时候,其实gpu并没有马上同步,你要对它调一个强制同步的方式,它才会同步上去,然后第二个呢是减少提高性能,减少cpu和gpu等待,第三个是可以简化你的代码,减少异步导致的问题。
这个异步导致的问题呢也嗯也非常容易理解,比如说我的这块buffer,我目前我的办法正在绘制gpu和cpu是异步的吗,gpu和cpu是异步操作的,然后我这个buffer在渲染的时候呢正在被使用。
那么这个时候,假如说我外面有一个cpu不诚实的cpu,我正在渲染,正在被使用中,然后外面有个cpu就把这个数据给改了,那么我在真正绘制的时候,他就可能会变成一个中间状态,或者说是一个不太确定的。
是跟时序相关的一个状态,你的这块bug的数据就变得不一致了,我cpu要保证它不被使用的时候,我再去拷贝,那么他才是不会出现问题的,然后我引入了stitch buffer之后呢,就可以避免这个问题。
stage buffer的所有的那个写入都是独立的,跟我渲染没有关系,因为我是新建了一个y的buff,然后这个web buffer我要拷贝到gpu。
这个事情呢是在gpu的一条command buffer里面做的,所以说这个时候也会保证了它的渲染是有序的,我们在一条ctrl buffer里面的,所有的命令调用都是同步的。
ok我们再回到那个working的绘制,呃,这边有同学问on map不是结束映射吗,呃是的,arma是结束映射,其实结束映射你就可以理解成,就是我cpu的数据要考到,通过pci e总线拷贝到gpu中。
然后working的绘制,这个就我们刚才讲了,有那么多的那个流程,那么working的绘制其实大致也可以概括成这样,简单的一个是绑begin command buffer。
我们新建一条ctrl n buffer,然后要begin command buffer,然后要有个你给一个专注pass,然后专注pass一个learn pass的作用域,下面要有个end。
然后下面要有个end of command buffer,在做作用域里面,我们可以设置你的那个视图,一些裁剪空间什么之类的,然后绑定的set,就是绑定你的uniform的那个描述符。
然后绑定你的pipeline,然后绑定你的vertex buffer。
index buffer,然后再去绘制,ok这个地方我们可以看一下,我们这边的command buffer,这里build camera buffer就是,我们把所有的v k command相关的接口。
把它高亮出来,我们所有的command相关的接口,都会有一个v k command这个前缀,他就是先,他这边的呃,首先我先要begin一下那个command buffer,然后再绑定我们的p s o。
然后绑定你的视图,然后绑定你的第一个相机,场景矩阵相关的那个描述,负极矩阵相关的uniform,然后我们这边的材质只有一个写的,所以我们这边直接绑定了一个papi,然后这边是画线框还是不非线框的。
一个拍拍的区别,然后再接下来是我们的模型机和tf美,我们的一个模型呢,它这边的模型呢是只有一块五test buffer,然后一块index buffer,然后我们再去看一下它的每一个模型里面。
它是分材质区域的,那个材质区域指的是我这块vertex buffer,中间的一部分是一个材质,然后后面再加一段offset names,第三个of,第三个片段是第三个offset。
然后这个绘制材质区域呢先是一个constant,push constant,这push constant指的就是它这个矩阵,如果这种非常频繁的更新的东西呢。
大家最好是使用这个push constant啊,不要用uniform的方式来更新它,然后preconter,然后再绑定那个材质相关的那个那个scribe set,然后再次绘制。
然后再是我们的中国index方式绘制,然后这颗那个叫tf是个场景数嘛,所以看我们还要递归的去绘制它的那个节点,可以我们可以通过那个只有tf的这个框架,大致的就流程就知道了。
我们的worker是怎么绘制的。
然后这个会有一个非常有趣的地方,大家可以看到我们在初始化的阶段,ok我们刚才的代码是绿的,command buffer是在初始化的阶段去调值调用的,但是我们真正的绘制的时候呢。
ok我们只是summit掉,把这条command buffer submit下,我们并没有每帧的渲染都去重建,这个是command buffer,这个也是working。
可以提高cpu端效率的一个重要的原因,因为我们的cpu端的command buffer,把所有的命令调用都收集起来了,ok了,那么我们在gpu特色渲染的时候,当你的卡牌buffer并没有被修改掉。
或者你的逻辑上就是可以一直复用的,那么我对他做不用做任何的cpu的修改,那么这张我的cpu的命令就完全省省掉了,然后只需要把它给扔到我们的队列去就可以了,嗯如果是哦,我们回忆一下。
如果是open gl的话,我们每次交考,我们都得把open gl的状态机重新设置一遍,重新调教考,那么这个cpu端的调的命令,其实就影响到了我们的gpu的渲染,因为gpu可能会在等你cpu再跑。
然后我们这样子做,我可能这样操作呢,也可以把我们的有限的cpu的资源也用用来拷贝,更做更有用的事情,比如说你要去做模拟啊,或者做别的计算,就而不是说其实只是在整理你的那个调用队列。
这个也是walking一个嗯比较有优势的。
它可以减少你的cpu的一个地方,我们说我很高效,就高效的在这种地方。
可以,然后下面我们来看一下那个调试工具和方法,调试工具方法呢这边有一个调试,我就没有讲了,就是walking是可以设置你的那个那个错误的回,调,那个东西大家可以一样的。
可以看一下我们的作业框架里面是有实现的,大家可以找到对应的代码,然后自己去看一下它是怎么来实现的,这个功能就可以了,我这边主要讲一些一些工具吧,这个是我一直在用,我觉得非常美妙的一个工具。
就是la docker。
他你要使用也非常简单,你在p3 的话,你只需要把你的那个可执行命令放进去,然后你的工作路径如果有编辑选项,就加编辑选项。
ok我lag起来,然后就可以通过lash起来。
按下f 12就可以抓帧了,然后呢他还有一些操作,比如我只想要抓地零针,就是前三针,我也可以通过这种方式去去一起来的时候,我就可以去绘制前三针前面的三针,然后每一帧表示你这一帧用到的内存,可能是多少。
记录下这一帧的内存是多少。
然后我们可以看到它所有的working的命令,然后这边呢我们可以做筛选,把所有的word命令做一个筛选,这个因为是第一针了,所以说它有些初始化的调用就不在这边了。
我们直接看一下第零帧吧,它所有的初始化的命令,包括创建对象都会在这边有体系体现,然后每一个dequest set,其实你是可以设置它的名字的,当然如果你不设置名字,就跟open 91样。
是102341个自增的一个东西,然后我们一般来说我们用它呢。
比较关心我们的交扣,我很我们可以看到你的输入的纹理是什么,输出的纹理是什么,然后我们在papi data里面可以看到每一个阶段。
你的vertex shader,你的输入的uniform,你的矩阵view,然后你的那个就是push constant,然后还有你的写的是什么样子的,甚至你还可以修改你的shader。
你把你的shader在那边做一些修改,可以马上体现在那个地方啊,我们来试一下啊,比如说我在pc手写的,我修改我的切的,把最后的颜色我们改成uv,然后一下哦发现编译错误了,因为我们忘写了个封号好。
我们再apply一下好,编译成功,这时我们再来看我们输出好,这个中期就是我们的uv的状态了,那么这个时候聪明的小伙伴就会发现了,那我们调试的时候不是很方便吗,对啊就是变得这么方便。
你只要去可以通过改写的方式去调试,很多时候,我们没有办法去单步的去调试这个命令,但是呢我们可以通过这种方式去,去做这个事情,然后其实你后面的话也是可以单独去去运行的,对其实甚至可以对它做单部的运行。
但是单步运行呃,我的经验是大家不要太依赖于它,因为只是嗯毕竟是有偏差的,他是一个模拟的gpu的,也也不是模拟吧,反正是gpu可以提供这个命令,就可以提供这样的接口。
但是往往有时候特别是在做一个并行的计算,比如说计算组合器的时候,其实它并不准确,然后你也可以看到你的这个像素的历史,比如说你在这个时候是颜色是黑的,我做了一次beginning的时候做一次clear。
然后把clear成了一个呃浅一点的黑色,然后再经过一次交换,它变成了绿色,这个工具是我觉得我推荐大家去做调试,结果正确与否的一个非常推荐的工具,但是它也有个缺陷,就是它对于你的那个那个耗时。
就是你迅速做profile它其实不太准确,而且效果也不太好。
ok哎呀,我的风扇在疯狂作响,然后第二个我比较推荐的一个工具呢是n set。
他这个优势呢,就是说第二个我可以看到那个扔lock 75,因为我们的客户work比较内存比较小,所以感觉感觉不出来,其实它占用的内存是蛮大的,当你的场景一复杂之后。
然后分赛场呢它的占用内存比较小,而且它的profile功能会比那会比pro那个会好很多,我给大家演示一下,但他普罗非要大家以那个管理员权限打开。
然后一样的把这个working的启动起来。
然后抓一下capture,抓一帧,ok然后基本上跟这个是一样的,然后它会有一个我们这个run pass,大概是我们会看到我pass,然后多少时间多少多少毫秒,以毫秒为算。
然后包括我们这个每次交换之间的cpu端的差距,跟我们的那个gpu的时间,它都会有一个非常准确的分析,包括在你的时间轴上,因为它的精确到小数点后两位的毫秒,所以精度要比那个乱刀和高很多。
然后最重要的是呢,你可以对你的这个命令做性能分析。
呃做你的性能分析,然后一般来说我们做性能分析只需要看这个图,其实就可以了,一般来说你的性能分析可能是某一个阶段,某一个地方卡住了,当这个绿色的能量条它被吃满了,那么证明你的这个阶段是性能瓶颈。
那么你就要考虑一下如何去降低的这个命令,然后下面会有很多的呃l2 ,包括你的那个shader,然后你的shader也是可以做你的profile,这个有有兴趣大家可以自己去研究研究嗯,如果要做性能分析啊。
恰好你的显卡呢是个英伟达显卡,那么这个工具是。
我觉得目前是最好的一个工具了,然后还有一些是高通的snp,可dragon profile,它是当时也是一样的,你得是个高通的手机,你才能去对它做性能分析,然后这边我时间有限。
我也就不演示了,还有一个是arm studio,absurd的那个graphic classic的这一部分,它也是可以,它不能像那个insight非常精确的说,每一个交错它占有多少。
它的好处是可以记录下你的用端的,从开始下面所有的命令他都会帮你记记录下来,然后arm studio它也有一个,一个专门用于做与性能分析的,它的性能分析呢跟这边会不太一样。
它是记录下你的那个传感器的那个占用的多少,然后,他会记录下你的传感器占用多少,然后最后会以你的那个你要自己去根据,那个传感器反馈给你的结果,你去看一下你的那个是怎么样,你的这个性能到底是怎么样的。
嗯当然这种东西我觉得没那么直观了,你是一个非常有经验的程序员,你才可以选择用它们,然后去对你的东西做性能的判断,ok然后今天的课就到此为止,谢谢然后我留五分钟吧,大家有什么想问的。
可以在评论区直接弹幕上直接发,留,留几分钟,大家有什么想问的,可以直接在弹幕上发一下,我,我回答一下,哦作业里那个模型没有skin这个同学那个模型。
你把你的代码再更新一下,因为我已经改改过了。
我原来的这个作业呢是根据这边的gtf的sample,改的,只有tf loading loading的,原来是于通过这个模型来做的,所以说你会看不到它,然后,我不知道这位同学看到的是不是这个这个模型。
所以这个模型是没有是没有骨骼,是没有动画,因为它是静态模型,然后我们的homework我已经做了修改了,现在我们的homework,homework一用到的是这个模型,这个模型是由骨骼有动画的可以。
所以同学不用担心这事情,然后最终的效果呢要做成什么样子呢,大家可以在,我们的作业要求里面去看一下。
它是有一个哦,对最终渲染出来得是个这样的东西比较好。
哦这个应该是个刚体动画吧,这个我也没有仔细去看过这个模型,我只是看了一下,他好像有动画吧,然后可能只是个刚体动画哦,有同学说只是个刚体的话,那应该就是个刚体的话,那没有关系,那大家就按照刚体动画来做吧。
这个没有关系,只要最后能够显示出这个这样的东西就可以了。
或者说能够按照这样子的最后显示出有自发光。
然后pp r材质正确,然后就可以了啊,有同学问amd有没有gpu调试工具也是有的,其实大家只需要在working的官网上去搜索一下,嗯amd也有它的工具,因为我的显卡不是amd的,所以我这边就没装了。
大家可以在那个working的官网上去看一下,有没有m也会有amd的cpu调试呃,但我这边好像上不去了,这个网站。
哦对大家可以看一下,emd的就是这个他的有个gpu profile。
呃有同学问这个模型有透明材质吗,这个我还真没有看过,看起来可能是看起来应该是没有的,但我不知道他的这个叶子,是他这个风扇是不是透明材质做的,还是说直接用模型做的,这我就没有仔细看过。
ok好那我们今天的课程就到这里了。
然后我们下个课时会讲walking的同步,还有基于用端的一些常见的优化和实践。