对我来说,每天能过得心情舒畅,有酒喝有美味佳肴吃,必要时工作一会儿,晚上睡得舒舒服服,就行了。 ——迪希亚

说实话我已经记不清上一篇文章是什么时候的事情了,感觉得有好几个月了,但其实我是在上篇文章结束就开始学习Pbr相关的东西了,只不过因为某些智障问题,导致我IBL的生成Irradiance贴图的部分一直是错误的,直到29号写完预滤波贴图的部分才发现自己前面哪里写错了,那是真的智障。不过好在赶在31号之前把全部的代码都写完了,也算是在去年完成的了。

还是这个仓库:

FREEstriker/Air_TileBasedForward (github.com)https://github.com/FREEstriker/Air_TileBasedForward

效果大概是这样的:

PBR+IBL - 知乎 (zhihu.com)https://www.zhihu.com/zvideo/1592840606350598145

切分的背景图:

生成的IrradianceMap:

至于为什么这么亮,我只能说原始的hdr图片里的那两个白光灯光照实在是太强了

生成的五级粗糙度的PrefilteredEnvironmentMap:

为了方便看,就放大了一点

生成的R32G32_SFLOAT格式的LutMap和压缩打包成R8G8B8A8_UINT格式存储用的LutMap:

踩到的坑

这几个问题真实困扰了好久,不过还好都解决了,哈哈哈哈。

半球采样

​用Hammersley进行半球采样的时候,抄错了。。。计算cosTheta的地方忘开平方了,导致算出来的IrradianceMap很奇怪,好像对也好像有点问题,看了两个月都没找到是这里写错了,一直是以为采样时空间转换哪里写错了,我真傻真的。

错误的IrradianceMap

就这,卡了一个多月。。。

QT的VulkanWindow

生成IrradianceMap的时候,我发现如果我像LearnOpenGL那样直接画一次就出结果的话,我的电脑会直接黑屏报显卡丢失的错误,似乎是占用显卡太长时间,导致系统以为显卡卡死了,就重置了,所以就只好使用分片的方式,一次只画一部分,分到几百帧里去画完。

但是到生成PrefilteredEnvironmentMap的时候,我发现如果要分片的话,最好使用一个StorageBuffer来存储Weight,这就需要原子操作了(但其实并没有必要,后面也没有用到原子操作)。但我在实际使用AtomicAdd时总会提示我打开拓展,但实际上我已经打开了拓展,并且通过打印支持的拓展可知,3060确实是支持atomic_float拓展的,非常奇怪。

开启拓展

支持拓展

上google查了查,发现似乎要设置一个VkPhysicalDeviceShaderAtomicFloatFeaturesEXT的结构体并且把它绑在VkDeviceCreateInfo的pNext上才行,但是QT并没有提供这样的接口,查了文档也没有。但我在QT6.4的手册里发现似乎有这个设置pNext的功能:

于是我把QT升到了6.4,然并卵,依然不能设置pNext,提供的setEnabledFeaturesModifier接口只能设置Vulkan1.0的Feature。我以为是我查手册的姿势不对,就又去翻QT6.4的源代码了:

​我只能说,毫无pNext的痕迹,所以如果想要使用atomic_float拓展,就只能改QT的源码再编译个自己的QT版本出来,我只是尝试编译了一下,反正6.4我是怎么都编不过,但是5.15编了一下午确实编完了,想想还是算了,干脆用个ComputeShader搞了一下Weight,后面会详细说,改QT什么的,还是留到下个大版本吧。。。反正现在也不是不能用。

FreeImage库

大多数Vulkan实现都是不提供RGB格式的,只提供RGBA格式,之前确实是问题不大的,基本直接就加载的png格式的带有a通道的贴图。但是要是加载EXR格式的HDR图片时,就有问题了,缺了个a通道,导致不能直接从内存拷贝到显存里,所以就需要先把它转换成RGBA在放在R32G32B32A32_SFLOAT格式的显存里。FreeImage库里确实存在这样的接口:

但是我从网上下载了许多的HDR图片,每一张在RenderDoc里查看它虽然是R32G32B32A32_SFLOAT的,可最大值都是1.00,完全没有HDR图片的样子,很是离谱,不管找多少图片都这样。但是去PhotoShop里查看的话确实是一张HDR图片,似乎是完全没有问题的。

没办法,就只能有去查手册了:

​好像说了,又好像没说,没办法,还是看源码吧:

​只能说真像大白了,我是没搞懂你RGBF转RGBAF还要搞个Clamp是做什么,RGBAF转RGBF也是一样的情况,挺无厘头的。

只能改了在编译一遍看看,去掉Clamp果然对了。。。

坐标系问题

虽然已经在前面的文章里每一篇都会提到坐标系的问题,但这次又出现了,我也没办法,我是真的麻了。刚开始的问题出在每次切分生成背景立方体贴图时,生成的6张图并不能组成向上面一样的十字图,有两张图总是需要翻转一下,剩下的图需要移动一次,很是离谱。

找了半天,发现大家用的世界坐标系的底平面都是XZ平面,而我由于高中留下来的习惯,我更习惯与使用XY底面Z向上的右手系,所以之前就都用的这个坐标系。而开启了VK_KHR_maintenance1拓展并设置后,NDC空间变成了和OpenGL一样的左手系,立方体贴图似乎就是照着OpenGL的NDC坐标系定义的。

我之前似乎是直接用片元的世界坐标系当做采样方向直接采样,生成时也是采用的类似操作,所以切分结果总是差点。之所以现在才发现这个问题,是因为我之前以为我用背景立方体贴图画背景的时候是由于Vulkan的某些差异造成的图片错位,就直接用PhotoShop修改了一下,满足显示需求就算蒙混过关了。

未启用VK_KHR_maintenance1的NDC

所以最后我把世界坐标系改乘了XZ为底面Y向上的右手系,而采样背景立方体贴图时,直接把片元的世界坐标的Z加个负号,这样就变成和OpenGL的NDC坐标系一样的了,直接这样采样就可以得到正确的结果了,且切分出来的六张图也可以拼成一张十字的图片。

Physical Based Rendering

虽然很早就知道有PBR这么个东西,但一直都没一仔细看过它的原理,反正就是很nb就是了。仔细看过之后,其实大部分都是已经确定的东西,只有少部分是需要推导的,反正最后的效果确实是比之前好了很多。

原理

​主要就是这个式子,等号右边的左半部分是漫反射,右半部分是镜面反射,需要了解的点包括立体角、菲涅尔、法线分布函数与几何分布函数。

立体角的部分可以看这个,写的很详细了:

漫反射部分是很简单的,kd就是漫反射所占的比例,一般就是用 来定义,c就是反射平面三通道的Albedo,之所以需要除以一个π是因为漫反射的能量会被分到法线半球上,最后的Li*(n·wi)就是把光照转换到法线方向上。

镜面反射的部分略微有点复杂:

F项就是菲涅尔项,大概就是下面这样的东西,视角固定时,光线越接近法线,反射效应越强,反之反射越弱。

​一般就用的是这个公式: 。其中,F0就是物体本身的反射率,所以应该是三通道的,是可以根据材质查表得到的,h是Light和View的中间向量,v就是View,有点类似于Blinn-Phong,大概知道是这样定义的就可以了。

​但是实际使用中,F0是通过这样一个式子所定义的: ,之所以是0.04是因为绝缘体的F0就是0.04,而albedo就是对应材质的反射率,通过一个metalness参数进行插值,就是比较灵活吧。

G就是几何函数的部分,由微平面理论可知,当光线入射和出射时,都会被遮挡掉一部分,几何函数所作的就是计算出最后出射的光线占入射光线的比例,少掉的光线就代表被遮挡掉了。

用的就是上面的公式,越粗糙,损耗的光线就越多,而最后将入射的有效比例乘上出射的有效比例,就得到了总的光照的出射比例。

D项是法线分布函数部分,这部分感觉非常有道理的样子,原理还是基于微平面理论:

当视线位置一定时,单位宏观平面内只有微观法向朝向h的微平面才会将入射光反射到摄像机里,法线分布函数基本就可以理解为单位宏观平面上微表面朝向h的比例,有了这个比例,就可以对光线进行衰减了。

总的来说,D、F、G这三个参数就是控制反射强度的,都是[0, 1],通过使用albedo、roughness、metalness三个参数来控制,很有道理的样子。问题就只剩下镜面反射部分的分母部分是怎么出来的了,这部分我是在这里学习到的,写得非常清楚:

具体物理单位相关的可以看这个:

我自己又整理了一下加强记忆:

实现

既然原理很明确了,实现起来就没有什么太大的问题了,而且之前本来就在灯光参数里加入了Color和Intensity参数,所以只需要改光照部分的GLSL代码就可以了。

shader接收的

Light基类定义的

但是偶然间我发现似乎GLSL的函数传参用的似乎是拷贝传参,这就很难顶了,因为我之前直接把一大堆参数不管有用没用全给传过去了,且内部经过检查灯光类型后,会进行一次分发,在使用一次拷贝传参:

​很浪费啊,感觉没必要,所以为了避免拷贝,我干脆就把PBR的计算用宏写了。

通用的部分就是公式的代码化:

通用的计算

不同灯光的强度需要做不同的处理,之前的文章已经说过很多次了,就是抄的RTR4里的公式:

不同灯光不同的计算

最后在用一个函数包起来,用来分发使用不同灯光函数,所以说基本就是去掉了一次拷贝:

没有太多可说的其实,和IBL比很简单。

在实际使用时,传入的Roughness和Metallic参数都是通过采样贴图得到的,并且最后还需要一个ao贴图来描述这个材质的自遮挡,所以需要一张三通道Albedo、一张单通道Roughness、一张单通道Metallic、一张单通道AO,这些贴图都是需要美术来提供的,所以我直接上网随便找了一个材质。

考虑到其中三张图都是单通道的,不如直接把这三张图合成一张三通道的图,需要考虑到的是,这三张图都是线性空间的,所以使用PhotoShop时需要一些额外的颜色空间操作才能正确的存储,最后合并出来的图就是这个样子的:

Roughness-Metallic-Occlusion贴图

最后再片元着色器里直接采样贴图传参就可以了:

Image Based Lighting

因为IBL也是用了BRDF,所以当时想着就一起写了算了,IBL的推导部分比较麻烦一些,实现起来也很麻烦。我是学习了LearnOpenGL上的教程,并结合网上的一些讲解完成的,原理推导主要是学习的这一篇文章,写的非常详细:

原理

拟蒙特卡洛积分:

本来的蒙特卡洛积分是这样的:

pdf是概率密度函数

所以如果我们要对法线半球做一些积分操作的话,就可以利用这个式子让它变成某种求和公式。至于生成随机样本,使用一个叫做低差异序列的东西,如果使用低差异序列生成的样本带入到蒙特卡洛积分中,这个东西就叫做拟蒙特卡洛了,大概就是这样。

生成低差异序列

低差异序列生成的半球样本是这样的

漫反射:

​观察等式右边的左半部分,c和π都是常量,没有问题。问题出在 上, ,其中 ,所以说 与h、v、metallic、albedo相关,在渲染时才能确定,所以最好把 给分到外面,预生成一称为IrradianceMap的图。但F中的h是与 相关的,需要把这部分做一个近似,所以就有了这样一个东西: ,相当于用 替换了原来的1,并且将coshv换成了cosnv,这样就可以将 完全搬出积分,只剩下对环境贴图的积分了,所以说漫反射的部分就变成了这样:

积分左边在实时渲染时确定,积分则使用拟蒙特卡洛对环境贴图预积分生成,实时渲染时根据片元法线采样就可以了。预生成时可以使用余弦权重的半球采样,也就是 ,所以上式就可以换成这样的形式:

所以预生成时只需要用N个样本来采样环境贴图,求和再平均就可以了。

镜面反射:

镜面反射部分用的是一个叫做spilt sum approximation的近似,把高光反射部分公式中的 给拆出去,成为这样的式子:

左边称为PrefilteredEnvironmentMap,右边可以将系数预计算到一张图上称为LutMap。

​至于推导中的pdf为什么要取这样一个值,我并没有深究,不过知乎上确实是有人讲这个东西的是可以推导出来的。这个pdf是与Roughness相关的,视角固定,不同Roughness的情况下,造成贡献的光线范围是有所差异的,大概是这样的感觉:

光线波瓣内的光线贡献光照

用这个pdf生成半球样本的话,就可以用Roughness控制波瓣大小了:

Roughness从0到1,从点到半球

而由于推出的 里面还有 、 这种东西,所以这里做了一种简化,在预计算时直接将采样方向上光线波瓣的值做积分,这样View、Normal就可以认为是一样的了。

简化模型

但在实际积分中,pdf确定的波瓣是H的范围,再通过View和H计算出入射的光线,从而得到光线波瓣,也算是微平面理论吧。

实际积分中的波瓣应用

既然Normal、View都一样了,就可以继续化简 了:

至于为什么 可以直接被 替换,似乎是因为F对结果影响不大,剩下的部分分布和值域也很类似,就直接换了。

这样就可以根据不同的粗糙度确定波瓣的大小,计算出一张PrefilteredEnvironmentMap,并且因为越粗糙就越模糊,正好和Mipmap很合得来,所以干脆就把每个粗糙度的结果存入对应的Mipmap就好了,实时渲染时直接使用 作为采样Mipmap的参数就好了。

去掉 就只剩下 这部分了,由于这个里面的 里面的F项带有很多实时渲染时才能确定下来的参数,所以需要对这个式子做一下整理,再把F项分到外面。

​在最后的A和B中除了 相关的样本,还剩下分母中的 、G中的Roughness,那么用这两个变量作为uv来绘制一张A在G通道、B在G通道的图就可以了,使用时直接根据 和Roughness来采样,在使用 进行计算就可以了。实际积分中也用到了前面提到的H波瓣计算光线波瓣的方法。

组合

有了上面出来的IrradianceMap、PrefilteredEnvironmentMap、LutMap,就可以在实时渲染中进行渲染了。需要根据法线N与视线V计算出可被观测到的光线L,因为漫反射部分预计算时是根据法线半球计算的,所以直接用N采样IrradianceMap就好了,而镜面反射部分在预计算时用光线波瓣做了积分,所以直接用L采样PrefilteredEnvironmentMap就可以了,最后根据 与roughness采样LutMap再合上 就可以了。最后组合出的公式应该是这样的( 、 的具体计算省略):

这样有了理论的支撑就可以开始实际的编码了。

修改框架

之前对VkImage与VkImageView的了解很模糊,只知道必须给Image绑一个ImageView才能访问,所以在封装Image类时就直接构造的时候申请一个VkImage和VkImageView,在生命周期内是不能够更改的,而且之前也没有用到Mipmap,无论是2D还是Cube一开始就确定了。

但是实际上,VkImageView和VkImage是“一对多”的关系。一个VkImage里会存有多张二维图片,一张称为一个Layer,每张图片还可以有多个Mipmap等级,不过每个一个VkImage里每个Layer的Level等级都是一样的。而VkImageView的作用则是设定以哪种方式来看这张VkImage。比如说:VkImageView0就只看Layer0-1Mipmap1-2,就是一个带1-2级Mipmap的长度为2的图片数组;VkImageView1就是单纯的一张二维图片;VkImageView2就是长度为3的只有第0级Mipmap的图片数组。

​所以我将封装的Image类里的一个VkImageView成员变量改成了一个以字符串为键的字典表,这样就可以通过视图名来访问Image拥有的VkImage的某个VkImageView了。

将VkImageView相关的信息都存入map中

通过视图名访问VkImageView

当想要对一个Image添加Barrier的时候,也是需要划定一个Barrier同步的VkImage区域的,我就直接偷懒使用上面提到的存储的VkImageView相关信息来填充Barrier的相关信息,所以要添加Barrier也需要添加一个对应的视图。

指定一个视图名,对象以资源添加Barrier

但其实这是不必要的,正确的做法应该是额外添加一个对VkImage分组的数据结构“ImageGroup”:如果需要添加Barrier就直接申请一个ImageGroup,只有相关信息并不生成VkImageView;如果需要一个视图,就先申请一个ImageGroup,再根据其中记录的Layer和Mipmap信息创建一个VkImageView。这样就不需要创建无用的VkImageView了。之后再改吧反正。

大概就是这样的感觉

除此之外,之前设计的资源系统也是有点问题的,除了Image这个类,还加了Texture2D的类用于加载资源,内部使用的就是封装后的Image,功能其实是重合了的,应该把加载用的相关信息放在AssetBase基类中,然后让Image直接继承AssetBase就好了,所以Material、AssetManager相关的地方就都改成了Image了,完全去掉了Texture2D这个东西。

直接继承AssetBase

考虑到IBL的步骤里可能会需要直接加载带有Mipmap的图片或直接生成一部分Mipmap,所以重写了Image的加载部分,并在json文件里添加了相关信息字段:

mipmapLayerSourcePaths控制需要加载的源图像路径

autoGenerateMipmap控制自动生成Mipmap

所以在加载时需要先把相应路径的图片加载到Buffer中,在拷贝到对应Layer对应Mipmap中,之后再根据设定使用Blit方法生成剩下的Mipmap,也就比之前多了两步而已。

先拷贝有源图像的Layer与Mipmap

再使用Blit生成剩余的Mipmap

后面应该会再完善一下。

切分立方体贴图

大部分HDR源图像都是一张等距柱状投影图,大概就是这样的:

而一般渲染时用的是立方体贴图,所以第一步就是先把一张等距柱状投影图切成六张的立方体贴图,便于后续过程的使用。切分时就使用一个fov为90°Attachment为正方形的相机,在原点上旋转朝向到六个面的中心,渲染一个立方体,就使用这个立方体的坐标作为采样的方向,至于如何采样等距柱状投影图,我是直接抄的LearnOpenGL上的公式。

抄来的公式

这样就可以直接开切了,需要切出来两种格式:一种线性空间的,用于画背景,需要做一个ToneMapping,直接存成png就好了;一种HDR格式的,用于生成IBL需要的其他阶段的源图像,单纯只是切分,我是存储成exr格式了。

生成的操作已经不属于实时渲染了,应该单独分出去成为一个资源生成管线,以在编辑器下单独使用,但这里我实在懒得分出来了,就直接在当前使用的Pipeline里添加了一个“BuildAssetRenderer”,并添加了一些RenderFeature用于不同资源的生成,所以直接将场景内的渲染器切换成“BuildAssetRenderer”就好了,但是窗口还是要显示东西的,所以就单纯的画个背景上去暂时将就用着。之后应该会重写一部分内容,把资源生成分出去成为一个资源生成管线。

添加BuildAssetRenderer渲染器

在BuildAssetRenderer渲染器内使用不同的RenderFeature生成不同资源

而且我也没有写图像存储的相关操作,主要是Vulkan不怎么支持RGB纹理,所以写起来会非常的麻烦,所以干脆就直接在RenderDoc里截个帧,把渲染到Attachment上的内容手动保存下来。。。

ToneMapping

至于ToneMapping,我是直接用的最简单的 。并且需要注意的是,RenderDoc里保存的是线性空间的图,所以加载时注意一下格式。

背景贴图

Hdr贴图

最后的生成结果应该是没有问题的。

生成IrradianceMap

和上面切分立方体贴图时一样,也是将摄像机转六个方向,每次渲染一个立方体,将立方图的片元位置直接作为采样的法向,然后在法向半球内运用上面的公式进行计算就好了。

由于用低差异序列生成出的采样方向是单位半球内的,所以需要将单位半球内的采样方向换算到法向半球中,之所以要额外计算一个up的原因是避免两个近似的向量做叉乘操作造成错误。

计算转换矩阵

还有另外一个问题,就是当单次计算生成的样本数过多,我反正是会黑屏报错,不知道是Vulkan的问题还是QT窗口的问题,所以我将计算分到了多帧上,一次计算一部分,这就需要一个东西来保存每次计算出的结果。观察一下这部分公式: ,直接把N拆开就好了,变成这样: 外边的是分帧,里面的是每次的样本总数。而存储的话,就直接使用Shader的Blend功能,设置成 就可以将每帧的结果累加起来了。

需要传入总步数、切分帧数和当前帧的索引

根据传入的数据还原出当前样本在总样本中的索引

半球采样

获取样本、采样、除以样本总数、设置srcColor

设置Blend参数

我直接狂采1048576次,呜呼!

生成PrefilteredEnvironmentMap

先观察一下式子: 显然不能像IrradianceMap那样直接切分,因为分母是相关的,所以最好分两步:先分帧累加分子,顺便把分子给记录下来;再将分子分母相除得到结果。我是用一个StorageBuffer来存储的分母的,在每帧的第0级Mipmap的第0面的第0号像素计算时将分母写入Buffer然后这一帧结束后因为已经对CommandBuffer执行了WaitFence了,所以下一帧也不需要同步资源,直接继续就好了。

所以第[0, stepCount-1]帧只做累加操作,且将分母累加入StorageBuffer。第stepCount帧做分子分母相除的操作。第[stepCount + 1, ]帧就单纯的discard掉片元单纯的保留Attachment让我们能够在RenderDoc里保存图片就可以了。

累加分子与分母

相除与discard

着了我将Blend设置为了 ,这样在累加阶段把srcAlpha设置成1就可以累加,在相除阶段将srcAlpha设置成分母的倒数就好了。

Blend设置

由于这部分在原理上涉及到波瓣之类的,所以单纯的半球采样需要换成与Roughness有关的重要性采样得到H,再由H得到需要采样的光线方向。

根据H计算L

但这里在实际采样的时候还用到了Mipmap,原理大概是概率低的样本采样需要大一些这样更平均一些,正好就采样更高层级的Mipmap,而概率高的样本采样小一点这样更精准,正好采样低层级的Mipmap,具体的说法可以看这里的20.4:

​通过上面这样一个公式计算出Mipmap登记后,直接采样就可以了。

​其他的就没什么了,保存的时候要拔全部的Layer和Mipmap都存成exr格式才行,所以json才会这么长:

最后也是狂采1048576次,但其实Roughness0的图直接用环境HDR图Blit过来就可以了,没有差别的,只是我懒得再改了,并且Mipmap层级高了以后样本数也可以减少一些的,因为本身需要的分辨率低了很多,不过我也懒得去改了。。。

生成LutMap

这部分就比较简单了。不用画6个面也不用画Mipmap,而且因为相当于就是在法向半球上做的预计算,所以直接在单位半球上计算就可以,转换矩阵也不需要了。

只需要累加就可以了

这里也需要根据Roughness生成H,单位半球所以Normal就是(0, 0, 1),根据输入的 也可以计算出View来,所以算就行了。分片和之前一样,Blend还是累加的设置。

采样计算并累加

而至于输入的参数 和Roughness,我是直接用gl_FragCoord除以Attachment的分辨率来获得的原点在左上角的uv坐标,分别作为 和Roughness就好了。

根据像素位置计算输入参数并反算View

这样就可以直接算出来了,不过考虑到R32G32_SFLOAT格式的Attachment并不是很好存储,所以我就又做了一次打包操作,先使用packHalf2x16将A、B两个float打包成一个uint,再将这个uint由高位到低位存储在一张R8G8B8A8_UINT的RGBA四个通道上,而R8G8B8A8_UINT可以直接存成png格式的图片,这就很方便了,只不过实时渲染使用的时候需要多一次解包的操作。

先采样再打包

​精度损失其实问题不大,毕竟是half,位数还是足够的,并且这个IBL的近似本身误差就大,完全不差这一点点。写一个解包的完全看不出来有区别。

source、pack、unpack

组合

已经把预计算的算完了,实时渲染只需要算一些当时才能确定的部分,照着公式组合起来就好了。

这里我也写成了宏定义的形式,最后再把这部分和之前的PBR结合起来就好了。

组合PBR和IBL

最后的效果也还行,不过可能是PrefilteredEnvironmentMap分辨率只有512*512的关系,反射强的部分模糊点,有锯齿。不过确实比之前最简单的Phong模型漂亮多了,真不错。

:||

总算是快写完了,这篇总结我都写了得有三四天了,虽然大多时候都在摸鱼。。。不过这部分确实比以前实现的部分麻烦很多,主要是理论部分很难彻底搞懂,我现在还有部分细节不是特别清楚,不过也算是差不多了。其实11月的时候还是很难过的,主要IrradianceMap部分最后的效果一直有点问题,低差异序列部分的bug一直没找出来,并且实验室的项目也是做不下去,完全不是代码上的问题,甲方配套的设备真的是有点烂,好在现在PBR+IBL这部分算是告一段落了,也不算是两手空空吧。

我一直再考虑要不要把这个Vulkan渲染器重写一遍,最近也在找一些好用的库了,准备变得nb一点,主要是现在想加一些需要预计算的东西很难加,而且这个框架已经是半年多前写的了,虽然一直修修补补的,但感觉也到了另起炉灶的时候了,上个月跑去学习了一下EffectiveC++,感觉也是挺nb的,之前自己写的都是一些比较朴素的设计方法,总之就是肯定会重写了。

之后应该会先继续在这个代码库做一些实验性的修改,只做验证用了,毕竟现在的Shader-Material-RenderPass-FrameBuffer简直是一坨狗食,之前说的合批、优化DescriptorSet也还没做,可能就会先试下这两个了。GI、SSR可能会在重写过后在新的代码库里做了,还想加一个简单的编辑器界面让它看起来更nb一点。

今年中旬也该投简历找工作了,幸好最近已经没有像去年暑假那样焦虑了,继续前进吧!

Vulkan PBR与IBL实践相关推荐

  1. DirectX12(D3D12)基础教程(二十一)—— PBR:IBL 的数学原理(1/5)

    目录 1.前言 1.1.一些感慨 1.2.运行效果展示 1.3.示例简介 1.4.示例操作说明 1.5.本章内容的简述 2.什么是IBL 2.1."Cook-Torrance" 模 ...

  2. LearnOpenGL学习笔记—PBR:IBL

    LearnOpenGL学习笔记-PBR:IBL 0 引入 1 渲染方程的求解 2 hdr文件转成cubemap 3 预计算漫反射积分 4 预计算镜面反射积分 4.1 预滤波HDR环境贴图 4.1.1 ...

  3. PBR之IBL和球谐的梳理

    @ PBR之IBL和球谐的梳理 其实pbr直接光输出的结果看起来都差不多,重点在于间接光的计算,因此引入了IBL和球谐. 基础 首先是反射方程: Lo(p,wo)=∫Ωfr(p,wi,wo)Li(p, ...

  4. DirectX12(D3D12)基础教程(二十一)—— PBR:IBL 的数学原理(3/5)漫反射积分项

    目录 3.4.漫反射项的重要性采样计算 3.4.1.漫反射项的二重积分形式极其近似预积分计算 3.4.2.漫反射辐照度积分项的直接积分计算 3.4.3.漫反射辐照度积分项的蒙特卡洛积分重要性采样计算 ...

  5. 基于物理的渲染PBR(二):挑战手写pbr和IBL环境光部分的见解

    手写PBR 在又经过一个星期左右的学习下,我对PBR的了解也有了一定的进展,所以我尝试了一下手写一套较为基础的pbr,不过对比于Unity自带的Standard来说,自己所写的肯定是略显粗糙的.Uni ...

  6. DirectX12(D3D12)基础教程(二十二) ——HDR IBL 等距柱面环境光源加载和解算及 GS 一次性渲染到 CubeMap

    前序文章目录 DirectX12(D3D12)基础教程(一)--基础教程 DirectX12(D3D12)基础教程(二)--理解根签名.初识显存管理和加载纹理.理解资源屏障 DirectX12(D3D ...

  7. 图形 2.4 传统经验光照模型详解(PBR光照计算公式介绍)

    参考视频: 图形 2.4 传统经验光照模型详解 GAMES101-现代计算机图形学入门-闫令琪 P15 参考资料: PBR-learnopengl 彻底看懂PBR/BRDF方程-知乎 辐射强度.辐亮度 ...

  8. PBR+unity 基础知识

    原文地址:https://zhuanlan.zhihu.com/p/33464301?utm_medium=social&utm_source=qq 猴子都能看懂的PBR(才怪) 也不知道怎么 ...

  9. 猴子都能看懂的PBR(才怪)

    原文地址:https://zhuanlan.zhihu.com/p/33464301?utm_medium=social&utm_source=qq 猴子都能看懂的PBR(才怪) 也不知道怎么 ...

最新文章

  1. linux给用户写任务计划,linux——计划任务
  2. 【ORACLE技术嘉年华PPT】MySQL压力测试经验
  3. 金融科技创业公司Revolut增加对BCH和XRP支持
  4. “物联网+云平台”的实验室管理方案,瞄准的是生物医药和化工行业
  5. 世上最伟大的十个公式,质能方程排名第五
  6. mac中 安装mysql无法启动_Mac 下安装MySQL(dmg方式),无法启动
  7. python 成语库_README.md · 天宇之游/一个python的TK猜成语游戏 - Gitee.com
  8. 1400协议是什么和28181区别_gb28181协议常见流程简析
  9. 用TensorFlow为图片添加字幕
  10. C++ 一维高斯积分的实现
  11. 上三角、下三角、对称矩阵
  12. linux服务器有电信和网通,Linux 双网关(电信与联通)
  13. 渥太华大学计算机科学,加拿大渥太华大学计算机科学排名第四
  14. linux系统下一页,linux下一页
  15. vue 使用Dialog对话框使用过程中出现灰色遮罩问题
  16. .max文件导入Unity出现异常
  17. html实现展开余下全文多个,DIV+css内容太长,实现点击展开余下全文
  18. Android Sensor感应器介绍,获取用户移动方向,指南针原理
  19. 小白学习Spring(二)
  20. PHP之微信头像加水印

热门文章

  1. 如何用c语言输出太阳图案,闪亮的太阳上色简笔画图片教程步骤
  2. 基于微信小程序的跳蚤市场(二手商城)
  3. 【Word】添加续表和标题
  4. Vivado hilbert变换的实现
  5. 玩转C语言for循环
  6. 【干货】微视短视频去水印软件使用介绍
  7. yum安装Apache Web Server命令
  8. spring boot 是真滴好用
  9. 企业微信如何设置群活码?
  10. 每天和琦琦学点新知识_爬虫篇001