详解Unity3D Shader之Shader Lab框架

笔者介绍:姜雪伟,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函数:

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++完整代码如下所示:

/-----------------------------------------------------------------------------
// 全局变量
//-----------------------------------------------------------------------------
ID3DXFont*                 g_pFont = NULL;          //ID3DXFont字体对象
ID3DXSprite*               g_pTextSprite = NULL;    //ID3DXSprite文本精灵对象
bool                       g_bShowHelp = true;      //标识是否显示简单说明文本

CDXUTDialogResourceManager g_DialogResourceManager; //对话框资源管理器
CD3DSettingsDlg            g_SettingsDlg;           //Direct3D设备设置对话框
CDXUTDialog                g_HUD;                   //对话框
CDXUTDialog                g_SampleUI;              //对话框


LPDIRECT3DVERTEXBUFFER9    g_pVB     = NULL;       //顶点缓冲区
LPD3DXEFFECT               g_pEffect = NULL;       //效果
bool                       g_bEnableSelfShadow;    //标志是否启用预编译渲染器


//顶点结构和灵活顶点格式
struct CUSTOMVERTEX
{
    D3DXVECTOR3 position;   //位置
    D3DXVECTOR3 normal;     //法线
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL)


//-----------------------------------------------------------------------------
// 控件ID
//-----------------------------------------------------------------------------
#define IDC_TOGGLEFULLSCREEN      1
#define IDC_TOGGLEREF             2
#define IDC_CHANGEDEVICE          3
#define IDC_ENABLE_SELFSHADOW     4


//-----------------------------------------------------------------------------
// Desc: 函数声明
//------------------------------------------------------------------------------
bool    CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext );
bool    CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps, void* pUserContext );
HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
void    CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
void    CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void* pUserContext );
void    CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext );
void    CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext );
void    CALLBACK OnLostDevice( void* pUserContext );
void    CALLBACK OnDestroyDevice( void* pUserContext );

void    InitApp();
void    RenderText();


//-----------------------------------------------------------------------------
// Desc: 入口函数
//-----------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
    //为Debug配置启用运行时内存检查功能
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

    //设置回调函数
    DXUTSetCallbackDeviceCreated( OnCreateDevice );
    DXUTSetCallbackDeviceReset( OnResetDevice );
    DXUTSetCallbackDeviceLost( OnLostDevice );
    DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackKeyboard( KeyboardProc );
    DXUTSetCallbackFrameRender( OnFrameRender );
    DXUTSetCallbackFrameMove( OnFrameMove );

	//应用程序相关的初始化
	InitApp();

	//初始化DXUT, 创建窗口, 创建Direct3D设备对象
	DXUTInit( true, true, true );
	DXUTSetCursorSettings( true, true );
	DXUTCreateWindow( L"HLSLSelfShadowing" );
	DXUTCreateDevice( D3DADAPTER_DEFAULT, true, 640, 480, 
		IsDeviceAcceptable, ModifyDeviceSettings );

	//进入消息循环和场景渲染
	DXUTMainLoop();

	//在此进行应用程序相关的清除工作

	return DXUTGetExitCode();
}


//-----------------------------------------------------------------------------
// Desc: 应用程序相关初始化
//-----------------------------------------------------------------------------
void InitApp()
{
    //初始化对话框
    g_SettingsDlg.Init( &g_DialogResourceManager );
    g_HUD.Init( &g_DialogResourceManager );
    g_SampleUI.Init( &g_DialogResourceManager );

	//为g_HUD对话框设置消息处理函数,添加控件
    g_HUD.SetCallback( OnGUIEvent ); int iY = 10; 
    g_HUD.AddButton( IDC_TOGGLEFULLSCREEN, L"Toggle full screen", 35, iY, 125, 22 );
    g_HUD.AddButton( IDC_TOGGLEREF, L"Toggle REF (F3)", 35, iY += 24, 125, 22 );
    g_HUD.AddButton( IDC_CHANGEDEVICE, L"Change device (F2)", 35, iY += 24, 125, 22, VK_F2 );

	//为g_SampleUI对话框设置消息处理函数,添加控件
    g_SampleUI.SetCallback( OnGUIEvent ); iY = 0; 
	g_bEnableSelfShadow = true;
    g_SampleUI.AddCheckBox( IDC_ENABLE_SELFSHADOW, L"Enable preshaders", 35, iY, 125, 22, true );
}


//-----------------------------------------------------------------------------
// Desc: 设备能力检查
//-----------------------------------------------------------------------------
bool CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, 
                                  D3DFORMAT BackBufferFormat, bool bWindowed, 
								  void* pUserContext )
{
	//检查后台缓冲区格式是否支持Alpha混合等操作(post pixel blending operations)
    IDirect3D9* pD3D = DXUTGetD3DObject(); 
    if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType,
                    AdapterFormat, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, 
                    D3DRTYPE_TEXTURE, BackBufferFormat ) ) )
        return false;

	//检查当前设备支持的顶点渲染器版本
	if( pCaps->VertexShaderVersion < D3DVS_VERSION(2,0) )
		return FALSE;

    return true;
}


//-----------------------------------------------------------------------------
// Desc: 修改Direct3D渲染设备设置
//-----------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, 
								    const D3DCAPS9* pCaps, void* pUserContext )
{
    //如果不支持硬件顶点处理则使用软件顶点处理
    if( (pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0)
    {
        pDeviceSettings->BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
    }

	//调试顶点渲染器需要参考设备或软件顶点处理
#ifdef DEBUG_VS
	if( pDeviceSettings->DeviceType != D3DDEVTYPE_REF )
	{
		pDeviceSettings->BehaviorFlags &= ~D3DCREATE_HARDWARE_VERTEXPROCESSING;
		pDeviceSettings->BehaviorFlags &= ~D3DCREATE_PUREDEVICE;                            
		pDeviceSettings->BehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
	}
#endif

	//调试像素渲染器需要参考设备
#ifdef DEBUG_PS
	pDeviceSettings->DeviceType = D3DDEVTYPE_REF;
#endif

	//如果使用参考设备,则弹出警告对话框
    static bool s_bFirstTime = true;
    if( s_bFirstTime )
    {
        s_bFirstTime = false;
        if( pDeviceSettings->DeviceType == D3DDEVTYPE_REF )
            DXUTDisplaySwitchingToREFWarning();
    }

    return true;
}


//-----------------------------------------------------------------------------
// Desc: 在此创建管理内存资源对象
//-----------------------------------------------------------------------------
HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, 
								const D3DSURFACE_DESC* pBackBufferSurfaceDesc, 
								void* pUserContext )
{
    HRESULT hr;

    V_RETURN( g_DialogResourceManager.OnCreateDevice( pd3dDevice ) );
    V_RETURN( g_SettingsDlg.OnCreateDevice( pd3dDevice ) );
    
    //创建字体
    V_RETURN( D3DXCreateFont( pd3dDevice, 15, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET, 
                         OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, 
                         L"Arial", &g_pFont ) );

	//创建顶点缓冲区
    V_RETURN( pd3dDevice->CreateVertexBuffer( 50*2*sizeof(CUSTOMVERTEX),
                                              0, D3DFVF_CUSTOMVERTEX,
                                              D3DPOOL_MANAGED, &g_pVB, NULL ));
    //填充顶点缓冲区
    CUSTOMVERTEX* pVertices;
    V_RETURN( g_pVB->Lock( 0, 0, (void**)&pVertices, 0 ) );
    for( DWORD i=0; i<50; i++ )
    {
        FLOAT theta = (2*D3DX_PI*i)/(50-1);
        pVertices[2*i+0].position = D3DXVECTOR3( sinf(theta),-1.0f, cosf(theta) );
        pVertices[2*i+0].normal   = D3DXVECTOR3( sinf(theta), 0.0f, cosf(theta) );
        pVertices[2*i+1].position = D3DXVECTOR3( sinf(theta), 1.0f, cosf(theta) );
        pVertices[2*i+1].normal   = D3DXVECTOR3( sinf(theta), 0.0f, cosf(theta) );
    }
    g_pVB->Unlock();

	//创建效果
    V_RETURN(D3DXCreateEffectFromFile( pd3dDevice, L"HLSLSelfShadowing.fx", NULL, NULL, 
		D3DXSHADER_DEBUG, NULL, &g_pEffect, NULL ));

    return S_OK;
}


//-----------------------------------------------------------------------------
// Desc: 在此创建默认内存类型资源对象
//-----------------------------------------------------------------------------
HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice, 
                                const D3DSURFACE_DESC* pBackBufferSurfaceDesc, 
								void* pUserContext )
{
    HRESULT hr;

    V_RETURN( g_DialogResourceManager.OnResetDevice() );
    V_RETURN( g_SettingsDlg.OnResetDevice() );

	//设置对话框位置和尺寸
    g_HUD.SetLocation( pBackBufferSurfaceDesc->Width-170, 0 );
    g_HUD.SetSize( 170, 170 );
    g_SampleUI.SetLocation( pBackBufferSurfaceDesc->Width-170, 
		                    pBackBufferSurfaceDesc->Height-50 );
    g_SampleUI.SetSize( 170, 50 );

	//恢复字体
    if( g_pFont )
        V_RETURN( g_pFont->OnResetDevice() );
   
	//创建ID3DXSprite接口对象
    V_RETURN( D3DXCreateSprite( pd3dDevice, &g_pTextSprite ) );

	//恢复效果对象
	if( g_pEffect )
        V_RETURN( g_pEffect->OnResetDevice() );

	//构造世界矩阵
	D3DXMATRIX matWorld;
	D3DXMatrixIdentity( &matWorld );

	//构造观察矩阵
	D3DXMATRIXA16 matView;
	D3DXVECTOR3 vEyePt( 0.0f, 3.0f,-5 );
    D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
    D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
    D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );

	//构造投影矩阵
	D3DXMATRIXA16 matProj;
	float fAspectRatio = (float)pBackBufferSurfaceDesc->Width / pBackBufferSurfaceDesc->Height;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, fAspectRatio, 1.0f, 100.0f );

	//为效果设置组合变换矩阵和世界矩阵
	D3DXMATRIX mWorldViewProj = matWorld * matView * matProj;
	g_pEffect->SetMatrix( "matWorldViewProj", &mWorldViewProj );
	g_pEffect->SetMatrix( "matWorld", &matWorld );

	//为效果设置观察点位置
	D3DXVECTOR4 vecEyeInEffect = D3DXVECTOR4(vEyePt.x, vEyePt.y, vEyePt.z, 0);
	g_pEffect->SetVector( "vecEye", &vecEyeInEffect );


	//为效果设置材质反射系数
	D3DXVECTOR4 mtrlAmbientInEffect  = D3DXVECTOR4( 0.5f, 0.5f, 0.0f, 1.0f );
	D3DXVECTOR4 mtrlDiffuseInEffect  = D3DXVECTOR4( 1.0f, 1.0f, 0.0f, 1.0f );
	D3DXVECTOR4 mtrlSpecularInEffect = D3DXVECTOR4( 1.0f, 1.0f, 1.0f, 1.0f );
	g_pEffect->SetVector( "materialAmbient",  &mtrlAmbientInEffect );
	g_pEffect->SetVector( "materialDiffuse",  &mtrlDiffuseInEffect );
	g_pEffect->SetVector( "materialSpecular", &mtrlSpecularInEffect);

	//设置剔出模式,为不剔出任何面
    hr = pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

    return S_OK;
}


//-----------------------------------------------------------------------------
// Desc: 更新场景
//-----------------------------------------------------------------------------
void CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, 
						   float fElapsedTime, void* pUserContext )
{
	//为效果设置光的方向 
	D3DXVECTOR4 lightDir = D3DXVECTOR4( cosf(timeGetTime()/350.0f), 1.0f,
                                        sinf(timeGetTime()/350.0f), 1.0f );
	g_pEffect->SetVector( "vecLightDir", &lightDir );
}


//-----------------------------------------------------------------------------
// Desc: 渲染场景
//-----------------------------------------------------------------------------
void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, 
							float fElapsedTime, void* pUserContext )
{
    HRESULT hr;
  
	//如果正在利用Direct3D设备设置对话框进行设置, 则不渲染场景
    if( g_SettingsDlg.IsActive() )
    {
        g_SettingsDlg.OnRender( fElapsedTime );
        return;
    }

    //清除后台颜色缓冲区和深度缓冲区
    V( pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 
		                 D3DCOLOR_ARGB(0, 45, 50, 170), 1.0f, 0) );

    //渲染场景
    if( SUCCEEDED( pd3dDevice->BeginScene() ) )
    {
		//使用效果渲染图形
		if(g_bEnableSelfShadow)
		{
			V(g_pEffect->SetTechnique( "TShaderSelfShadow" ));
		}
		else
		{
			V(g_pEffect->SetTechnique( "TShaderNoSelfShadow" ));
		}

		UINT nPasses;
		g_pEffect->Begin( &nPasses, 0 );
		for( UINT iPass = 0; iPass < nPasses; iPass ++ )
		{
			V(g_pEffect->BeginPass( iPass ));
			V(pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) ));
			V(pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX ));
			V(pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2*50-2 ));
			V(g_pEffect->EndPass());
		}
		g_pEffect->End(); 

		//渲染文本和控件
        DXUT_BeginPerfEvent( DXUT_PERFEVENTCOLOR, L"HUD / Stats" ); 
        RenderText();
        V( g_HUD.OnRender( fElapsedTime ) );
        V( g_SampleUI.OnRender( fElapsedTime ) );
        DXUT_EndPerfEvent();

        V( pd3dDevice->EndScene() );
    }
}


//-----------------------------------------------------------------------------
// Desc: 渲染文本
//-----------------------------------------------------------------------------
void RenderText()
{
    CDXUTTextHelper txtHelper( g_pFont, g_pTextSprite, 15 );

    //显示当前Direct3D设备状态和渲染帧速率
    txtHelper.Begin();
    txtHelper.SetInsertionPos( 5, 5 );
    txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
    txtHelper.DrawTextLine( DXUTGetFrameStats(true) );
    txtHelper.DrawTextLine( DXUTGetDeviceStats() );

	//显示其他简要信息
    txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );
    txtHelper.DrawTextLine( L"通过HLSL编程实现自遮蔽阴影" );
    
    //显示简单帮助文本
    const D3DSURFACE_DESC* pd3dsdBackBuffer = DXUTGetBackBufferSurfaceDesc();
    if( g_bShowHelp )
    {
        txtHelper.SetInsertionPos( 10, pd3dsdBackBuffer->Height-15*6 );
        txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 0.75f, 0.0f, 1.0f ) );
        txtHelper.DrawTextLine( L"Controls (F1 to hide):" );

        txtHelper.SetInsertionPos( 40, pd3dsdBackBuffer->Height-15*5 );
        txtHelper.DrawTextLine( L"Quit: ESC" );
    }
    else
    {
        txtHelper.SetInsertionPos( 10, pd3dsdBackBuffer->Height-15*2 );
        txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );
        txtHelper.DrawTextLine( L"Press F1 for help" );
    }
    txtHelper.End();
}


//-----------------------------------------------------------------------------
// Desc: 消息处理
//-----------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, 
						 bool* pbNoFurtherProcessing, void* pUserContext )
{
    *pbNoFurtherProcessing = g_DialogResourceManager.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;

    if( g_SettingsDlg.IsActive() )
    {
        g_SettingsDlg.MsgProc( hWnd, uMsg, wParam, lParam );
        return 0;
    }

    *pbNoFurtherProcessing = g_HUD.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;
   
	*pbNoFurtherProcessing = g_SampleUI.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;

    return 0;
}


//-----------------------------------------------------------------------------
// Desc: 键盘消息处理
//-----------------------------------------------------------------------------
void CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext )
{
    if( bKeyDown )
    {
        switch( nChar )
        {
            case VK_F1: g_bShowHelp = !g_bShowHelp; break;
        }
    }
}


//-----------------------------------------------------------------------------
// Desc: 处理各种控件消息
//-----------------------------------------------------------------------------
void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, 
						 void* pUserContext )
{
    switch( nControlID )
    {
        case IDC_TOGGLEFULLSCREEN:
			DXUTToggleFullScreen(); 
			break;

        case IDC_TOGGLEREF:
			DXUTToggleREF(); 
			break;

        case IDC_CHANGEDEVICE:
			g_SettingsDlg.SetActive( !g_SettingsDlg.IsActive() ); 
			break;

		case IDC_ENABLE_SELFSHADOW: 
            g_bEnableSelfShadow = g_SampleUI.GetCheckBox( IDC_ENABLE_SELFSHADOW )->GetChecked(); 
            break;
    }
}


//-----------------------------------------------------------------------------
// Desc: 释放在OnResetDevice()中创建的资源
//-----------------------------------------------------------------------------
void CALLBACK OnLostDevice( void* pUserContext )
{
    g_DialogResourceManager.OnLostDevice();
    g_SettingsDlg.OnLostDevice();
    if( g_pFont )
        g_pFont->OnLostDevice();
    SAFE_RELEASE( g_pTextSprite );

	if( g_pEffect )
		g_pEffect->OnLostDevice();
}


//------------------------------------------------------------------------------
// Desc: 释放在OnCreateDevice()中创建的资源
//------------------------------------------------------------------------------
void CALLBACK OnDestroyDevice( void* pUserContext )
{
    g_DialogResourceManager.OnDestroyDevice();
    g_SettingsDlg.OnDestroyDevice();
    SAFE_RELEASE( g_pFont );

	SAFE_RELEASE(g_pVB);
	SAFE_RELEASE(g_pEffect);
}

C++代码可以直接在程序中运行,但是需要安装Direct SDK,代码中都有注释,这里就不一一讲解了,如果对此还不清楚,自己可以查看一下Direct SDK文档。在这里只把重点代码解释一下,首先是加载Shader脚本文件到内存中,代码函数如下所示:

	//创建效果
    V_RETURN(D3DXCreateEffectFromFile( pd3dDevice, L"HLSLSelfShadowing.fx", NULL, NULL, 
		D3DXSHADER_DEBUG, NULL, &g_pEffect, NULL ));

接下来开始对Shader文件中的矩阵和声明的变量进行赋值操作:

	//构造投影矩阵
	D3DXMATRIXA16 matProj;
	float fAspectRatio = (float)pBackBufferSurfaceDesc->Width / pBackBufferSurfaceDesc->Height;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, fAspectRatio, 1.0f, 100.0f );

	//为效果设置组合变换矩阵和世界矩阵
	D3DXMATRIX mWorldViewProj = matWorld * matView * matProj;
	g_pEffect->SetMatrix( "matWorldViewProj", &mWorldViewProj );
	g_pEffect->SetMatrix( "matWorld", &matWorld );

	//为效果设置观察点位置
	D3DXVECTOR4 vecEyeInEffect = D3DXVECTOR4(vEyePt.x, vEyePt.y, vEyePt.z, 0);
	g_pEffect->SetVector( "vecEye", &vecEyeInEffect );


	//为效果设置材质反射系数
	D3DXVECTOR4 mtrlAmbientInEffect  = D3DXVECTOR4( 0.5f, 0.5f, 0.0f, 1.0f );
	D3DXVECTOR4 mtrlDiffuseInEffect  = D3DXVECTOR4( 1.0f, 1.0f, 0.0f, 1.0f );
	D3DXVECTOR4 mtrlSpecularInEffect = D3DXVECTOR4( 1.0f, 1.0f, 1.0f, 1.0f );
	g_pEffect->SetVector( "materialAmbient",  &mtrlAmbientInEffect );
	g_pEffect->SetVector( "materialDiffuse",  &mtrlDiffuseInEffect );
	g_pEffect->SetVector( "materialSpecular", &mtrlSpecularInEffect);

这些参数是需要传到shader脚本文件中的,由于Unity3D它有自己的UnityCG库,它是已经实现了直接调用接口即可。

在Direct3D中通过调用函数SetTechnique执行Technique技术,也就是执行GPU渲染,代码段如下所示:

  //渲染场景
    if( SUCCEEDED( pd3dDevice->BeginScene() ) )
    {
		//使用效果渲染图形
		if(g_bEnableSelfShadow)
		{
			V(g_pEffect->SetTechnique( "TShaderSelfShadow" ));
		}
		else
		{
			V(g_pEffect->SetTechnique( "TShaderNoSelfShadow" ));
		}

		UINT nPasses;
		g_pEffect->Begin( &nPasses, 0 );
		for( UINT iPass = 0; iPass < nPasses; iPass ++ )
		{
			V(g_pEffect->BeginPass( iPass ));
			V(pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) ));
			V(pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX ));
			V(pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2*50-2 ));
			V(g_pEffect->EndPass());
		}
		g_pEffect->End(); 

		//渲染文本和控件
        DXUT_BeginPerfEvent( DXUT_PERFEVENTCOLOR, L"HUD / Stats" ); 
        RenderText();
        V( g_HUD.OnRender( fElapsedTime ) );
        V( g_SampleUI.OnRender( fElapsedTime ) );
        DXUT_EndPerfEvent();

        V( pd3dDevice->EndScene() );
    }

其实,以上利用Direct3D的原理帮助读者理解Unity3D引擎内部实现原理,它们都是相通的,我在博客中也做了关于3D游戏引擎系列文章讲解,在这里也是为了帮助读者理解。文字写的比较少,但是代码都有注释希望读者能够理解。



你可能感兴趣的:(3D引擎,图形学编程)