11 KiB
纯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)把方向矢量从世界空间变换到模型空间中
浮点格式
float:32位 half:16位 -60000~+60000 fixed:11位 -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:用于遗留的顶点光照渲染。
不透明物体的渲染顺序
- 先渲染所有不透明物体,并开启深度测试与深度写入
- 把半透明物体按它距离摄像机的远近进行排序 ,然后按照从后往前的顺序渲染这些半透明物体,并开启它们的深度测试,但关闭深度写入。
Unity3d的解决方案
定义了5个渲染队列
- Backgrouond
- Geometry
- AlphaTest
- Transparent
- Overlay
可以在Tags定义队列:
SubShader{
Tags{"Queue"="AlphaTest"}
Pass{
ZWrite Off
}
}
Blend相关命令
BlendOff BlendSrcFactorDstFactor BlendSrcFactorDstFactor,SrcFactorA DstFactorA BlendOp BlendOperation
混合操作
- Add
- Sub
- RevSub
- Min
- Max
常见的混合类型
- Blend SrcAlpha OneMinusSrcAlpha 正常
- Blend OneMinusDstColor One 柔和相加
- Blend DstColor Zero 正片叠底
- Blend DstColor SrcColor 两倍相乘
- BlendOp Min Blend One One 变暗
- BlendOp Max Blend One One 变亮
- Blend One One 线性减淡
解决半透明乱序问题
- 使用两个Pass来渲染模型,第一个Pass开启深度写入,但不输出颜色。第二个Pass进行正常的透明度混合。
- 双面渲染的透明效果
剔除命令
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_DeltaTime:dt为时间的增量,4个分量为:dt,1/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);
}