
这里首先说明了一个问题,就是之前远距离观察的时候,我们的纹素被缩小,针对这个问题我们可以利用mipmap很好的解决它,mipmap会带来一些模糊效果,但是这个完全没有问题,因为本来就是远距离的东西,看上去有些模糊,看不到很清楚的细节也是很正常的。
还有一个问题就是近距离观察的时候,近距离观察的时候,纹素被放大,纹素放大了,我们像素的颜色值采用的是插值的方式,这么一插值会让最终的结果不再锯齿了,但是会带来模糊,而我们近处观看的时候本该是看到一些清晰的细节的,但是看到的确实模糊,这一点就有问题了。
解决这个就是增大纹素的密度,这里有两种方式,一种是直接对图片进行处理,整一个高分辨率的图,但是这回导致内存消耗,而且远距离情况下就没意义了,就浪费了。
其次是增大采样时纹素的密度,也就是利用纹理平铺tile的方式。、
利用平铺,在近距离的时候,问题就被解决了,他虽然是repeat的,但是近距离一叶障目,你看到的范围本身就小,而这时候的纹理清晰度也是够的。
然而问题就是,当我们利用平铺,把距离拉远的时候,他就会过渡重复显得很单调,带来这样的一个问题。
所以最终就是:我们需要combine平铺和不平铺的纹理。
(118条消息) 平面(Plane)和四方形(Quad)_caimouse的博客-优快云博客
纹理压缩简介 DXT PVR ETC - 简书 (jianshu.com)
DXT纹理压缩,从中可以看出压缩之后,会有一些颜色丢失,

之前采样两次,其中一次没有使用,编译器优化掉了。这次采样相同的,采样两次都使用了,编译器也进行了优化,只采样一次。
这块最核心的目的就是增加detail。上边说过我们要combine tile和untile。
这里利用tile和untile的纹理采样结果进行相乘,效果上确实靠近我们的理想效果了,但是结果有些变暗了,这里利用了一个相乘去缓解这个变暗,但是直接乘以2,会让整体都变量,并不符合想要的效果。

这里主要理解这个reinterpret,这里所表达的应该是重新解释,重新给他弄系数。
首先这里的细节纹理detail texture指的是那个经过tile的。然后这里对maintex本身要加一个细节。就是通过detail tex去和mainTex相乘来实现的。
这里我们可以把detailtex想象为一个调色纹理,我们主纹理去呈上这一个细节纹理,可以对主纹理的细节进行调整,从而让主纹理有细节内容。
那么这里是如何调节的呢?总的来说 主纹理都去乘以了一个数,这个数是:detail*2,那么意味着,如果detail是纯灰色(0.5)那么主纹理就不会变,而如果是大于0.5,那么就会变亮,小于0.5就会变暗。
这里我们想要控制他的细节如何变量变暗,就意味着,我们需要对我们自己的贴图进行一个控制。
所以就有了下面,以灰色为center的一个detail图,这个就是一部分大于0.5,一部分小于0.5从而让原图有变亮的,有变暗的,这样不久增加了很多的细节么。


这里讨论的detail纹理是否必须是灰度图,也就是黑白灰图。
首先它的本质是对于主纹理图的一个调色器,通过和主纹理进行相乘,对主纹理的颜色进行一个调控。那么既然是调,彩色其实也是可以的,不一定是黑白灰。
但是黑白灰的图片,是更加直观的结果,就是对原来的颜色进行亮暗的处理。确切的来说是Value值的修改。

由于我们需要额外添加一张新的纹理,所以这里有了两套UV,还有这里俩UV是不一样的tiling和offset的。也就意味着需要传给fragment两套UV,在DX中为了节省Register的数量,所以把两套UV合并放在了一个4维向量中存储。
上边说到我们可以手动打包,其中的interpolation的意思并不是插值,仔细看下面的输入参数是interpolator,之前说过,因为像素所有的属性都是插值得来的,所以这里就写的这个。那么上边的意思是插值时传输属性成为瓶颈。
下面也说了,这个插值器是非常少的,也就是10个左右。

这里实现完细节添加之后,其实还有一个问题,就是我们的细节是用于近距离情况的,图像放大的时候能看到细节而不是模糊。
但是当我们距离远了的时候,其实我们是应该把这些细节的东西给去掉的。因为远处就不宜该看到这些细节了。
所以这里需要一种方式来fade掉这个detail,采用的就是之前说过的fadeout mipmap,它本身的作用就是帮我们把图片根据mipmap的level进行不同程度的fade,天作之合的一点是它fade,最终得到的是一个gray,而gray*2之后就是1,所以最后当整个图fade之后,其实就完全回到了我们的maintex,太完美了。
最终输出的颜色来源于三项,一个是main,一个是detail,一个是系数2。现在假设线性空间下main亮度是x,detail是y,那么最开始他们都处于gamma0.45空间,然后如果直接在gamma空间下进行输出,
结果就是 (x^0.45 * y^0.45 * 2)^(2.2)
如果换到线性空间中,结果就会是:(x*y*2)
(我上边最终结果都换算到了线性空间中)
上边我们可以看出,gamma空间下会多一个2^1.2的系数。
所以gamma要比线性空间亮一些。这个是从整体分析。
那么还是按照detail tex作为调色来分析,这里gamma空间下的,其实它是(1/2)^2.2次方作为变亮和变暗的分界,对于线性空间是1/2。前者约等于0.22,所以小于0.22的变暗,大于0.22的变亮。所以大部分都变亮了。所以比线性空间要亮一些。注意这里的比较是:相对于最初始的main,他们分别提亮的多少,gamma提亮的更多,所以整体更亮。
注意我们上边的结论是gamma要比线性亮2^1.2倍,这个和提亮更多更少是不是矛盾,前者表达的是整体比线性亮,后者表达的是提亮的部分更多。
其实只要思考不提亮的部分,也就是变暗的部分,其实gamma变暗的程度很弱,而线性变暗的稍微严重一些,而且提亮的程度也是有差别的。所以他俩提亮变暗调色之后的结果,一个整体上是另外一个的
2^1-2倍完全没问题。
这里还有一个问题,就是我们之前说的gamma不是分界为1/2么,这里咋变了,其实那个1/2是gamma空间时的1/2,这里的(1/2)^2.2次方是线性空间下的。这俩完全是吻合的。
我们同样可以在gamma空间下对两者进行对比,这时候线性空间换到gamma空间他的阈值就是:(1/2)^0.5,这个是大于gamma空间的1/2的,也就是他还是只有少部分变亮了。
以上我们写的xy都是线性空间下的。如果我们改成xy是gamma下的。
那么gamma空间输出结果:(x * y * 2)^2.2
而线性空间,x^2.2 * y^2.2 * 2
所以要想线性和gamma输出的是一样的,就不能在shader中再对颜色乘以2了,而是在shader中乘以2^2.2次方。这样俩就一样了。
分析的时候,抓住核心,就是一个贴图的颜色始终不变,只是在两者空间下切换而已。假设一个空间下的初值,然后可以直接换算到另外一个空间下。直接看shader操作即可。
然后就可以得到他们分别的结果,再次换到一个空间中,对于下面我们不考虑shader之前的东西。shader中就够了,shader中你是线性空间就是该贴图的线性空间的值,gamma就gamma空间的值,这俩值能换算。这样就够了。

线性空间下必须勾选为sRGB那个问题,是因为线性空间时,shader里面算的不是线性值,所以上边和这里没有关系。


对于上边说的,要想让线性和gamma一样就乘以4.59(2^2.2).
unity又帮我们考虑到了这一点。
之前的detail texture只支持一种细节,整个表面都是完全一样的,这个可能对有些情况是合适的,但是对于有些则不行。
那就有了下面的一种方式,利用splat map。用多个细节,然后根据下面的遮罩进行一个选择,然后中间的过渡部分还可以利用插值来实现。

低分辨率,意味着锯齿,有锯齿和没锯齿,区别就是边缘,锯齿意味着边缘过渡是有问题的,那么插值可以解决这一点。
所以说插值了,就不必需要太高的分辨率在这了。

这个东西没找着。版本更新之后就没了。

这一大段,没看太明白。
这一节,其实主要两个东西,一个是detail texture,这个主要是用贴图来给另外一个贴图增加细节。并且远距离的时候再fade,结合了mipmap的使用。
然后是splat texture,这个主要是遮罩,黑白的二分遮罩,还有多个通道的遮罩。
==================================================================================================================================================================================
04课

首先这里,关于那个立方体object法线输出后居然随着视角变化的现象,他说了原因是为了减少drawcall时使用的dynamic batching导致的。但是我实行了以下,我这里根本不会变化。
当然,先知道有这个dynamic batching这回事。

这里注意,他说的dynamic batching的设置,是在player setting里面。也就是file-build setting-player setting。

类似的还有一个static batching,看来这个dynamic和static是说的mesh的属性了。

首先说一件事情的,undo,那就是做了A,undo就是做逆A。而这里是表达两件事情。
这里的意思是,你做了A,然后做了B,这件事情想要undo应该如何。那就是先做B逆,再做A逆。也就是以相反的顺序去做对应的逆步骤。就像鞋子和袜子。

上边说的那个不是正方形的时候才有意义,否则就无效,没看懂啥意思。

这里讲解normal的逆转置的时候挺有意思,直接看出来只有scale的时候需要逆,而旋转不需要逆,平移这些直接忽略。
然后在推导的时候,首先得到了逆矩阵。然后这个逆转置之后就是N。

推导的过程挺有趣的。

最后封装的接口,剖开源码,右乘逆转置的实现。其实可以左乘转置。

两个Unit-length的向量进行插值,得到的不是unit,而是shorter,所以这里必须normalize!!!!!!!!!!!

这里最后还提到了一点,就是这个normalize其实有时候可以不写,首先它不写带来的误差并不会很大,而且可以节省性能,所以如果不写的情况下,效果还是满意的,就可以不写。

这里有一点,我们是可以直接定义光的,如上玩法。
这里看一下两个空间下的对比,其实法线等信息,俩空间下都是一样的,根据不同空间有不同的值,在shader中不同的是他们都把光源写成了010,但是这并不公平,因为线性空间用这个算出的结果,换成gamma空间时,会多加一个0.45次方。对于法线,gamma空间已经加了,对于光照这个,并没有加,而如果变换过去应该加的。所以gamma就暗一些。
这里思考的时候,拿最终的颜色来思考,空间的概念是建立在颜色的变换上,所以我们从输出的颜色值变换到gamma空间。然后把颜色值拆分来思考。

其实这里也有道理,因为他毕竟是写死的裁剪到0到1范围,而max则是万能的一个。所以可能一些i情况下前者更高效。为啥说可能,毕竟他还多做了一个判断,把大于1给裁剪掉。

更牛逼的是这个!

这个其实就是帮我们在内部做了一些判断决策,来根据情况判断之前那俩那个更好。
另外他提了一句精度,就是谈的精度都是移动平台的事情。

这里就是可以把之前的头文件给去掉了。

这里就是这个内置变量的内容,在方向光情况下他是一个向量,在其他光源的情况下是光源的位置,都是用四维向量来存储的。

这里我们写的shader如果不加LightMode,有些情况下是直接全黑的,有些情况下是正常的灯光效果,并且转动当前物体是可以看到光照效果的变动的。
但是更改光照方向是没有任何反应的。不进行更新的。
这就是因为光照的一些信息我们无权访问,无法更新光照方向的信息。

这里又一次提到了精度,PC上这些精度最后都会是float。

注意这里定义的核心,他是描述各种颜色通道被反射的多少的。他和diffuse,漫反射出去的光的颜色之间的区别是什么?
albedo也就是这是一个物体本身的性质的,和具体接收到的光照没有什么关系。

这里的代码可以看出来。
而diffuse上边已经说了,是最终漫反射反射出去的颜色。
我们为什么把纹理贴图直接作为albedo(先不考虑tint,就算考虑,可以把他考虑到贴图里面,看作贴图的一部分,单拉处理其实就是为了便于修改罢了)
你可以这么想,我们有RGB三个分量,而我们实际的纹理贴图记录的是,三种颜色的光反射出去的百分比。
所以那这个反射出去的百分比作为albedo,因为贴图就是代表我们物体本身的颜色,本身的颜色其实就是反射对应的光,所以什么颜色反射什么光,所以颜色可以看作反射率。

和之前diffuse不同的是,这一部分光是直接bounce off,并不进入物体表面。

高光这里他先用反射方向和视线方向来做一个dot,然后来考虑smoothness对高光点大小的影响。

这里明白这个情况就行了,在这种情况下,视线和法线基本上重合,然后这时候光线从背面照射过来,half就有可能和法线有一个小于90度的夹角,而当smoothness非常小的时候,这时候光照面积非常大,这种效果也就比较明显。
这个可以按照他上边说的方法解决,但是这都不重要,待会就换了。

首先说非金属,他们有明显的albedo,其实意思就是反射光的能力很强 in diffuse,而纯金属基本上是没有diffuse或者是albedo的,他有的就是镜面反射。
注意金属的镜面反射光是colored,是着色的,彩色的。而非金属的不是colored,是和光的颜色相关的。这里就认为color是对光进行的一个着色,金属的对光进行着色,那结果就是金属反射带有颜色,而非金属不会对光着色,所以最终反射的就是光本身的颜色作为高光。

把之前的diffuse和specular相加作为最终的颜色的时候,这里会有一些问题,正常情况下,光本身是白色的,也就是物体最亮也就被照射成白色的。
但是我们这里一些情况下出现了过曝,大面积白色的情况,这就不好了。
究其原因,其实就是能量守恒的问题,我们现在直接把颜色看作能量,一个白色光过来满能量。
我们算specular的时候,考虑白色光过来,算diffuse的时候还考虑白色光过来,这就相当于我们把实际的光double了,create light out of nowhere。
所以说为了保证一个能量守恒,我们怎么做呢?就把一份白色光分成两份,一份是高光的Tint,这个我们拿到这个光去乘以一个小于1的系数。然后albedo去乘以1-高光tint,这样albedo本身是小于1的。
所以最后都是小于1的系数乘以这两部分,最终在加一块的时候,一定还是小于1 的。

首先这个是没有高光的时候,漫反射的结果,他是这个颜色,我们按照上边考虑了能量守恒之后,会带来一个问题,如果我们采用一些单色偏向严重的光来作为高光,会有下面效果:


其实原因很简单,拿第一个来说,我们把高光弄成红色,也就是把光的R分量都用于了高光,那么对于albedo就只用GB分量,也就是拿剩下的蓝+绿光去考虑diffuse效果,相当于考虑漫反射时照射过来的是蓝绿光,而这就直接影响albedo结果偏向于蓝绿色。
这不行啊,这不符合我们的想法,我们其实想象的是把白光给量化,我们想的是一份白光,拆成两份,变成两份灰色的。然后分别去作为diffuse和specular去考虑。
这时候如果specular是红色的,那么它只需要从他的灰色那部分中拿走红色分量即可,然后剩下的可以认为是被吸收了。(当然这里说吸收有些不太合理)
而剩下的diffuse在处理的时候,根本不受到高光是什么颜色的影响。
所以就有了下面:

他根据高光最大的某个分量,来决定出一个灰色,然后剩下的灰色交给albedo。

他这里介绍了一个函数,其实这个函数内部做的就是我们上边分析的那些,这里他会额外返回一个值,叫做1减反射率,其实这里成为反射率有些不太合理,首先反射率是物体的概念,不分镜面和漫反射,就是反射出去的能量和入射的能量的比值,或者是功率的比值,这是同理的。
所以说他直接那高光强度,也就是高光灰色那部分作为反射率,没有任何依据的啊。

首先这里我调用了一下,发现和我自己写的不一样效果,而且经过测试发现,他的SpecularStrength返回的应该是r分量,而不是最大的那个分量。
根据源码可以分析,是宏出现了问题,所以我们可以查找一下相关的宏发现:
这个宏默认情况下是2.5的,而上边需要大于3.0才会执行减去最大的分量的那个分支,所以这里#pragma换成大于3.0的就可以看到和自己写的一样的效果了。

我们之前对于一个物体的描述,光照计算是基于specular来对物体进行diffuse和高光计算的,基于specular来对金属和非金属进行区分的。
而这里还存在另外一种方式,来对金属和非金属用另外的属性进行一个区分,就是利用Metallic。
注意我们主要研究的是金属和非金属,处于两者之间的一个过渡的很少,所以说metallic在这里是一个toggle,非黑即白的开关。但是不乏有些处于两者中间的,这里我们把Metallic搞成一个range0到1.
然后就利用这样一个开关来对金属和非金属进行区分的,然后基于这个对diffuse和高光进行计算。

首先他这里给了一个over简化版本,针对金属还是ok的,非金属就有点不合理了。
首先最最重要的是第一行,我们需要知道高光的specularTint,也就高光的反射颜色,对于金属来说,他是非常接近于金属自身的 颜色,也就是金属的albedo。
比如说黄金的高光反射颜色就是金色的。
因此这里就用金属度和albedo相乘作为高光颜色,然后对于diffuse部分,金属是没有的,上边算法,albedo搞完了specularTint下面要作为diffuse计算的一部分,所以处理了一下,这里金属直接就变成0了。所以diffuse就成0了。
说过上边换成非金属就不好使了,因为高光就直接给整没了。下面有Unity提供的函数:

我们看他的实现:

他这里的reflectivity还是specularTint那一部分。(SpecularTint就是高光颜色)
高光颜色是根据非金属或则是电解质的高光颜色和纯金属的高光颜色(albedo)进行一个插值based on metallic得到的。
对于albedo则是和刚刚一样,需要乘以 1-减去高光颜色,这一点就是和之前能量守恒一个思路。
注意这里的高光颜色不是上边直接插值得到的,如果直接用上边插值得到的,那就会出现类似之前一样的情况,

(红色高光,漫反射必然偏蓝绿)
按照之前的思路,我们应该是拿到插值之后的高光颜色,来对rgb三个分量取一个最大值来作为最终的反射率。
而这里并没有使用上边rgb最大值的方式,而是是使用的非金属高光和1之间进行的一个插值,我想应该是,1 > albedo,所以这里就直接拿1来做,这样lerp的结果一定是大于等于实际的specular Color的
所以说再用1减去这个,去乘以albedo作为环境光,是符合能量守恒的。
其实这里也还是可以认为把一个白光分成两份,一个是用于specular,一个是albedo。specular那一份我们没有取确切的,而是取了一个稍微大于他的值。然后剩下的albedo,还是按照原来的方式进行计算。
说实话,这个albedo算的是真混乱,高光很清晰,但是albedo真的很乱,从reflectivity就乱,而且当金属度变化的时候,金属度为0,他的反射率本该是接近0的值,现在变得乱七八糟,反正知道乱就得了,下面也说了他是vague的。注意上边写了一个over简化的版本,他写的也是1-reflective是1-metallic,其实如果这里把漫反射高光看作是1的话,也是这个结果,如果能想明白用1-metallic来做这个的合理性,基本上就通了。
而这一点,其实就很容易通了,因为金属度越强,那albedo部分就越弱,反之,albedo部分就越强。在之前已经说过这一点了。所以完全可以用metallic来代表reflectivity。

可以再去从反射率定义上理解一下这块的反射率,不然完全搭不上边,还叫这个名字,没道理啊??
或者是说,他这里的反射率从头到尾一直指的都是 高光反射能量/进来光的总能量。
关于gamma空间的额

最后再回头看这个俩种工作流的对比。

其实两种工作流的核心思路都是:能量守恒,总的颜色是1,然后分成高光色,1-高光就是diffuse部分。

最后这里介绍了PBS的计算。这个不同于之前的两种工作流的介绍,

工作流介绍的只是如何去计算specularTint和Albedo这两个部分,而PBS解决的则是计算出来这俩之后,如何去计算diffuse和specular以及我们还没有涉及的间接光部分。


他这个PBS是分不同的level的,不同level之间效果上会有差异。
博客围绕纹理处理和光照计算展开。纹理处理方面,探讨了mipmap解决远距离纹素缩小、纹理平铺解决近距离纹素放大问题,还提及结合平铺与不平铺纹理、添加细节纹理及fade掉细节等。光照计算方面,分析了法线逆转置、不同空间下光照对比、能量守恒及PBS计算等内容。

被折叠的 条评论
为什么被折叠?



