crysis shader系统简单分析
crysis的shader都在Shaders.pak中,把shaders.pak改成shaders.zip解压即可看到所有的shader,除了一些扩展,语法基本上
和hlsl差不了太多.
主要的宏定义:
%_LT_LIGHTS //灯光数量
%DYN_BRANCHING //是否使用动态分支(当%DYN_BRANCHING有效时,通过LightsNum_DB来控制灯光数量而不再使用%_LT_LIGHTS)
%_LT_0_TYPE %_LT_1_TYPE %_LT_2_TYPE %_LT_3_TYPE //分别定义每个灯光的类型
%_RT_FOG
%_RT_ALPHABLEND
%BUMP_MAP
...
也就是说crysis要为每个shader根据宏的不同定义来生成若干个版本,游戏目录中的ShaderCache.pak保存了预先生成好的若干shader。
对于shader 3.0及以上的显卡,通过常量寄存器+static branch来取代宏的方式似乎更加简洁.
RunTime.ext和相关的.ext中定义了对于每个宏应该precache哪些shader
一,fragLib.cfi
fragLib.cfi定义了公用的一些shader函数以及要在每个具体的shader文件中中实现的shader函数原型
通用shader函数原型有主要有:
frag_unify_parameters( inout fragPass pPass ) //每个shader实现根据需求控制使用哪些shader特性
frag_custom_begin(in vert2FragGeneral IN, inout fragCustomPass pPass)
frag_custom_per_light(in fragCustomPass pPass, inout fragLightPass pLight) //每个shader实现每个灯光的光照算法
...
通用shader函数:
//-----------------------------------------
void frag_unify(inout fragPass pPass, in vert2FragGeneral IN)
{
...
//初始化shader输入参数及一些特性开关,在具体的shader文件中实现
//////////////////////////////////////////////////////////
frag_unify_parameters( pPass );
//////////////////////////////////////////////////////////
...
}
//-----------------------------------------
half4 frag_shared_output(inout fragPass pPass)
{
...
frag_quality_setup( pPass );
...
// do custom pass setup
frag_custom_begin(pPass);
...
#if %_LT_LIGHTS
#if %DYN_BRANCHING
float2 tcLI = LightInfoTC_DB.xy;
float tcIter = LightInfoTC_DB.z;
float tcIterParam = LightInfoTC_DB.w;
//这里其实是通过常量LightsNum_DB来使用static branch,并不是DYANCHING
//ps的static branch只在ps 2.0a及以上才支持,dynamic branch则在ps3.0及以上才支持
for (int i=0; i<LightsNum_DB; i++, tcLI.x+=tcIter)
#else
// Light types
const int aLType[4] = {%_LT_0_TYPE, %_LT_1_TYPE, %_LT_2_TYPE, %_LT_3_TYPE};
#ifdef D3D10
[unroll]
#endif
for (int i=0; i<%_LT_NUM; i++)
#endif
{
float4 WorldLightPos;
#if %DYN_BRANCHING
// We can't index constants in cycle, so get light/shadow info from the texture
WorldLightPos = tex2Dlod(LightInfoSampler_DB, float4(tcLI, 0, 0));
half4 Diffuse = tex2Dlod(LightInfoSampler_DB, float4(tcLI.x+tcIterParam, tcLI.y, 0, 0));
half4 Specular = Diffuse;
Specular.xyz *= Diffuse.w;
half4 ShadowChanMask = tex2Dlod(LightInfoSampler_DB, float4(tcLI.x+tcIterParam*3, tcLI.y, 0, 0));
float nType = tex2Dlod(LightInfoSampler_DB, float4(tcLI.x+tcIterParam*2, tcLI.y, 0, 0)).w;
#else
int nType = aLType[i];
WorldLightPos = LGetPosition(i);
half4 Diffuse = LGetDiffuse(i);
half4 Specular;
...
half4 ShadowChanMask = LGetShadowMask(i);
// Some optimisations for sun light (per-frame parameters and hardcoded values)
if (nType == LT_DIRECTIONAL)
{
WorldLightPos = g_PS_SunLightDir;
ShadowChanMask = float4(1,0,0,0);
}
...
half fFallOff = 1;
float3 vLight, vLightWS;
if (nType == LT_DIRECTIONAL)
{
vLightWS = WorldLightPos.xyz * 10000.0f;
vLight = WorldLightPos.xyz;
}
else
{
vLightWS = WorldLightPos.xyz - pPass.IN.vView.xyz;
vLight = normalize(vLightWS.xyz); // 3 alu
fFallOff = GetAttenuation(vLightWS.xyz, WorldLightPos.w); // 2 alu
}
#endif
//计算每个灯光的光照,在具体的shader文件中实现
//////////////////////////////////////////////////////////
frag_custom_per_light(pPass, pLight);
//////////////////////////////////////////////////////////
...
}
...
}
//-----------------------------------------------------------------------------------
/**
*/
二,具体的shader实现
如HumanSkin.cfx
void frag_unify_parameters( inout fragPass pPass )
{
//这些变量编译后是用作常量呢还是临时变量?推测应该是使用临时变量
pPass.bRenormalizeNormal = true;
#if %BUMP_DIFFUSE
pPass.bDiffuseBump = true;
#endif
}
//灯光类型在frag_shared_output已经处理了,好象只支持point light和direct light?
void frag_custom_per_light(inout fragPass pPass, inout fragLightPass pLight)
{
if (pPass.bDiffuseBump )
{
pLight.fNdotL = dot(pPass.vNormalDiffuse.xyz, pLight.vLight.xyz);
}
...
if( pPass.nQuality == QUALITY_HIGH )
{
cDiffuse = saturate( lerp( cSubSurface.xyz*(pLight.fOcclShadow*0.5+0.5), pLight.fOcclShadow * lerp(pLight.fNdotL, 1, DiffusionAmount), fWrappedNdotL) );
}
else
{
cDiffuse = fWrappedNdotL * pLight.fOcclShadow;
}
...
}
////////////////////////////////////////////////////////
//注意:frag_unify_parameters等函数在上面已经被重定义
#include "fragLib.cfi"
////////////////////////////////////////////////////////
///////////////// pixel shader //////////////////
pixout SkinPS(vert2FragGeneral IN)
{
pixout OUT = (pixout) 0;
// Initialize fragPass structure
fragPass pPass = (fragPass) 0;
//////////////////////////////////////////////////
//在fragLib.cfi中定义!
frag_unify(pPass, IN);
//////////////////////////////////////////////////
//////////////////////////////////////////////////
//frag_shared_output在fragLib.cfi中定义!
half4 cFinal = frag_shared_output(pPass);
//////////////////////////////////////////////////
HDROutput(OUT, cFinal, 1);
return OUT;
}
//////////////////////////////// techniques ////////////////
technique General
<
string Script =
"TechniqueZ=ZPass;"
"TechniqueMotionBlur=MotionBlurPass;"
//"TechniqueDetail=DetailPass;"
"TechniqueCaustics=CausticsPass;"
#ifndef %DISABLE_RAIN_PASS
"TechniqueRainPass=RainPass;"
#endif
"TechniqueCustomRender=CustomRenderPass;"
"TechniqueShadowGen=ShadowGen;"
#ifdef D3D10
"TechniqueShadowGenDX10=ShadowGenGS;"
#endif
"TechniqueShadowPass=ShadowPass;"
>
{
pass p0
{
VertexShader = compile vs_Auto SkinVS() GeneralVS;
PixelShader = compile ps_Auto SkinPS() GeneralPS;
ZEnable = true;
ZWriteEnable = true;
CullMode = Back;
}
}
//////////////////////////////// Common techniques ////////////////
#include "CommonZPass.cfi"
//include "CommonDetailPass.cfi"
#include "CommonCausticsPass.cfi"
#include "CommonMotionBlurPass.cfi"
#include "CommonViewsPass.cfi"
#ifndef %DISABLE_RAIN_PASS
#include "CommonRainPass.cfi"
#endif
#include "ShadowCommon.cfi"
#include "CommonShadowGenPass.cfi"
#ifdef D3D10
#include "CommonShadowGenPassGS.cfi"
#endif
#include "CommonShadowPass.cfi"
/////////////////////// eof ///