网上有很多关于Shader的教程,我在这里就不给读者讲解基础知识了,我们直接讲重点,我会结合着C++底层代码一起讲解,帮助读者理解Unity3D引擎内部对于Shader加载的实现原理,下面就结合着Unity3D中的Shader的编写给读者解释,在Unity3D中的每个Shader中都有SubShader代码段。以下面的代码为例:
- SubShader {
- Pass{
- // Dont write to the depth buffer
- ZWrite off
- // Set up alpha blending
- Blend SrcAlpha OneMinusSrcAlpha
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "UnityCG.cginc"
- sampler2D _MainTex;
- float4 _Color;
- struct v2f{
- float4 pos:SV_POSITION;
- float4 texcoord : TEXCOORD0;
- };
- v2f vert(appdata_base v)
- {
- v2f o;
- o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
- o.texcoord = v.texcoord;
- return o;
- }
- half4 frag(v2f i):COLOR0
- {
- half4 col = _Color * tex2D(_MainTex, i.texcoord.xy);
- return col;
- }
- ENDCG
- }
- pass
- {
- }
- }
- SubShader
- {
- Pass
- {
- }
- }
SubShader中包含的代码段是核心程序,它中间还有Pass通道,Pass通道包含了定义的输出结构体和处理顶点函数和处理片段函数,在一个Shader中还可以包含多个SubShader,这些SubShader可以根据硬件自行适配,下面结合着C++代码给读者分析一下Unity3D中的Shader。先把DirectX中的Shader代码展示如下:
- //--------------------------------------------------------------
- // 全局变量
- //--------------------------------------------------------------
- float4x4 matWorldViewProj;
- float4x4 matWorld;
- float4 vecLightDir;
- float4 vecEye;
- float4 materialAmbient;
- float4 materialDiffuse;
- float4 materialSpecular;
- //-------------------------------------------------------------
- // 顶点渲染器输出结构
- //-------------------------------------------------------------
- struct VS_OUTPUT
- {
- float4 Pos : POSITION;
- float4 Color : COLOR;
- };
- //-------------------------------------------------------------
- // 顶点渲染器
- //-------------------------------------------------------------
- VS_OUTPUT VS( float4 Pos: POSITION, float3 Normal: NORMAL,
- uniform bool bEnableSelfShadow )
- {
- VS_OUTPUT Out = (VS_OUTPUT) 0;
- //顶点坐标变换
- Out.Pos = mul(Pos, matWorldViewProj);
- //单位化光照方向向量
- float3 LightDir = normalize( vecLightDir );
- //计算观察方向
- float3 PosWorld = normalize( mul(Pos, matWorld) );
- float3 ViewDir = normalize( vecEye - PosWorld );
- //计算法向量方向和漫反射强度
- float3 NormalWorld = normalize( mul(Normal, matWorld) );
- float4 diff = saturate( dot(NormalWorld, LightDir) );
- //计算反射光方向( R = 2 * (N.L) * N - L)和镜面反射强度
- float3 Reflect = normalize( 2 * diff * NormalWorld - LightDir );
- float4 specu = pow( saturate(dot(Reflect, ViewDir)), 0.5 );
- //各种光的颜色
- float4 ambientColor = { 0.1f, 0.0f, 0.0f, 1.0f};
- float4 diffuseColor = { 1.0f, 0.0f, 0.0f, 1.0f};
- float4 specularColor = { 1.0f, 0.0f, 0.0f, 1.0f};
- //计算顶点颜色
- if(bEnableSelfShadow) //启用自阴影
- {
- float shadow = saturate(4* diff);
- //计算顶点颜色 = Ambient + Shadow * ( Diffuse + Specular )
- Out.Color = ambientColor * materialAmbient + shadow *
- (diffuseColor * materialDiffuse * diff +
- specularColor * specu * materialSpecular);
- }
- else //禁用自阴影
- {
- Out.Color = ambientColor * materialAmbient +
- diffuseColor * materialDiffuse * diff +
- specularColor * specu * materialSpecular;
- }
- return Out;
- }
- //--------------------------------------------------------------
- // 技术
- //--------------------------------------------------------------
- technique TShaderSelfShadow
- {
- pass P0
- {
- VertexShader = compile vs_2_0 VS(true);
- }
- }
- technique TShaderNoSelfShadow
- {
- pass P0
- {
- VertexShader = compile vs_2_0 VS(false);
- }
- }
上述Shader定义了输出结构体这个跟Unity3D中的定义的结构体很类似,DirectX中的Shader定义了顶点渲染器VS函数: view plain copy
- VS_OUTPUT VS( float4 Pos: POSITION, float3 Normal: NORMAL,
- uniform bool bEnableSelfShadow )
其中,VS函数是结合了顶点着色器和片段着色器一起实现的,另外,它还定义了technique,在technique中也有pass通道,这个technique跟SubShader类似,pass通道跟SubPass中的pass通道类似。因为Unity引擎内部我们是看不到的,那就以DirectX中的C++代码为例给读者解密一下引擎内部是如何实现的,C++完整代码如下所示: