笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。
CSDN课程视频网址:http://edu.csdn.net/lecturer/144
网上有很多关于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函数: