相信大家都应该玩过CS或者CF吧,游戏里面有个喷图功能,就是按一个T键就能在墙上或者地板上喷出自己预先设定的图案.
而刚好这就是我们这个Shader所需实现的内容.由于没有潜伏者的贴图,我只有从这个图里用PS扣一个Alpha通道出来,我后面会讲具体操作
下面就起跟我一起进入亮瞎氪金狗眼的3D世界吧
国际惯例上图先:
怎么样?过瘾吧,这样的话,到处乱喷,估计保卫者都快疯掉了,呵呵!!!!!!!!!
好了,进入正题,讲下原理:
投影贴图是从灯光处看到世界坐标系的原点从而建立VIEW矩阵,物体在灯光中视椎体的位置仍然由物体的MODEL矩阵来控制,然后通过PROJECTION矩阵转成标准设备坐标,由于标准设备坐标的范围为(-1,1),但是纹理坐标的范围为(0,1),所以我们需要将其移动到(0,1)
投影贴图的矩阵变换
In the rendering pipeline, geometry outside the frustum is clipped. However, when we generate projective texture coordinates by projecting the geometry from the
point of view of the light projector, no clipping is done — we simply project vertices. Consequently, geometry outside the projector's frustum receives projective texture coordinates outside the [0, 1] range.
这里有两种方法来处理位于视椎体外面的点,一种方法:就是为灯光设置一个锥形照射范围,这样超过这个范围的点将不会映射,第二种方法,我们检测纹理坐标,如何超出了(0,1)的范围,我们就不要映射了.
这里我们采用了第二种方法:
float2 uv = texCoordProj.xy / texCoordProj.w;
if( uv.x < 0.0f || uv.x > 1.0f || uv.y < 0.0f || uv.y > 1.0f )
{
return diffuseLighting;
}
另外,我们会发现,视椎体的前后都会投影,
The second issue is that back-projection artifacts can appear when the q coordinate is negative.
Back-projection refers to the texture being projected on surfaces that are behind the light source (or
projector).
There are several ways to avoid artifacts when q is negative:
1.Use culling to draw only geometry that is in front of the light source.
2.Use clip planes to remove geometry that is behind the light source.
3.Fold the back-projection factor into a 3D attenuation texture.
4.Use a fragment program to check when q is negative.
The first two solutions are tedious to implement; the fragment program solution is simple and efficient but
requires an advanced fragment profile. Just check the value of the q coordinate. If q is negative, you can
ignore the projective texture computation and output black.
这里我们采用第4种方法
if( texCoordProj.w < 0 )
{
return diffuseLighting;
}
好了,这里讲下PS相关的内容,为了将潜伏者从原图中扣出来,去掉背景的影响,我们需要新建一个Alpha通道,然后在Alpha通道里用多边形套索工具,自己慢慢把它扣出来,最后保存为32位的tga格式,将alpha通道保存进去
alpha通道
然后在片断着色器中,我们采用混合的方式,将其显示出来
return textureColor * textureColor.w + (1.0 - textureColor.w) * diffuseLighting;
最后再来一个暴雪的LOGO吧
是不是有点像放电影的赶脚,呵呵.
好了,贴下源代码
/*------------------------------------------------------------ 3D_Shader_ProjectivetTexturing.cpp -- achieve projective texturing (c) Seamanj.2013/8/30 ------------------------------------------------------------*/ #include "DXUT.h" #include "resource.h" // phase1 : add camera // phase2 : add wall // phase3 : add light and shader // phase4 : add animation // phase5 : add objects in space // phase6 : add projective texture #define phase1 1 #define phase2 1 #define phase3 1 #define phase4 1 #define phase5 1 #define phase6 1 #if phase1 #include "DXUTcamera.h" CModelViewerCamera g_Camera; #endif #if phase2 // Vertex Buffer LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; // Index Buffer LPDIRECT3DINDEXBUFFER9 g_pIB = NULL; #endif #if phase3 #include "SDKmisc.h"//加载文件时会用到 ID3DXEffect* g_pEffect = NULL; // D3DX effect interface D3DXHANDLE g_hTech = 0; D3DXHANDLE g_hWorldViewProj = NULL; D3DXHANDLE g_hWorld = NULL; D3DXHANDLE g_hWorldInv = NULL; D3DXHANDLE g_hLightPosition = NULL; ID3DXMesh* g_pLightSphereMesh = 0; static float g_fLightAngle = -0.4; /* Angle light rotates around scene. */ static float g_fLightHeight = 2.0f; /* Vertical height of light. */ #endif #if phase4 static bool g_bAnimation = false; short g_sDirection = 1; #endif #if phase5 ID3DXMesh* g_pSphereMesh = 0; ID3DXMesh* g_pTeapotMesh = 0; #endif #if phase6 IDirect3DTexture9* g_pTex = NULL; D3DXHANDLE g_hTex = NULL; D3DXHANDLE g_hTextureMatrix = NULL; #endif //-------------------------------------------------------------------------------------- // Rejects any D3D9 devices that aren't acceptable to the app by returning false //-------------------------------------------------------------------------------------- bool CALLBACK IsD3D9DeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext ) { // Typically want to skip back buffer formats that don't support alpha blending IDirect3D9* pD3D = DXUTGetD3D9Object(); if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, BackBufferFormat ) ) ) return false; return true; } //-------------------------------------------------------------------------------------- // Before a device is created, modify the device settings as needed //-------------------------------------------------------------------------------------- bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext ) { #if phase1 pDeviceSettings->d3d9.pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; #endif return true; } //-------------------------------------------------------------------------------------- // Create any D3D9 resources that will live through a device reset (D3DPOOL_MANAGED) // and aren't tied to the back buffer size //-------------------------------------------------------------------------------------- HRESULT CALLBACK OnD3D9CreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext ) { #if phase1 // Setup the camera's view parameters D3DXVECTOR3 vecEye( 0.0f, 0.0f, -5.0f ); D3DXVECTOR3 vecAt ( 0.0f, 0.0f, -0.0f ); g_Camera.SetViewParams( &vecEye, &vecAt ); FLOAT fObjectRadius=1; //摄像机缩放的3个参数 g_Camera.SetRadius( fObjectRadius * 3.0f, fObjectRadius * 0.5f, fObjectRadius * 20.0f ); g_Camera.SetEnablePositionMovement( true ); #endif #if phase3 HRESULT hr; // Create vertex shader WCHAR str[MAX_PATH]; // Read the D3DX effect file V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"ProjectiveTexturing.fx" ) ); // Create the effect LPD3DXBUFFER pErrorBuff; V_RETURN( D3DXCreateEffectFromFile( pd3dDevice, // associated device str, // effect filename NULL, // no preprocessor definitions NULL, // no ID3DXInclude interface D3DXSHADER_DEBUG, // compile flags NULL, // don't share parameters &g_pEffect, // return effect &pErrorBuff // return error messages ) ); //pErrorBuff // Get handle g_hTech = g_pEffect->GetTechniqueByName("MyTechnique"); g_hWorldViewProj = g_pEffect->GetParameterByName(0, "g_mWorldViewProj"); g_hLightPosition = g_pEffect->GetParameterByName(0, "g_lightPosition"); g_hWorld = g_pEffect->GetParameterByName(0, "g_mWorld"); g_hWorldInv = g_pEffect->GetParameterByName(0, "g_mWorldInv"); D3DXCreateSphere(pd3dDevice, 0.15, 30, 30, &g_pLightSphereMesh, 0); #endif #if phase5 D3DXCreateSphere(pd3dDevice, 2.0f, 40, 40, &g_pSphereMesh, 0); D3DXCreateTeapot(pd3dDevice, &g_pTeapotMesh, 0); #endif #if phase6 g_hTextureMatrix = g_pEffect->GetParameterByName(0, "g_mTextureMatrix"); g_hTex = g_pEffect->GetParameterByName(0, "g_tex"); D3DXCreateTextureFromFile(pd3dDevice, L"cf.tga", &g_pTex); #endif return S_OK; } #if phase2 struct MyVertexFormat { FLOAT x, y, z; FLOAT nx, ny, nz; }; #define FVF_VERTEX (D3DFVF_XYZ | D3DFVF_NORMAL ) static HRESULT initVertexIndexBuffer(IDirect3DDevice9* pd3dDevice) { static const MyVertexFormat Vertices[] = { //floor { 12, -2, -12, 0, 1, 0 }, { -12, -2, -12, 0, 1, 0 }, { -12, -2, 12 , 0, 1, 0 }, { 12, -2, 12, 0, 1, 0 }, //front { -12, -2, -12,0, 0, 1 }, { 12, -2, -12,0, 0, 1 }, { 12, 10, -12,0, 0, 1 }, { -12, 10, -12, 0, 0, 1}, //back { 12, -2, 12 ,0, 0, -1 }, { -12, -2, 12 ,0, 0, -1 }, { -12, 10, 12 ,0, 0, -1}, { 12, 10, 12,0, 0, -1 }, //ceiling { -12, 10, -12,0, -1, 0 }, { 12, 10, -12,0, -1, 0 }, { 12, 10, 12,0, -1, 0 }, { -12, 10, 12,0, -1, 0}, //left { -12, -2, 12,1, 0, 0 }, { -12, -2, -12,1, 0, 0 }, { -12, 10, -12,1, 0, 0 }, {-12, 10, 12,1, 0, 0 }, //-right { 12, -2, -12,-1, 0, 0 }, { 12, -2, 12,-1, 0, 0 }, {12, 10, 12,-1, 0, 0}, {12, 10, -12,-1, 0, 0 } }; if (FAILED(pd3dDevice->CreateVertexBuffer(sizeof(Vertices), 0, FVF_VERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL))) { return E_FAIL; } void* pVertices; if (FAILED(g_pVB->Lock(0, 0, /* map entire buffer */ &pVertices, 0))) { return E_FAIL; } memcpy(pVertices, Vertices, sizeof(Vertices)); g_pVB->Unlock(); // Create and initialize index buffer static const WORD Indices[] = { 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8,10, 11, 12,13,14, 12,14,15, 16,17,18, 16,18,19, 20,21,22, 20,22,23 }; if (FAILED(pd3dDevice->CreateIndexBuffer(sizeof(Indices), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &g_pIB, NULL))) { return E_FAIL; } void* pIndices; if (FAILED(g_pIB->Lock(0, 0, /* map entire buffer */ &pIndices, 0))) { return E_FAIL; } memcpy(pIndices, Indices, sizeof(Indices)); g_pIB->Unlock(); return S_OK; } #endif //-------------------------------------------------------------------------------------- // Create any D3D9 resources that won't live through a device reset (D3DPOOL_DEFAULT) // or that are tied to the back buffer size //-------------------------------------------------------------------------------------- HRESULT CALLBACK OnD3D9ResetDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext ) { #if phase3 HRESULT hr; if( g_pEffect ) V_RETURN( g_pEffect->OnResetDevice() ); #endif #if phase2 pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); //Setup the camera's projection parameters float fAspectRatio = pBackBufferSurfaceDesc->Width / ( FLOAT )pBackBufferSurfaceDesc->Height; g_Camera.SetProjParams( D3DX_PI / 2, fAspectRatio, 0.1f, 5000.0f ); g_Camera.SetWindow( pBackBufferSurfaceDesc->Width, pBackBufferSurfaceDesc->Height ); g_Camera.SetButtonMasks( MOUSE_LEFT_BUTTON, MOUSE_WHEEL, MOUSE_RIGHT_BUTTON ); #endif #if !phase1 return S_OK; #else return initVertexIndexBuffer(pd3dDevice); #endif } #if phase4 static const double my2Pi = 2.0 * 3.14159265358979323846; #endif //-------------------------------------------------------------------------------------- // Handle updates to the scene. This is called regardless of which D3D API is used //-------------------------------------------------------------------------------------- void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext ) { #if phase1 g_Camera.FrameMove( fElapsedTime ); #endif #if phase4 if( g_bAnimation ) { if( g_fLightHeight > 9.0f) g_sDirection = -1; else if( g_fLightHeight < -1.5f ) g_sDirection = 1; g_fLightHeight += 0.0002 * g_sDirection; g_fLightAngle += 0.0002; if( g_fLightAngle > my2Pi) { g_fLightAngle -= my2Pi; } } #endif } #if phase6 static void buildTextureMatrix(D3DXMATRIXA16& textureMatrix, const D3DXMATRIXA16& modelMatrix, const D3DXMATRIXA16& viewMatrix )//需要modelMatrix是因为需要了解物体在灯光的视椎体里面的位置 { D3DXMATRIXA16 projMatrix; D3DXMATRIXA16 RangeMappingMatrix( 0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0,0.5, 0, 0.5, 0.5,0.5, 1); D3DXMatrixPerspectiveFovLH(&projMatrix, 50.0f * 3.14f / 180.0f , 1.72f, 0.25, 20.0); textureMatrix = modelMatrix * viewMatrix * projMatrix * RangeMappingMatrix; } static void buildLightViewMatrix(D3DXMATRIXA16& lightViewMatrix,const D3DXVECTOR3 *pLightPosition ) { D3DXMatrixLookAtLH(&lightViewMatrix, pLightPosition, &D3DXVECTOR3(0, 0, 0), &D3DXVECTOR3(0, -1, 0));//看世界坐标系中的原点 } #endif //-------------------------------------------------------------------------------------- // Render the scene using the D3D9 device //-------------------------------------------------------------------------------------- void CALLBACK OnD3D9FrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext ) { HRESULT hr; #if phase3 const float lightPosition[4] = { 6*sin(g_fLightAngle), g_fLightHeight, 6*cos(g_fLightAngle), 1 }; #endif // Clear the render target and the zbuffer V( pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB( 0, 45, 50, 170 ), 1.0f, 0 ) ); // Render the scene if( SUCCEEDED( pd3dDevice->BeginScene() ) ) { #if phase3 UINT iPass, cPasses; D3DXMATRIXA16 mWorldViewProjection,mWorld,mWorldInv; #if phase6 D3DXMATRIXA16 mLightViewMatrix, mTextureMatrix; #endif V(g_pEffect->SetTechnique(g_hTech)); V( g_pEffect->Begin( &cPasses, 0 ) ); for( iPass = 0; iPass < cPasses; iPass++ ) { V( g_pEffect->BeginPass( iPass ) ); //set light position V( g_pEffect->SetFloatArray( g_hLightPosition, lightPosition, 4) ); #if phase6 //set texture V( g_pEffect->SetTexture( g_hTex, g_pTex) ); #endif #if phase2//build wall #if phase5 // Set world matrix D3DXMatrixIdentity(&mWorld); V( g_pEffect->SetMatrix( g_hWorld, &mWorld) ); // Set world inverse matrix D3DXMatrixInverse(&mWorldInv, NULL, &mWorld); V( g_pEffect->SetMatrix( g_hWorldInv, &mWorldInv) ); // set worldviewproj matrix mWorldViewProjection = mWorld * *g_Camera.GetViewMatrix() * *g_Camera.GetProjMatrix(); V( g_pEffect->SetMatrix( g_hWorldViewProj, &mWorldViewProjection) ); #endif #if phase6 // set light view matrix buildLightViewMatrix(mLightViewMatrix, &D3DXVECTOR3(lightPosition[0], lightPosition[1], lightPosition[2]) ); // set texture matrix buildTextureMatrix(mTextureMatrix, mWorld, mLightViewMatrix); V( g_pEffect->SetMatrix( g_hTextureMatrix, &mTextureMatrix) ); #endif pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); V( g_pEffect->CommitChanges() ); pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(MyVertexFormat)); pd3dDevice->SetIndices(g_pIB);//sets the current index buffer. pd3dDevice->SetFVF(FVF_VERTEX);//Sets the current vertex stream declaration. pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 24, 0, 12); #endif #if phase5 // Set world matrix D3DXMatrixTranslation(&mWorld, 2, 0, 0); V( g_pEffect->SetMatrix( g_hWorld, &mWorld) ); // Set world inverse matrix D3DXMatrixInverse(&mWorldInv, NULL, &mWorld); V( g_pEffect->SetMatrix( g_hWorldInv, &mWorldInv) ); // set worldviewproj matrix mWorldViewProjection = mWorld * *g_Camera.GetViewMatrix() * *g_Camera.GetProjMatrix(); V( g_pEffect->SetMatrix( g_hWorldViewProj, &mWorldViewProjection) ); #if phase6 // set light view matrix buildLightViewMatrix(mLightViewMatrix, &D3DXVECTOR3(lightPosition[0], lightPosition[1], lightPosition[2]) ); // set texture matrix buildTextureMatrix(mTextureMatrix, mWorld, mLightViewMatrix); V( g_pEffect->SetMatrix( g_hTextureMatrix, &mTextureMatrix) ); #endif V( g_pEffect->CommitChanges() ); g_pSphereMesh->DrawSubset(0); // set teapot world matrix // Set world matrix D3DXMatrixTranslation(&mWorld, -2, 1, 0); V( g_pEffect->SetMatrix( g_hWorld, &mWorld) ); // Set world inverse matrix D3DXMatrixInverse(&mWorldInv, NULL, &mWorld); V( g_pEffect->SetMatrix( g_hWorldInv, &mWorldInv) ); // set worldviewproj matrix mWorldViewProjection = mWorld * *g_Camera.GetViewMatrix() * *g_Camera.GetProjMatrix(); V( g_pEffect->SetMatrix( g_hWorldViewProj, &mWorldViewProjection) ); #if phase6 // set light view matrix buildLightViewMatrix(mLightViewMatrix, &D3DXVECTOR3(lightPosition[0], lightPosition[1], lightPosition[2]) ); // set texture matrix buildTextureMatrix(mTextureMatrix, mWorld, mLightViewMatrix); V( g_pEffect->SetMatrix( g_hTextureMatrix, &mTextureMatrix) ); #endif V( g_pEffect->CommitChanges() ); g_pTeapotMesh->DrawSubset(0); #endif V( g_pEffect->EndPass() ); } V( g_pEffect->End() ); #endif #if phase3 pd3dDevice->SetRenderState(D3DRS_LIGHTING, false); // Set world matrix D3DXMATRIX M; D3DXMatrixIdentity( &M ); // M = identity matrix D3DXMatrixTranslation(&M, lightPosition[0], lightPosition[1], lightPosition[2]); pd3dDevice->SetTransform(D3DTS_WORLD, &M) ; //这里三角形更像是世界坐标中静止的物体(比如墙)因为按W它会相对与摄像机会动,不像茶壶总在摄像机前面,相对于摄像机静止 // Set view matrix D3DXMATRIX view = *g_Camera.GetViewMatrix() ; pd3dDevice->SetTransform(D3DTS_VIEW, &view) ; // Set projection matrix D3DXMATRIX proj = *g_Camera.GetProjMatrix() ; pd3dDevice->SetTransform(D3DTS_PROJECTION, &proj) ; g_pLightSphereMesh->DrawSubset(0); #endif V( pd3dDevice->EndScene() ); } } //-------------------------------------------------------------------------------------- // Handle messages to the application //-------------------------------------------------------------------------------------- LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void* pUserContext ) { #if phase1 g_Camera.HandleMessages( hWnd, uMsg, wParam, lParam ); #endif return 0; } //-------------------------------------------------------------------------------------- // Release D3D9 resources created in the OnD3D9ResetDevice callback //-------------------------------------------------------------------------------------- void CALLBACK OnD3D9LostDevice( void* pUserContext ) { #if phase1 SAFE_RELEASE(g_pVB); SAFE_RELEASE(g_pIB); #endif #if phase3 if( g_pEffect ) g_pEffect->OnLostDevice(); #endif } //-------------------------------------------------------------------------------------- // Release D3D9 resources created in the OnD3D9CreateDevice callback //-------------------------------------------------------------------------------------- void CALLBACK OnD3D9DestroyDevice( void* pUserContext ) { #if phase3 SAFE_RELEASE(g_pEffect); SAFE_RELEASE(g_pLightSphereMesh); #endif #if phase5 SAFE_RELEASE(g_pSphereMesh); SAFE_RELEASE(g_pTeapotMesh); #endif #if phase6 SAFE_RELEASE(g_pTex); #endif } #if phase4 void CALLBACK OnKeyboardProc(UINT character, bool is_key_down, bool is_alt_down, void* user_context) { if(is_key_down) { switch(character) { case VK_SPACE: g_bAnimation = !g_bAnimation; break; } } } #endif //-------------------------------------------------------------------------------------- // Initialize everything and go into a render loop //-------------------------------------------------------------------------------------- INT WINAPI wWinMain( HINSTANCE, HINSTANCE, LPWSTR, int ) { // Enable run-time memory check for debug builds. #if defined(DEBUG) | defined(_DEBUG) _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); #endif // Set the callback functions DXUTSetCallbackD3D9DeviceAcceptable( IsD3D9DeviceAcceptable ); DXUTSetCallbackD3D9DeviceCreated( OnD3D9CreateDevice ); DXUTSetCallbackD3D9DeviceReset( OnD3D9ResetDevice ); DXUTSetCallbackD3D9FrameRender( OnD3D9FrameRender ); DXUTSetCallbackD3D9DeviceLost( OnD3D9LostDevice ); DXUTSetCallbackD3D9DeviceDestroyed( OnD3D9DestroyDevice ); DXUTSetCallbackDeviceChanging( ModifyDeviceSettings ); DXUTSetCallbackMsgProc( MsgProc ); DXUTSetCallbackFrameMove( OnFrameMove ); #if phase4 DXUTSetCallbackKeyboard( OnKeyboardProc ); #endif // TODO: Perform any application-level initialization here // Initialize DXUT and create the desired Win32 window and Direct3D device for the application DXUTInit( true, true ); // Parse the command line and show msgboxes DXUTSetHotkeyHandling( true, true, true ); // handle the default hotkeys DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen DXUTCreateWindow( L"3D_Shader_ProjectivetTexturing" ); DXUTCreateDevice( true, 640, 480 ); // Start the render loop DXUTMainLoop(); // TODO: Perform any application-level cleanup here return DXUTGetExitCode(); }
/*-------------------------------------------------------------------------- ProjectiveTexturing.fx -- Projective texturing shader (c) Seamanj.2013/8/30 --------------------------------------------------------------------------*/ //-------------------------------------------------------------------------------------- // Global variables //-------------------------------------------------------------------------------------- float4x4 g_mWorldViewProj; float4x4 g_mWorld; float4x4 g_mWorldInv; float4x4 g_mTextureMatrix; float3 g_lightPosition; texture g_tex; //----------------------------------------------------------------------------- // Sampler //----------------------------------------------------------------------------- sampler2D g_sam = sampler_state { Texture = <g_tex>; MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; AddressU = Border; AddressV = Border; }; //-------------------------------------------------------------------------------------- // Vertex shader output structure //-------------------------------------------------------------------------------------- struct VS_Output { float4 position : POSITION; float4 diffuseLighting : COLOR; float4 texCoordProj : TEXCOORD0; }; //-------------------------------------------------------------------------------------- // Vertex shader //-------------------------------------------------------------------------------------- VS_Output MyVertexEntry(float4 position : POSITION,float3 normal : NORMAL) { VS_Output OUT; OUT.position = mul ( position, g_mWorldViewProj); OUT.texCoordProj = mul(position, g_mTextureMatrix); // Compute diffuse lighting normal = mul(normal, transpose((float3x3)g_mWorldInv)); float3 N = normalize(normal); position = mul( position, g_mWorld );//在世界坐标系中计算光照 float3 L = normalize(g_lightPosition - position.xyz); OUT.diffuseLighting = float4(1,1,1,1) * max(dot(N, L), 0); return OUT; } //-------------------------------------------------------------------------------------- // Pixel shader //-------------------------------------------------------------------------------------- float4 MyPixelEntry(float4 diffuseLighting : COLOR, float4 texCoordProj : TEXCOORD0) : COLOR { float2 uv = texCoordProj.xy / texCoordProj.w; //如果纹理坐标超出了(0,1)范围以及投影椎体的后面都不要映射 if( uv.x < 0.0f || uv.x > 1.0f || uv.y < 0.0f || uv.y > 1.0f || texCoordProj.w < 0 ) { return diffuseLighting; } uv.x = 1 - uv.x;//由于建立灯光的view矩阵时采用的是左手坐标系,所以转成纹理坐标需要左右相反 float4 textureColor = tex2D(g_sam, uv); return textureColor * textureColor.w + (1.0 - textureColor.w) * diffuseLighting; } //-------------------------------------------------------------------------------------- // Renders scene to render target //-------------------------------------------------------------------------------------- technique MyTechnique { pass P0 { VertexShader = compile vs_2_0 MyVertexEntry(); PixelShader = compile ps_2_0 MyPixelEntry(); } }