WHCSRL 技术网

unity shader 基础之十一 动画、屏幕后处理、深度纹理

11.1 时间变量

_Time,_SinTime,_CosTime,_unity_DeltaTime

11.2 纹理动画

一张贴图就是一张序列帧贴图,通过对uv偏移,获取不同uv下不同的图片

floor(x),获取小于x的最大整数

frac(x),获取x的小数部分

11.3 顶点动画

Disable Batching 标签,关闭合批,因为顶点动画是对单个顶点进行操作,合批会让多个单物体合并成一个物体,从而丢失了各自的模型空间

11.4 广告牌动画

广告牌的目的构建一个变化的基向量,也就是固定一个轴,从而计算出其它两个轴的坐标,根据这个基向量,计算出顶点变化后的位置,然后对图片采样

固定的轴向分为 法线或者向上的轴,法线就是摄像机位置-模型坐标的中心点位置

如果法线的y轴不为0,说明当前固定的是法线,然后通过法线和向上的位置叉乘得到向右的位置

12.1 屏幕后处理脚本系统

OnRenderImage(RT source,RT dest)

把当前画面渲染到source里面,然后对其应用指定的shader,赋值给dest,最终显示出来

Graphic.Blit(textureD source,RT dest)

12.2 增加屏幕对比度 、亮度、饱和度

12.3 边缘检测

边缘检测用的是卷积计算,重要的是选择合适的卷积核,把要计算的像素放在卷积核的中心,一次计算相邻像素的值,然后相加就是该像素的新值,该值就是相邻像素的对比值

每一个像素的新值,都要进行N*N次计算,N是卷积核的行列数

12.4 高斯模糊

算法:

 

 12.5 Bloom 模糊

Bloom的原理:首先通过一个阈值,对屏幕图像提取一个亮度,存储到一个RT中,然后对该RT进行高斯模糊,然后和原图像进行混合

提取亮度:

 和原图像混合:

 13.1 获取深度和法线纹理

 

13.1.2 获取深度和法线纹理

 urp 里面只有深度纹理,没有法线纹理

fload d=SAMPLE _DEPTH_TEXTURE(depthTex,uv):获取当前点的深度

 

SAMPLE_DEPTH_TEXTURE_PROJ(depthTex, UNITY_PROJ_COORD(scrPos))

上面的第二个参数是float3或者float4类型的,一般是前两个值除以第三个值之后,再进行采样,如果有第四个值,还会和第四个值进行比较

scrPos=ComputeScreenPos(i.pos)获取到的

通过采样得到的深度值,是映射之后的像素值,取值范围[0,1],所以NDC下的深度值也就是z/w=2d-1,取值范围[-1,1]

然后NDC下的深度值又等于

二者 结合得出

视角空间下的z:

上式=near*far/d*near+(1-d)far,以为d的取值范围是[0,1],所以 它的取值范围为[near,far]

是非线性的

如果要得到线性的,除以far,则上述为 near/d*near+(1-d)far,则范围[near/far,1],当near为0的时候,也就是近平面和摄像机重合时,取值就为[0,1]

float d=LinearEyeDepth(d):把采样得到的深度值,转换到视角空间下

float d=Linear01Depth(d):把采样得到的深度值,转换到线性空间下

如果要对 深度+法线纹理进行转换,unity提供的函数 DecodeDepthNormal(texel,out depth,out normal)

因为深度+法线纹理,法线存储在RG通道,深度存储在BA通道

解析深度的函数是

float d=DecodeFloatRG(uv.zw)

float n=DecodeViewNormalStereo(texel)

13.2 运动模糊,通过矩阵重建世界坐标

 

 上图第一次画框的地方,我们的uv 就是NDC 坐标,只不过只有xy,所以经过对深度纹理采样,获得了NDC下的深度坐标z

然后反映射,得到裁剪空间下的坐标,也就是x,y,z,经过旋转矩阵之后得到了世界空间下的坐标,但是有一点是,世界空间下的w=1,所以除以w

13.3 全局雾效

 开启雾效要使用#pragma multi_compile_fog

使用UNITY_FOG_COORDS, UNITY_TRANSFROM_FOG, UNITY_APPLY_FOG

上面重建世界坐标的方式,要在片元着色器中两次矩阵乘法,比较好性能,下面介绍一种

整体公式:

这里是线性深度,也就是从深度图中采样得到的深度

后面的interpolatedRay 是插值射线,也就是从摄像机到屏幕上一点的连线

下面有推导:

我们已知有图中的Near,depth,以及屏幕的宽高比(aspect),我们要求的向量是有图中的问号代表的欧式距离

先得到几个辅助向量:

上面的为什么要用camera.forward 乘以呢,是为了最后的结果是向量,halfHeight是个标量,我们要进行的是向量的运算

然后我们得到depth*interpolatedRay

根据三角形相似原则  得到 depth/?=near/TL

则 ?=depth * TL/near 

提取出depth  则得到 interpolatedRay=TL/Near

因为四个角到摄像机的距离都相等,所以 TL是动态可换的,又因为最终我们得到的是向量的运算,所以采取的是单位向量*|Ray|,也就是 (TL/|TL|)* (|TL|/|Near|) 前边TL可以换成TR

然后把这个值传给片元着色器,根据差值得到某一点的射线向量

为什么只计算四个点就能知道某一点的射线向量呢?

因为我们的模型也就是抓取到的图像是一个面,只有四个顶点,也就是两个三角形,所以我们只需要处理好这四个点的向量,至于三角形面里面的,就在片元着色器进行差值得到了

下面是雾的计算:

推荐阅读