BlueRoseNote/07-Other/Unity/ShaderLab笔记.md
2023-06-29 11:55:02 +08:00

11 KiB
Raw Blame History

纯Shader

  • Uniform代表该变量在顶点与片元着色器中值都是相同的。
  • fixed:低精度数字它们以精度来换取移动设备上的速度。在台式机上fixed只是float的别名。

内置库

  • UnityShaderVariables.cginc定义了渲染所需的一堆着色器变量例如变换相机和光照数据。这些都在需要时由Unity设置。
  • HLSLSupport.cginc进行了设置因此无论代码针对的是哪个平台都可以使用相同的代码进行编写。无需担心使用特定于平台的数据类型等。
  • UnityInstancing.cginc专门用于实例化支持这是一种减少绘制调用的特定渲染技术。尽管它不直接包含文件但依赖于UnityShaderVariables。

关键字

PropertyType

  • Int
  • Float
  • Range
  • Color
  • Vector
  • 2D texture
  • Cube texture
  • 3D texture

SubShader

针对不同性能的显卡适配不同的Shader。

SubShader{
    [Tags]
    
    [RenderSetup]
    
    Pass{
        
    }
}

Fallback关键字

Fallback关键词用于处理匹配失败的情况指定一个用于处理这个情况的Pass或者直接跳过。

FallBack Off

CGPROGRAM与ENDCG

里面编写这里编写HLSL/CG对于VertexShader与PixelShader则写在SubShader的Pass关键字的{}中。

内置变量

矩阵

  • UNITY_MATRIX_MVP
  • UNITY_MATRIX_MV
  • UNITY_MATRIX_P
  • UNITY_MATRIX_VP
  • UNITY_MATRIX_T_MV
  • UNITY_MATRIX_IT_MV
  • _Object2World
  • _World2Object
  • unity_WorldToObject

摄像机

  • WorldSpaceCameraPos
  • ProjectionParams
  • ScreenParams
  • ZBufferParams
  • unity_OrthoParams
  • unity_CameraProjection
  • unity_CameraInvProjection
  • unity_CameraWorldClipPlanes[6]

灯光

  • UNITY_LIGHTMODEL_AMBIENT
  • _WorldSpaceLightPos0

定义VS与PS名称

#pragma vertex vert
#pragma fragment frag

UnityCG.cginc

常用结构体

  • appdata_base float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord: TEXCOORD0;
  • appdata_tan float4 vertex : POSITION; float4 tangent : TANGENT; float3 normal : NORMAL; float4 texcoord : TEXCOORD0;
  • appdata_full float4 vertex : POSITION; float4 tangent : TANGENT; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; float4 texcoord3 : TEXCOORD3; # if defined(SHADER_API_XBOX360) half4 texcoord4 : TEXCOORD4; half4 texcoord5 : TEXCOORD5; # endif fixed4 color : COLOR;
  • appdata_img float4 vertex : POSITION; half2 texcoord : TEXCOORD0;
  • v2f_img 裁剪空间中的位置、纹理坐标

常用函数

  • float4 WorldSpaceViewDir(float4 v)输入一个模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向
  • float4 UnityWorldSpaceViewDir(float4 v)输入一个世界空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向
  • float4 ObjSpaceViewDir(float4 v)输入一个模型空间中的顶点位置,返回模型空间中从该店到摄像机的观察方向
  • float4 WorldSpaceLightDir(flaot4 v)仅用于向前渲染。 输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向。没有被归一化
  • float4 ObjectSpaceLightDir(float4 v)仅用于向前渲染中,输入一个模型空间中的顶点位置, 返回模型空间中从该点到光源的光照方向。没有被归一化
  • float4 UnityWorldSpaceLightDir(float4 v)仅用于向前渲染中,输入一个世界空间中的顶点位置, 返回世界空间中从该点到光源的光照方向。没有被归一化
  • float3 UnityObjectToWorldNormal(float3 norm)把法线方向从模型空间中转换到世界空间中
  • float3 UnityObjectToWorldDir(float3 dir)把方向矢量从模型空间中变换到世界空间中
  • float3 Unity WorldToObjectDir(float3 dir)把方向矢量从世界空间变换到模型空间中

浮点格式

float32位 half16位 -60000~+60000 fixed11位 -2.0~+2.0

贴图

Sampler2D _MainTex使用类似float4 _MainTex_ST作为缩放与位移。

管线LightMode

定义在Pass内的Tag{}中。

  • Always总是渲染但不计算任何光照。
  • ForwardBase用于前向渲染该Pass会计算环境光、平行光、逐顶点SH与LightMap。
  • ForwardAdd用于前向渲染该Pass会计算额外的逐像素光源每个Pass对应一个光源。
  • Deferred用于延迟渲染该Pass会计算GBuffer。
  • ShadowCaster把物体的深度信息渲染ShadowMap或是一张深度纹理中。
  • PrepassBase用于遗留的延迟渲染该Pass会渲染法线和高光反射的指数部分。
  • PrepassFinal用于遗留的延迟渲染该Pass通过合并纹理、光照和自发光来渲染得到最终的颜色。
  • Vertex、VertexLMRGBM、VertexLM用于遗留的顶点光照渲染。

不透明物体的渲染顺序

  1. 先渲染所有不透明物体,并开启深度测试与深度写入
  2. 把半透明物体按它距离摄像机的远近进行排序 ,然后按照从后往前的顺序渲染这些半透明物体,并开启它们的深度测试,但关闭深度写入。

Unity3d的解决方案

定义了5个渲染队列

  • Backgrouond
  • Geometry
  • AlphaTest
  • Transparent
  • Overlay

可以在Tags定义队列

SubShader{
    Tags{"Queue"="AlphaTest"}
    Pass{
        ZWrite Off
    }
}

Blend相关命令

BlendOff BlendSrcFactorDstFactor BlendSrcFactorDstFactor,SrcFactorA DstFactorA BlendOp BlendOperation

混合操作

  1. Add
  2. Sub
  3. RevSub
  4. Min
  5. Max

常见的混合类型

  1. Blend SrcAlpha OneMinusSrcAlpha 正常
  2. Blend OneMinusDstColor One 柔和相加
  3. Blend DstColor Zero 正片叠底
  4. Blend DstColor SrcColor 两倍相乘
  5. BlendOp Min Blend One One 变暗
  6. BlendOp Max Blend One One 变亮
  7. Blend One One 线性减淡

解决半透明乱序问题

  1. 使用两个Pass来渲染模型第一个Pass开启深度写入但不输出颜色。第二个Pass进行正常的透明度混合。
  2. 双面渲染的透明效果

剔除命令

Cull Back | Front | Off

内置时间变量

  • _Time场景加载开始到现在的时间4个分量为t/20,t,2t,3t
  • _SinTime时间的正弦值4个分量为t/8,t/4,t/2,t
  • _CosTime时间的余弦值4个分量为t/8,t/4,t/2,t
  • untiy_DeltaTimedt为时间的增量4个分量为dt1/dt,SmoothDt,1/SmoothDt

预处理命令

multi_compile

multi_compile定义的宏,如#pragma multi_compile_fog#pragma multi_compile_fwdbase基本上适用于大部分shader与shader自身所带的属性无关。

shader_feature

shader_feature定义的宏多用于针对shader自身的属性。比如shader中有_NormalMap这个属性(Property),便可通过#pragma shader_feature _NormalMap来定义宏用来实现这个shader在material有无_NormalMap时可进行不同的处理。

优化

减少Draw Call的方式有动态合批与静态合批。Unity中支持两种批处理方式一种是动态批处理一种是静态批处理。对于动态批处理来说有点是一切处理都是Unity自动完成的不需要我们自己做任何操作而且物体是可以移动的但缺点是限制很多可能一不小心就会破坏了这种机制导致Unity无法动态批处理一些使用了相同材质的物体。 而对于静态批处理来说,它的优点是自由度很高,限制很少;但缺点是可能会占用更多的内存,而且经过静态批出里的所有物体都不可以再移动了。 动态批处理的原理是每一帧把可以进行批处理的模型网格进行合并再把合并后模型数据传递给GPU然后使用同一个材质对其渲染。处理实现方便动态批处理的另一个好处是经过批处理的物体仍然可以移动这是由于在处理每帧时Unity都会重新合并一次网格。

共享材质

将多张纹理合并到一起,并且制作成材质。

动态合批

在使用同一个材质的情况下,满足的条件后就会被动态处理,每帧都会合并一次。条件:

  • 能够进行动态批处理的网格顶点属性规模要小于900.例如如果Shader中需要使用顶点位置、法线和纹理坐标这3个顶点属性那么想要让模型能够被动态批处理它的顶点数目不能超过300。需要注意的是这个数字未来有可能会发生变化因此不要依赖这个数据。
  • 一般来说所有对象都需要使用同一个缩放尺度。一个例外的情况是如果所有的物体都使用了不同的非统一缩放那么它们也是可以被动态批处理的。但在Unity 5 中,这种对模型缩放的限制已经不存在了。
  • 使用光照纹理的物体需要格外小心处理。这些物体需要额外的渲染参数,例如,在光照纹理上的索引、偏移量和缩放信息等。因此,为了让这些物体可以被动态批处理,我们需要保证它们指向光照纹理中的同一个位置。
  • 多Pass的Shader会中断批处理。在前向渲染中我们有时需要使用额外的Pass来为模型添加更多的光照效果但这样一来模型就会被动态批处理了。

静态合批

在运行开始阶段,把需要进行静态批处理的模型合并到一个新的网格结构中。

GeometryShader

ShaderModel必须4.0以上#program target 4.0如果低于这个目标u3d会自定提升至该级别。

  • maxvertexcount定义输出顶点数如果只是处理三角形只需要设置为3即可。
  • triangle为输入类型关键字。
  • TriangleStream为输出流类型。
[maxvertexcount(3)]
void MyGeometryProgram (
	triangle InterpolatorsVertex i[3],
	inout TriangleStream<InterpolatorsGeometry> stream
)

Flat线框效果CatLikeCoding中的案例

向三角形添加重心坐标的一种方法是使用网格的顶点颜色存储它们。每个三角形的第一个顶点变为红色,第二个顶点变为绿色,第三个顶点变为蓝色。但是,这将需要具有以此方式分配的顶点颜色的网格,并且无法共享顶点。我们想要一种适用于任何网格的解决方案。幸运的是,我们可以使用我们的几何程序添加所需的坐标。

由于网格不提供重心坐标因此顶点程序不了解它们。所以它们不属于InterpolatorsVertex结构。要使几何程序输出它们我们必须定义一个新结构。首先在MyGeometryProgram上方定义InterpolatorsGeometry。它应包含与InterpolatorsVertex相同的数据因此使用它作为其内容

struct InterpolatorsGeometry {
	InterpolatorsVertex data;
	CUSTOM_GEOMETRY_INTERPOLATORS
};
  • MyGeometryProgram的作用为调整按照面法线调整顶点法线在调整barycentricCoordinates值最后塞入inout TriangleStream中。
  • GetAlbedoWithWireframe为线控效果控制最终会在My Lighting.cginc中以宏替换的方式整合至渲染流程中。
float3 GetAlbedoWithWireframe (Interpolators i) {
	float3 albedo = GetAlbedo(i);
	float3 barys;
	barys.xy = i.barycentricCoordinates;
	barys.z = 1 - barys.x - barys.y;
	float3 deltas = fwidth(barys);
	float3 smoothing = deltas * _WireframeSmoothing;
	float3 thickness = deltas * _WireframeThickness;
	barys = smoothstep(thickness, thickness + smoothing, barys);
	float minBary = min(barys.x, min(barys.y, barys.z));
	return lerp(_WireframeColor, albedo, minBary);
}