Google
 

Friday, August 17, 2007

Generic XNA - SM3 Fog



So here is the SM3 (Shader Model 3) HLSL fog. Now this is taken pretty much 99% from Leaf's example, but thought I would comment on how easy this technique is to put into your existing shaders.

I also found that adding this to a shader pushes the operation count up a fair bit, the limit in SM2 is 64 operations per shader, so 128 in all, 64 in the Vertex Shader and another 64 in the Pixel Shader. So What I did was put the fog code into a second pass so giving me another 64x64 operation slots to play with.



So here are the shaders:

This first shader is the one used for the buildings you see in the shots, it is a single pass and a simple texture shader.

ShaderFog.fx

float4x4 World : World;
float4x4 WorldViewProject : WorldViewProjection;
float3 EyePosition : CameraPosition;
texture thisTexture;
float3  LightPos  = (0,-1,0);

float4 ambient = {0.25, 0.25, 0.25, 1.0};
float4 diffuse = {1.0, 1.0, 1.0, 1.0};
float4 specularColor = {0.2, 0.2, 0.2, 1.0};
float shininess = 40;

float fogNear = 10;
float fogFar = 100;
float fogAltitudeScale = 10;
float fogThinning = 100;
float4 fogColor = {0.5, 0.5, 0.5, 1.0};

sampler TextureSampler = sampler_state
{
   Texture = <thisTexture>;
};

struct VS_INPUT
{
   float4 Position : POSITION0;
   float2 Texcoord : TEXCOORD0;
   float3 Normal : NORMAL0;
};

struct VS_OUTPUT
{
   float4 Position : POSITION;
   float2 Texcoord : TEXCOORD0;
   float3 Normal : TEXCOORD1;    
   float3 WorldPos : TEXCOORD2;
};
struct PS_INPUT
{
   float4 Position : TEXCOORD4;
   float2 Texcoord : TEXCOORD0;
   float3 Normal : TEXCOORD1;    
   float3 WorldPos : TEXCOORD2;
};

float4 blinn2(
       float3 N,
       float3 L,
       float3 V,
       uniform float4 diffuseColor,
       uniform float4 specularColor,
       uniform float shininess)
{
   float3 H = normalize(V+L);
   float4 lighting = lit(dot(L,N), dot(H,N), shininess);
   return diffuseColor*lighting.y + specularColor*lighting.z;
}


VS_OUTPUT Transform(VS_INPUT Input)
{
   VS_OUTPUT Output;

   Output.WorldPos = mul(Input.Position,World);
   Output.Position = mul(Input.Position, WorldViewProject);
   Output.Texcoord = Input.Texcoord;
   Output.Normal = mul(Input.Normal,World);

   return Output;
}

float4 Texture(PS_INPUT Input) : COLOR0
{
   float4 colorMap = tex2D(TextureSampler, Input.Texcoord.xy) * 1.5;
   float3 N = normalize(Input.Normal);
   float3 V = normalize(EyePosition - Input.WorldPos);
   float3 L = normalize(LightPos - Input.WorldPos);
   
   float4 C = ambient*colorMap;
   
   C += blinn2(N, L, V, colorMap * diffuse, specularColor * colorMap.a, shininess);
   float d = length(Input.WorldPos - EyePosition);
   float l = saturate((d - fogNear) / (fogFar - fogNear) / clamp(Input.Position.y / fogAltitudeScale + 1, 1, fogThinning));
   return lerp(C, fogColor, l);    
};

technique TransformTexture
{
   pass P0
   {
       VertexShader = compile vs_1_1 Transform();
       PixelShader  = compile ps_2_0 Texture();
   }
}



This is the shader I use for the floor tile, again this is a single pass. Just a variation on the theam really.

Floor.fx

float4x4 World : World;
float4x4 WorldViewProject : WorldViewProjection;
float3 EyePosition : CameraPosition;
texture thisTexture;
float3  LightPos  = (0,-1,0);

float4 ambient = {0.25, 0.25, 0.25, 1.0};
float4 diffuse = {1.0, 1.0, 1.0, 1.0};
float4 specularColor = {0.2, 0.2, 0.2, 1.0};
float shininess = 0;

float fogNear = 10;
float fogFar = 100;
float fogAltitudeScale = 10;
float fogThinning = 100;
float4 fogColor = {0.5, 0.5, 0.5, 1.0};

sampler TextureSampler = sampler_state
{
   Texture = <thisTexture>;
};

struct VS_INPUT
{
   float4 Position : POSITION0;
   float2 Texcoord : TEXCOORD0;
   float3 Normal : NORMAL0;
};

struct VS_OUTPUT
{
   float4 Position : POSITION;
   float2 Texcoord : TEXCOORD0;
   float3 Normal : TEXCOORD1;    
   float3 WorldPos : TEXCOORD2;
};
struct PS_INPUT
{
   float4 Position : TEXCOORD4;
   float2 Texcoord : TEXCOORD0;
   float3 Normal : TEXCOORD1;    
   float3 WorldPos : TEXCOORD2;
};

float4 blinn2(
       float3 N,
       float3 L,
       float3 V,
       uniform float4 diffuseColor,
       uniform float4 specularColor,
       uniform float shininess)
{
   float3 H = normalize(V+L);
   float4 lighting = lit(dot(L,N), dot(H,N), shininess);
   return diffuseColor*lighting.y + specularColor*lighting.z;
}


VS_OUTPUT Transform(VS_INPUT Input)
{
   VS_OUTPUT Output;

   Output.WorldPos = mul(Input.Position,World);
   Output.Position = mul(Input.Position, WorldViewProject);
   Output.Texcoord = Input.Texcoord * 500;
   Output.Normal = mul(Input.Normal,World);

   return Output;
}

float4 Texture(PS_INPUT Input) : COLOR0
{
   float4 colorMap = tex2D(TextureSampler, Input.Texcoord.xy);
   float3 N = normalize(Input.Normal);
   float3 V = normalize(EyePosition - Input.WorldPos);
   float3 L = normalize(LightPos - Input.WorldPos);
   
   float4 C = ambient*colorMap;
   
   C += blinn2(N, L, V, colorMap * diffuse, specularColor * colorMap.a, shininess);
   float d = length(Input.WorldPos - EyePosition);
   float l = saturate((d - fogNear) / (fogFar - fogNear) / clamp(Input.Position.y / fogAltitudeScale + 1, 1, fogThinning));
   return lerp(C, fogColor, l);
};

technique TransformTexture
{
   pass P0
   {
       VertexShader = compile vs_1_1 Transform();
       PixelShader  = compile ps_2_0 Texture();
   }
}


And finaly this is the shader used for the sky box, now, you may think, why is he using a sky box when he has such dense fog? Well it is here as an example and this is where I have put the fog calcs into the second pass so you can see how to do it (or rather how I did it)


/////////////////////////////////////////////////////////////
//                                                            //
//    Writen by C.Humphrey                                    //
//    26/07/2007                                                //
//                                                            //
//                                                            //
//    Shader used to render a cube map to an inverted box        //
//    mesh.                                                    //
//                                                            //
// 17/08/2006 Multi pass fog aded.                            //
//                                                            //
//////////////////////////////////////////////////////////////

Texture surfaceTexture;
samplerCUBE TextureSampler = sampler_state
{
   texture = <surfaceTexture> ;
   magfilter = LINEAR;
   minfilter = LINEAR;
   mipfilter = LINEAR;
   AddressU = Mirror;
   AddressV = Mirror;
};

float4x4 World : World;
float4x4 View : View;
float4x4 Projection : Projection;

float3 EyePosition : CameraPosition;

float4 ambient = {0.25, 0.25, 0.25, 1.0};
float4 diffuse = {1.0, 1.0, 1.0, 1.0};
float4 specularColor = {0.2, 0.2, 0.2, 1.0};
float shininess = 10;

float fogNear = 500;
float fogFar = 1000;
float fogAltitudeScale = 0;
float fogThinning = 1;
float4 fogColor = {0.5, 0.5, 0.5, 1.0};

float4 c;

struct VS_INPUT
{
   float4 Position    : POSITION0;
   float3 Normal : NORMAL0;    
};

struct VS_OUTPUT
{
   float4 Position    : POSITION0;
   float3 ViewDirection : TEXCOORD2;
   float3 Normal : TEXCOORD0;
   float4 WorldPos : TEXCOORD1;
};

float4 CubeMapLookup(float3 CubeTexcoord)
{    
   return texCUBE(TextureSampler, CubeTexcoord);
}

VS_OUTPUT Transform(VS_INPUT Input)
{
   float4x4 WorldViewProjection = mul(mul(World, View), Projection);
   float3 ObjectPosition = mul(Input.Position, World);
   
   VS_OUTPUT Output;
   Output.Position    = mul(Input.Position, WorldViewProjection);    
   
   Output.ViewDirection = EyePosition - ObjectPosition;    
   
   Output.WorldPos = mul(Input.Position,World);
   Output.Normal = mul(Input.Normal,World);
   
   return Output;
}

float4 blinn2(
       float3 N,
       float3 L,
       float3 V,
       uniform float4 diffuseColor,
       uniform float4 specularColor,
       uniform float shininess)
{
   float3 H = normalize(V+L);
   float4 lighting = lit(dot(L,N), dot(H,N), shininess);
   return diffuseColor*lighting.y + specularColor*lighting.z;
}

struct PS_INPUT
{    
   float3 ViewDirection : TEXCOORD2;
   float3 Normal : TEXCOORD0;
   float3 WorldPos : TEXCOORD1;
   float4 Position : TEXCOORD3;
};

float4 BasicShader(PS_INPUT Input) : COLOR0
{    
   float3 ViewDirection = normalize(Input.ViewDirection);        
   float4 C = CubeMapLookup(-ViewDirection);
 
   c = C;
   return C;
}
float4 Fog(PS_INPUT Input) : COLOR0
{
   float3 ViewDirection = normalize(Input.ViewDirection);        
   
   float3 N = normalize(Input.Normal);
   float3 V = normalize(EyePosition - Input.WorldPos);
   float3 L = normalize(Input.WorldPos);
   L = normalize(Input.WorldPos);
   
   float4 C = c;
 
   C += blinn2(N, L, V, C * diffuse, specularColor * C.a, shininess);
   float d = fogFar-(fogFar/35);
   float l = saturate((d - fogNear) / (fogFar - fogNear) / clamp(Input.Position.y / fogAltitudeScale + 1, 1, fogThinning));
   return lerp(C, fogColor, l);
}

technique BasicShader
{
   pass P0
   {
       VertexShader = compile vs_2_0 Transform();
       PixelShader  = compile ps_2_0 BasicShader();
   }
   pass P1
   {
       AlphaBlendEnable    = true;
       SrcBlend = SrcAlpha;
       DestBlend = InvSrcAlpha;
       PixelShader  = compile ps_2_0 Fog();
   }
}




Controls
Mouse rotates camera
Arrow Keys translate camera
Esc to exit.

GenericExampleShaderFog.zip

Thanks to Leaf for his example on the XNA UK User Group Without it I would not know how to do it...

Thursday, August 16, 2007

Generic XNA - How to get images from a cube map

OK, may be a bit of an odd post, but I thought I would put it up any way.

I have 2 methods for creating a skybox now, either by using 6 textured quads and positioning them around the camera at set distance and scale or using an inverted cube mesh and then applying a cube map texture to it. So for both these methods I need textures to generate these skybox's, and the majority of images I have found were in cube maps but this then means that I can only use them in one of my skybox methods.

So I had a look at the DirectX Texture tool to see if there was a way to dissect a cube map there (as this is where I create my maps at the moment) but could not find how to do this here. So I decided to write my own.

In short, what I am posting here is a method to take a given cube map and break it up into it's 6 constituent parts and save them as jpg files so you can use them in the textured quad skybox method.

Here is the code:
   cube = game.Content.Load<TextureCube>("Content/textures/skybox/CubeMap");
   Color[][] sideData = new Color[6][];

   for (int s = 0; s < 6; s++)
   {
       sideData[s] = new Color[cube.Size * cube.Size];
       cube.GetData<Color>((CubeMapFace)s, sideData[s]);
       Texture2D side = new Texture2D(cube.GraphicsDevice, cube.Size, cube.Size, 0, ResourceUsage.AutoGenerateMipMap, cube.Format);
       side.SetData<Color>(sideData[s]);
       side.Save("Content/textures/skybox/" + ((CubeMapFace)s).ToString() + ".jpg", ImageFileFormat.Jpg);
   }



I guess you could reverse this to take 6 images and generate a cube map possibly to build a cube map to be used as an environment cube map used in a reflection shader or just to create a cube map for a skybox.

I intend to post an example of creating cube maps on the fly so that reflective shades will give true reflections.

Hope you find it useful.