《Unity Shader入门精要》笔记:高级篇(3)以及扩展

  • 本篇博客主要为个人学习所编写读书笔记,不用于任何商业用途,以及不允许任何人以任何形式进行转载。
  • 本篇博客会补充一些扩展内容(例如其他博客链接)。
  • 本篇博客还会提供一些边读边做的效果截图。文章内所有数学公式都由Latex在线编辑器生成。
  • 本篇博客主要提供一个“glance”,知识点的总结。如有需要请到书店购买正版。
  • 博客提及所有官方文档基于2022.2版本,博客会更新一些书中的旧的知识点到2022.2版本。
  • 个人博客网址:《Unity Shader入门精要》笔记:高级篇(3)以及扩展 - Sugar的博客,如文章转载中出现例如传送门失效等纰漏,建议直接访问原博客网址。

  • 如有不对之处欢迎指正。
  • 我创建了一个游戏制作交流群:637959304 进群密码:(CSGO的拆包密码)欢迎各位大佬一起学习交流,不限于任何平台(U3D、UE、COCO2dx、GamesMaker等),以及欢迎编程,美术,音乐等游戏相关的任何人员一起进群学习交流。
  • 完结撒花~

目录

噪声

消融效果

水波效果

Unity中的渲染优化技术

影响性能的因素

Unity中的渲染分析工具

减少draw call数目

减少需要处理的顶点数目

减少需要处理的片元数目

减少带宽

减少计算复杂度

扩展


噪声

消融效果

  • 原理:噪声纹理+透明度测试
//核心代码在于:
//第一个Pass
float4 frag (VertexOutput i) : SV_Target
{
				fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
//burn为噪声贴图,_BurnAmount为时间
				clip(burn.r - _BurnAmount);
				fixed t = 1 - smoothstep(0.0, _LineWidth, burn.r - _BurnAmount);
				fixed3 burnColor = lerp(_BurnFirstColor, _BurnSecondColor, t);
				burnColor = pow(burnColor, 5);
				//书里面写的是3D的,然后这里我改了改放在了2D上面
				fixed4 finalColor = lerp(这里填入你原本的颜色, fixed4(burnColor,1.0), t * step(0.0001, _BurnAmount));
				return finalColor;
}
//第二个用于裁剪的Pass
fixed4 frag(v2f i) : SV_Target {
{
				fixed3 burn = tex2D(_BurnMap, i.uvBurnMap).rgb;
				clip(burn.r - _BurnAmount);
				SHADOW_CASTER_FRAGMENT(i)
}

《Unity Shader入门精要》笔记:高级篇(3)以及扩展_第1张图片

 

  • 当你试图把你的砖块贴图当成噪声放上去的时候

 


水波效果

  • 把噪声图当做高度图使用,不断修改水面的法线方向。
    把立方纹理作为环境纹理贴图模拟折射效果。
  • 使用菲涅尔系数动态混合反射和折射颜色,v和n代表视角方向和法线方向。夹角越小,数值越小,反射越弱。

Shader "Example/Water Wave" {
	Properties {
		_Color ("Main Color", Color) = (0, 0.15, 0.115, 1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_WaveMap ("Wave Map", 2D) = "bump" {}
		_Cubemap ("Environment Cubemap", Cube) = "_Skybox" {}
		_WaveXSpeed ("Wave Horizontal Speed", Range(-0.1, 0.1)) = 0.01
		_WaveYSpeed ("Wave Vertical Speed", Range(-0.1, 0.1)) = 0.01
		_Distortion ("Distortion", Range(0, 100)) = 10
	}
	SubShader {

		Tags { "Queue"="Transparent" "RenderType"="Opaque" }
//抓取屏幕图像,并自定义名字传入到下方的Pass并采样
		GrabPass { "_RefractionTex" }
		
		Pass {
			Tags { "LightMode"="ForwardBase" }
			
			CGPROGRAM
			
			#include "UnityCG.cginc"
			#include "Lighting.cginc"
			
			#pragma multi_compile_fwdbase
			
			#pragma vertex vert
			#pragma fragment frag
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
//水的关于波纹的法线噪声贴图
			sampler2D _WaveMap;
			float4 _WaveMap_ST;
//环境贴图
			samplerCUBE _Cubemap;
			fixed _WaveXSpeed;
			fixed _WaveYSpeed;
//控制扭曲程度
			float _Distortion;	
			sampler2D _RefractionTex;
			float4 _RefractionTex_TexelSize;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 tangent : TANGENT; 
				float4 texcoord : TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float4 scrPos : TEXCOORD0;
				float4 uv : TEXCOORD1;
				float4 TtoW0 : TEXCOORD2;  
				float4 TtoW1 : TEXCOORD3;  
				float4 TtoW2 : TEXCOORD4; 
			};
			
			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				o.scrPos = ComputeGrabScreenPos(o.pos);
				
				o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
				o.uv.zw = TRANSFORM_TEX(v.texcoord, _WaveMap);
				
				float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;  
				fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);  
				fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);  
				fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; 
//得到切线空间下的3个坐标轴(切线、附切线和法线的方向)在世界空间下的表示
				o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);  
				o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);  
				o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);  
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
//计算法线纹理当前偏移量
				float2 speed = _Time.y * float2(_WaveXSpeed, _WaveYSpeed);
//两次采样对应了模拟两层交叉的睡眠波动效果。
				fixed3 bump1 = UnpackNormal(tex2D(_WaveMap, i.uv.zw + speed)).rgb;
				fixed3 bump2 = UnpackNormal(tex2D(_WaveMap, i.uv.zw - speed)).rgb;
				fixed3 bump = normalize(bump1 + bump2);
//选用切线空间下的法线进行便宜
				float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
				i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
//使用透视除法并采样得到折射颜色
				fixed3 refrCol = tex2D( _RefractionTex, i.scrPos.xy/i.scrPos.w).rgb;
				
				bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
				fixed4 texColor = tex2D(_MainTex, i.uv.xy + speed);
				fixed3 reflDir = reflect(-viewDir, bump);
//使用反射方向对CUBE采样,把结果和主纹理颜色相乘得到反射颜色。
				fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb * _Color.rgb;
//计算菲涅尔系数并混合折射和反射。
				fixed fresnel = pow(1 - saturate(dot(viewDir, bump)), 4);
				fixed3 finalColor = reflCol * fresnel + refrCol * (1 - fresnel);
				
				return fixed4(finalColor, 1);
			}
			
			ENDCG
		}
	}
	// Do not cast shadow
	FallBack Off
}


Unity中的渲染优化技术

  • 不同平台不同手机型号所用架构都各有差异,这就要求在移动平台上需要花更大的功夫去做优化。例如:PowerVR芯片(用于IOS和部分安卓)使用基于瓦片的延迟渲染架构(TBDR)。Adreno(高通)和Mali(ARM的芯片)使用Early-z或相似技术进行一个低精度的深度检测,来剔除那些不需要渲染的片元。Tegra(英伟达芯片)使用传统架构设计,overdraw(一个像素被绘制多次)可能会造成性能瓶颈。

影响性能的因素

  • CPU:负责保证帧率。有可能的性能影响:1、过多的draw call 2、复杂的脚本或物理模拟
  • GPU:负责分辨率相关的处理。有可能的性能影响
    顶点处理:1、过多的顶点 2、过多的逐顶点计算
    片元处理:1、过多的片元(有可能是分辨率,也有可能是overdraw)2、过多逐片元计算
  • Draw Call:CPU在每次通知GPU进行渲染之前,都要提前准备好顶点数据(位置、颜色、法线、纹理坐标等),然后调用一系列API把他们放到GPU可以访问到的指定位置,最后调用绘制指令。而调用绘制命令会产生一个draw call,过多的draw call会造成CPU性能瓶颈。这是因为CPU往往需要盖面很多渲染状态的设置,这很好费时间。
  • 优化技术:
    CPU:使用批处理技术减少draw call数目。
    GPU:1、减少需要处理的顶点数目:优化几何体,使用模型的LOD(level of detail)技术,使用遮挡剔除(Occlusion Culling)技术 2、减少需要处理的片元数目:控制绘制程序,警惕透明物体,减少实时光照 3、减少计算复杂度:使用Shader的LOD技术,代码方面的优化
    节省内存带宽:1、减少纹理大小 2、利用分辨率缩放

Unity中的渲染分析工具

  • 渲染统计窗口:他会统计音频、图像、网络三个方面信息。
  • FPS:帧率以及处理一帧所需的时间(括号内)
  • Batches:一帧需要进行的批处理数目
  • Saved by batching:合并的批处理数目(表明批处理为我们节省了多少draw call)
  • Tris,Verts:需要绘制的三角面片和顶点数目
  • Screen:屏幕大小以及占用内存大小
  • SetPass calls:渲染使用的pass数目(每个pass都需要Unity的runtime绑定新的Shader,这可能造成CPU性能瓶颈)
  • shadow casters:阴影映射数目
  • Visible skinned meshes:渲染的蒙皮网格数目
  • Animation/Animator components playing:播放动画数目

《Unity Shader入门精要》笔记:高级篇(3)以及扩展_第2张图片

  • 性能分析器(Profiler):通过Window->Analysis->Profiler打开。

《Unity Shader入门精要》笔记:高级篇(3)以及扩展_第3张图片

  • 帧调试器(Frame Debugger):Window->Analysis->Frame Debugger打开。在窗口中可以查看每个draw call的工作和结果。
  • Drawing为10代表绘制工作需要10个draw call完成,剩余的4个draw call是准备处理工作。

《Unity Shader入门精要》笔记:高级篇(3)以及扩展_第4张图片

  • 其他平台性能分析工具:
    安卓:高通分析工具可以对不同测试机进行详细的性能分析。英伟达提供了NVPerfHUD工具。
    IOS平台:Unity内置工具可以得到整个场景花费GPU的时间。PoweVRAM的PVRUniSCo shader也可以给出一些宏观上的性能信息。

减少draw call数目

  • Unity渲染优化的4种批处理:传送门
  • 动态批处理:如果场景中有一些模型共享了同一个材质并满足一些条件,Unity就可以自动把他们进行批处理,合并为一个draw call完成。
    原理:每一帧把可以进行批处理的模型网络进行合并,再把合并后模型数据传递给GPU,然后使用同一个材质对其渲染。
    条件限制:1、能够进行动态批处理的网格的顶点属性规模要小于900。(该规模大小为Unity5时所设定)2、所有对象最好使用同一个缩放尺度。 3、使用光照纹理的物体需要小心处理。 4、多Pass的Shader会被批处理中断。
  • 静态批处理:适用于任何大小的几何模型。在Unity中只需要勾选模型的Static可选框即可变为静态批处理。
    原理:在运行开始阶段,把需要进行静态批处理的模型合并到一个新的网格结构中。但只需要进行一次合并操作,因此要比动态批处理更加高效。缺点有:模型不可以在运行时刻被移动,占用更多的内存空间来存储合并后的集合结构。
    对于不同材质的物体,静态批处理可以减少draw call之间的切换,同样也可以优化性能。

《Unity Shader入门精要》笔记:高级篇(3)以及扩展_第5张图片

  • 共享材质:如果两个材质之间只有使用的纹理不同,我们可以把这些文理合并到一张更大的纹理中,这张更大的纹理被称为一张图集(atlas),再次使用时采用不同的采样坐标即可。

减少需要处理的顶点数目

  • 优化几何体:Unity和三维建模软件中都有相应优化选项。Unity中显示的顶点数目一般会多于三维建模软件中的,因为Unity要进行GPU计算,会进行顶点拆分。
  • 模型的LOD技术:当一个物体离摄像机很远时,模型上的细节无法被察觉到。因此LOD允许当对象原理摄像机时,减少模型上的面片数量。
  • 遮挡剔除技术:消除其他物件后面看不到的物件,因此就不会计算看不到的顶点。

减少需要处理的片元数目

  • 这部分的重点在于减少overdraw
  • 控制绘制顺序:深度测试可以保证物体都是从前往后绘制的,可以很大程度上减少overdraw。
    人为控制绘制顺序的经验:优先绘制主人公,敌人绘制顺序可以往后放,天空盒子放在渲染队列最后。
  • 透明物体:移动设备尽量避免半透明物体的绘制,或者考虑开启深度写入的透明方法。
  • 减少实时光照和阴影:使用逐像素点光源以及逐像素的Shader可能造成draw call数目成倍增加,同时也会增加overdraw的数量。同时同态批处理和静态批处理都无法优化逐像素点光源。
    解决方法:1、提前进行光照烘焙得到光照纹理,进行光源模拟。 2、使用God Ray进行光源模拟,通过透明纹理的模拟来得到。 3、使用查找纹理,提前存储好内容,使用时根据光源方向,视角方向,法线方向等参数直接采样得到光照结果。这种方法可以得到更出色的光照模型。

减少带宽

  • 减少纹理大小
    1、纹理长宽比最好是正方形,且最少是2的整数幂。
    2、尽可能使用多级渐远纹理技术(mipmapping)和纹理压缩。(书中所述的Advanced的Texture Type已经没有了,在Default类型中也可以直接设置是否生成Mip Maps,如下图所示)纹理压缩如下图最下方的Format选项所示,Unity会自动根据平台不同采用不同纹理压缩技术。

《Unity Shader入门精要》笔记:高级篇(3)以及扩展_第6张图片

  • 利用分辨率缩放:过高屏幕分别率也会造成性能下降。Unity设置屏幕分辨率可以直接调用Screen.SetResolution

减少计算复杂度

  • Shader的LOD技术:和模型的LOD技术类似。只有Shader的LOD值小于某个设定的值,这个Shader才会被使用,而超过了的物体将不会被渲染。(difffuse的LOD为200,Bumped Specular为400)
  • 代码方面的优化:1、尽可能使用低精度的浮点值进行运算,高精度,float适用于存储顶点坐标等变量。half适用于一些标量、纹理坐标等变量,计算速度大约是float的两倍。fixed适用于绝大多数颜色变量和归一化后的方向矢量,计算速度大约是float的4倍。2、尽可能使用全屏的屏幕后处理效果。 3、尽可能不要使用分支语句和循环语句,避免三角函数、pow、log等复杂的数学运算(可以用查找表进行代替),尽可能不要使用discard操作会影响硬件某些优化。

扩展

  • 书中的最后一个部分主要是一些扩展内容的讲解,这里不作过多赘述,推荐去原书中阅览一番。(另外原作者在Github中更新了最后一个章节的新内容,部分与原版书籍不同)根据书中扩展内容加之本人的理解,提到如下可以扩展的方向,也是值得有时间找专门教程仔细研究一番:
  • 表面着色器
  • PBS基于物理的渲染
  • 光线追踪技术
  • 伽马校正、HDR
  • SRP、URP、HDRP
  • ShaderGraph

完结撒花~

你可能感兴趣的:(Unity3D,着色器,unity,游戏引擎)