Sun
13
Sep 2009
Today is the 256's day of the year, so it's the Programmers' Day. Thus I wish all the best, especially a good code and no bugs to all of you who code for fun or/and profit (hopefully "and" :)
I've recently started new home project called "BlueWay". There's noting special about it - just another time I start everyting from scratch :) But this time I have deferred shading, cascaded shadow mapping, new scene management (based on k-d tree), new component-based object system and asynchronous resource manager (resources are loaded in the background on separate thread). I'm not sure whether I can call D3DXCompileShaderFromFile from separate thread without D3DCREATE_MULTITHREADED, but it seems to work OK :)
What I want to show today is a code for previewing textures. When coding some complex effects, it's often handy to look at intermediate results in render-target textures. Of course we have famous PIX and NVIDIA PerfHUD, but I believe that such custom code for real-time in-game texture preview is useful anyway. I wanted to make it general and flexible and here is the way I do it.
To display vertices of texture preview in 2D I use transformed XYZRHW coordinates. I wanted to be able to view normal textures as well as cube textures and to look at selected texture channels. I've developed multiple code paths to select best display method for given combination of channels (D3DCOLORWRITEENABLE_ flags). For example, when viewing single channel (red, green, blue or alpha), texture is displayed in grayscale, when viewing many color channels, texture is displayed in color and if alpha channel is also included, preview performs alpha-blending.
Here is the code of my shader (TexturePreview.fx):
/* Macros: CUBE = 0, 1 */ texture g_Texture; float4 g_ComponentMask; sampler TextureSampler = sampler_state { Texture = (g_Texture); AddressU = CLAMP; AddressV = CLAMP; AddressW = CLAMP; MinFilter = POINT; MagFilter = POINT; MipFilter = POINT; }; struct VERTEX { #if CUBE != 0 float3 Tex : TEXCOORD0; #else float2 Tex : TEXCOORD0; #endif }; float4 SampleTexture(VERTEX In) { #if CUBE != 0 return texCUBE(TextureSampler, In.Tex); #else return tex2D(TextureSampler, In.Tex); #endif } void AllPS( in VERTEX In, out float4 Out : COLOR0) { Out = SampleTexture(In); } void AlphaPS( in VERTEX In, out float4 Out : COLOR0) { Out.rgb = SampleTexture(In).a; Out.a = 1; } void SingleChannelPS( in VERTEX In, out float4 Out : COLOR0) { float4 TextureSample = SampleTexture(In); Out.rgb = dot(TextureSample.rgb, g_ComponentMask.rgb); Out.a = TextureSample.a; } void ComponentMaskPS( in VERTEX In, out float4 Out : COLOR0) { float4 TextureSample = SampleTexture(In); Out.rgb = TextureSample.rgb * g_ComponentMask.rgb; Out.a = TextureSample.a; } technique All { pass { PixelShader = compile ps_3_0 AllPS(); } } technique Alpha { pass { PixelShader = compile ps_3_0 AlphaPS(); } } technique SingleChannel { pass { PixelShader = compile ps_3_0 SingleChannelPS(); } } technique ComponentMask { pass { PixelShader = compile ps_3_0 ComponentMaskPS(); } }
And here is the C++ function that does the texture preview rendering:
void RenderTexturePreview(const RECTF &Rect, IDirect3DBaseTexture9 *Texture, DWORD Channels) { assert(Channels != 0); bool isCube = Texture->GetType() == D3DRTYPE_CUBETEXTURE; uint macroValues[] = { isCube ? 1 : 0 // CUBE }; ID3DXEffect *eff = TexturePreviewShader->GetEffect(macroValues); ////// Choice of technique const uint ALL_COLOR_CHANNELS = D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE; bool AlphaBlending = (Channels & D3DCOLORWRITEENABLE_ALPHA) != 0; // All color channels if ((Channels & ALL_COLOR_CHANNELS) == ALL_COLOR_CHANNELS) eff->SetTechnique("All"); // Single color channel else if ((Channels & ALL_COLOR_CHANNELS) == D3DCOLORWRITEENABLE_RED) { eff->SetTechnique("SingleChannel"); eff->SetVector("g_ComponentMask", &VEC4(1.f, 0.f, 0.f, 0.f)); } else if ((Channels & ALL_COLOR_CHANNELS) == D3DCOLORWRITEENABLE_GREEN) { eff->SetTechnique("SingleChannel"); eff->SetVector("g_ComponentMask", &VEC4(0.f, 1.f, 0.f, 0.f)); } else if ((Channels & ALL_COLOR_CHANNELS) == D3DCOLORWRITEENABLE_BLUE) { eff->SetTechnique("SingleChannel"); eff->SetVector("g_ComponentMask", &VEC4(0.f, 0.f, 1.f, 0.f)); } // Alpha channel only else if (Channels == D3DCOLORWRITEENABLE_ALPHA) { eff->SetTechnique("Alpha"); AlphaBlending = false; } // Two color channels else { eff->SetTechnique("ComponentMask"); eff->SetVector("g_ComponentMask", &VEC4( (Channels & D3DCOLORWRITEENABLE_RED) != 0 ? 1.f : 0.f, (Channels & D3DCOLORWRITEENABLE_GREEN) != 0 ? 1.f : 0.f, (Channels & D3DCOLORWRITEENABLE_BLUE) != 0 ? 1.f : 0.f, 0.f )); } eff->SetTexture("g_Texture", Texture); UINT Foo; eff->Begin(&Foo, 0); eff->BeginPass(0); Dev->SetRenderState(D3DRS_ZENABLE, FALSE); Dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); Dev->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); Dev->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); if (AlphaBlending) { Dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); Dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); Dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); } else Dev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); if (isCube) { float midX[3], midY[2]; midX[1] = Rect.GetCenterDim(0); midX[0] = (Rect.Min.x + midX[1]) * 0.5f; midX[2] = (midX[1] + Rect.Max.x) * 0.5f; midY[0] = Rect.Min.y + Rect.GetSizeDim(1) * (1.f/3.f); midY[1] = Rect.Min.y + Rect.GetSizeDim(1) * (2.f/3.f); struct VERTEX { VEC4 Pos; VEC3 TexCoord; }; VERTEX vb[24] = { // -x VEC4(Rect.Min.x, midY[0], 0.5f, 1.f), VEC3(-1.f, 1.f, -1.f), VEC4(midX[0], midY[0], 0.5f, 1.f), VEC3(-1.f, 1.f, 1.f), VEC4(Rect.Min.x, midY[1], 0.5f, 1.f), VEC3(-1.f, -1.f, -1.f), VEC4(midX[0], midY[1], 0.5f, 1.f), VEC3(-1.f, -1.f, 1.f), // +x VEC4(midX[1], midY[0], 0.5f, 1.f), VEC3(1.f, 1.f, 1.f), VEC4(midX[2], midY[0], 0.5f, 1.f), VEC3(1.f, 1.f, -1.f), VEC4(midX[1], midY[1], 0.5f, 1.f), VEC3(1.f, -1.f, 1.f), VEC4(midX[2], midY[1], 0.5f, 1.f), VEC3(1.f, -1.f, -1.f), // -y VEC4(midX[0], midY[1], 0.5f, 1.f), VEC3(-1.f, -1.f, 1.f), VEC4(midX[1], midY[1], 0.5f, 1.f), VEC3( 1.f, -1.f, 1.f), VEC4(midX[0], Rect.Max.y, 0.5f, 1.f), VEC3(-1.f, -1.f, -1.f), VEC4(midX[1], Rect.Max.y, 0.5f, 1.f), VEC3( 1.f, -1.f, -1.f), // -y VEC4(midX[0], Rect.Min.y, 0.5f, 1.f), VEC3(-1.f, 1.f, -1.f), VEC4(midX[1], Rect.Min.y, 0.5f, 1.f), VEC3( 1.f, 1.f, -1.f), VEC4(midX[0], midY[0], 0.5f, 1.f), VEC3(-1.f, 1.f, 1.f), VEC4(midX[1], midY[0], 0.5f, 1.f), VEC3( 1.f, 1.f, 1.f), // -z VEC4(midX[2], midY[0], 0.5f, 1.f), VEC3( 1.f, 1.f, -1.f), VEC4(Rect.Max.x, midY[0], 0.5f, 1.f), VEC3(-1.f, 1.f, -1.f), VEC4(midX[2], midY[1], 0.5f, 1.f), VEC3( 1.f, -1.f, -1.f), VEC4(Rect.Max.x, midY[1], 0.5f, 1.f), VEC3(-1.f, -1.f, -1.f), // +z VEC4(midX[0], midY[0], 0.5f, 1.f), VEC3(-1.f, 1.f, 1.f), VEC4(midX[1], midY[0], 0.5f, 1.f), VEC3( 1.f, 1.f, 1.f), VEC4(midX[0], midY[1], 0.5f, 1.f), VEC3(-1.f, -1.f, 1.f), VEC4(midX[1], midY[1], 0.5f, 1.f), VEC3( 1.f, -1.f, 1.f), }; uint2 ib[36]; uint2 vi = 0; uint2 *ibPtr = ib; for (uint quad_i = 0; quad_i < 6; quad_i++) { *ibPtr = vi ; ibPtr++; *ibPtr = vi+1; ibPtr++; *ibPtr = vi+2; ibPtr++; *ibPtr = vi+2; ibPtr++; *ibPtr = vi+1; ibPtr++; *ibPtr = vi+3; ibPtr++; vi += 4; } Dev->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0)); Dev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 24, 12, ib, D3DFMT_INDEX16, vb, sizeof(VERTEX)); } else { struct VERTEX { VEC4 Pos; VEC2 TexCoord; }; VERTEX vb[4] = { VEC4(Rect.Min.x, Rect.Min.y, 0.5f, 1.f), VEC2(0.f, 0.f), VEC4(Rect.Max.x, Rect.Min.y, 0.5f, 1.f), VEC2(1.f, 0.f), VEC4(Rect.Min.x, Rect.Max.y, 0.5f, 1.f), VEC2(0.f, 1.f), VEC4(Rect.Max.x, Rect.Max.y, 0.5f, 1.f), VEC2(1.f, 1.f) }; Dev->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0)); Dev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, vb, sizeof(VERTEX)); } eff->EndPass(); eff->End(); }
Comments | #rendering #directx Share