详解Unity3D Shader之Shader Lab框架
2017-02-03 22:35
337 查看
笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。
CSDN课程视频网址:http://edu.csdn.net/lecturer/144
网上有很多关于Shader的教程,我在这里就不给读者讲解基础知识了,我们直接讲重点,我会结合着C++底层代码一起讲解,帮助读者理解Unity3D引擎内部对于Shader加载的实现原理,下面就结合着Unity3D中的Shader的编写给读者解释,在Unity3D中的每个Shader中都有SubShader代码段。以下面的代码为例:
SubShader中包含的代码段是核心程序,它中间还有Pass通道,Pass通道包含了定义的输出结构体和处理顶点函数和处理片段函数,在一个Shader中还可以包含多个SubShader,这些SubShader可以根据硬件自行适配,下面结合着C++代码给读者分析一下Unity3D中的Shader。先把DirectX中的Shader代码展示如下:
上述Shader定义了输出结构体这个跟Unity3D中的定义的结构体很类似,DirectX中的Shader定义了顶点渲染器VS函数:
C++代码可以直接在程序中运行,但是需要安装Direct SDK,代码中都有注释,这里就不一一讲解了,如果对此还不清楚,自己可以查看一下Direct SDK文档。在这里只把重点代码解释一下,首先是加载Shader脚本文件到内存中,代码函数如下所示:
接下来开始对Shader文件中的矩阵和声明的变量进行赋值操作:
这些参数是需要传到shader脚本文件中的,由于Unity3D它有自己的UnityCG库,它是已经实现了直接调用接口即可。
在Direct3D中通过调用函数SetTechnique执行Technique技术,也就是执行GPU渲染,代码段如下所示:
其实,以上利用Direct3D的原理帮助读者理解Unity3D引擎内部实现原理,它们都是相通的,我在博客中也做了关于3D游戏引擎系列文章讲解,在这里也是为了帮助读者理解。文字写的比较少,但是代码都有注释希望读者能够理解。
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游戏引擎系列文章讲解,在这里也是为了帮助读者理解。文字写的比较少,但是代码都有注释希望读者能够理解。
相关文章推荐
- 详解Unity3D Shader之Shader Lab框架
- 详解Unity3D Shader开发之Shader框架
- 详解Unity3D Shader开发之Shader框架
- unity3d--内置shader详解
- Unity3D Built-in Shader,unity3d内置shader详解1
- BindChannels纹理绑定详解[Unity ShaderLab]
- Unity3D Shaderlab实战学习笔记 一 (《shaders for game programmers and artists》实践)
- Unity3D Shaderlab 学习记录
- Unity3d开发(一)ShaderLab 入门 语法格式
- Unity3D - Shader - ShaderLab的混合命令
- Unity3D Shaderlab 学习记录
- Unity3D ShaderLab Half Lambert光照模型
- Unity3D ShaderLab 自定义光照模型
- Unity3D Shader官方教程翻译(二)(ShaderLab)
- unity3D ShaderLab <五>漫反射光照改善技巧之使用2D ramp texture来创建一个假的BRDF(双向反射分布函数)
- Unity3D ShaderLab 混合两张贴图(Blend)
- Unity3D ShaderLab Use Properties
- Unity3D ShaderLab<二>Shader属性
- 详解Unity3D Shader开发之渲染管线
- Unity3D - Shader - ShaderLab基础语法