Coding Texture Preview

Uwaga! Informacje na tej stronie mają ponad 5 lat. Nadal je udostępniam, ale prawdopodobnie nie odzwierciedlają one mojej aktualnej wiedzy ani przekonań.

# Coding Texture Preview

17:17
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

Comments

STAT NO AD
[Stat] [STAT NO AD] [Download] [Dropbox] [pub] [Mirror]
Copyright © 2004-2017