Today i decided to start an OpenSource Project “GFXMFF” wich stands for “GFXMaker File Format”, its a Game oriented file format exported by 3dsMax (Currently),it contains informations about a 3d scene : Materials,Meshs,Cameras,Lights,Animations( Key Framed,Sampled ),Skins / Bones. here’s the Git repository URL : git://codaset.com/fatehmtd/gfxmff.git

 

GFXMFF

I’ve been working on this effect for sometime now , here’s a video that demonstrates POM in GFXMaker

 

here’s the HLSL code for this technique:

 

 

////////////////////////////////////////////
struct MATERIAL
{      
       float4 Diffuse;
       float4 Ambient;
       float4 Specular;
       float4 Emissive;
       float  Shininess;
};
////////////////////////////////////////////
struct LIGHT
{
    unsigned int    Type;            /* Type of light source */
    float4          Diffuse;         /* Diffuse color of light */
    float4          Specular;        /* Specular color of light */
    float4          Ambient;         /* Ambient color of light */
    float3          Position;         /* Position in world space */
    float3          Direction;        /* Direction in world space */
    float           Range;            /* Cutoff range */
    float           Falloff;          /* Falloff */
    float           Attenuation0;     /* Constant attenuation */
    float           Attenuation1;     /* Linear attenuation */
    float           Attenuation2;     /* Quadratic attenuation */
    float           Theta;            /* Inner angle of spotlight cone */
    float           Phi;              /* Outer angle of spotlight cone */
};
////////////////////////////////////////////////////////////////////////////////////////

MATERIAL Material;  // The Current Material

bool     HasColorMap ,
         HasNormalMap,
         HasSpecularMap,
         HasHeightMap;

texture  ColorMap,  // Diffuse Map
         NormalMap, // Normal Map , used for PerPixel Lighting
         SpecularMap,
         GlossMap,
         HeightMap;

sampler ColorMap_Sampler = sampler_state
{
        Texture = ColorMap;   
};

sampler SpecularMap_Sampler = sampler_state
{
        Texture = SpecularMap;
};

sampler NormalMap_Sampler = sampler_state
{
        Texture = NormalMap;
};

sampler GlossMap_Sampler = sampler_state
{
        Texture = GlossMap;
};

sampler HeightMap_Sampler = sampler_state
{
        Texture = HeightMap;
};

///////////////////////////////////////////////
unsigned int  MaxLights = 16,numLights = 0;

LIGHT             Lights[16];

 

////////////////////////////////////////////////////////////////////////////////////////
///////  Shared Vars
////////////////////////////////////////////////////////////////////////////////////////

float4x4 matView ,
         matProjection ,
         matWorld ,
         matWorldInverseTranspose;

float3   ViewerPosition,ViewerDirection;

float3   MeshPivot = float3(0,0,0);  // The current mesh’s Pivot/Origin

 

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Parallax Occlusion Mapping Rendering
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct VS_POMR_OUT
{
       float4 Position   : POSITION;      
       float2 UV         : TEXCOORD0;
       float3 ViewDirTS   : TEXCOORD1;
       float3 LightDir   : TEXCOORD2;
       float3 NormalWS   : TEXCOORD3;
       float3 ViewDirWS   : TEXCOORD4;
       float2 ParallaxOffset : TEXCOORD5;
};

float  HeightMapScale = 0.03f;
int    LODThreshold   = 3;        // The mip level id for transitioning between the full computation
                                  // for parallax occlusion mapping and the bump mapping computation
float ShadowSoftening = 0.12;

int   MinimumSamples = 10 , MaximumSamples = 40;

VS_POMR_OUT Parallax_Ocllusion_Mapping_Rendering_VS_Main(VS_INPUT input)
{
        VS_POMR_OUT output = (VS_POMR_OUT)0 ;
        output.Position = mul(input.Position,mul(mul(matWorld,matView),matProjection));

        float3 NormalWS   = (mul(input.Normal  ,(float3x3)matWorld));
        float3 TangentWS  = (mul(input.Tangent ,(float3x3)matWorld));
        float3 BinormalWS = (mul(input.Binormal,(float3x3)matWorld));
        output.NormalWS   = NormalWS;

        NormalWS   = normalize(NormalWS);
        TangentWS  = normalize(TangentWS);
        BinormalWS = normalize(BinormalWS);

        float3x3 TangentSpaceMatrix = (float3x3(TangentWS,BinormalWS,NormalWS));

        float3 dir;
        switch(Lights[0].Type)
        {
               case 3:
               dir = Lights[0].Direction;
               break;
               default:
               dir =  Lights[0].Position – mul(input.Position,matWorld);
               break;
        }

        float3 eyedir = ViewerPosition – mul(input.Position,matWorld);

        output.UV        = input.UV;
        output.LightDir  = mul(TangentSpaceMatrix,dir);

        output.ViewDirWS = eyedir;
        output.ViewDirTS = mul(TangentSpaceMatrix,output.ViewDirWS);

        float2 ParallaxDirection = normalize(output.ViewDirTS.xy);
        float  len            = length(output.ViewDirTS);
        float  ParallaxLen    = sqrt(len*len-output.ViewDirTS.z*output.ViewDirTS.z)/output.ViewDirTS.z;

        output.ParallaxOffset = ParallaxDirection * ParallaxLen;
        output.ParallaxOffset *= HeightMapScale;

        return output;
}

float4 ComputeIllumination(float3 LightDirTS,float2 TexCoord,float3 ViewDirTS)
{
       float3 Normal   = normalize(tex2D(NormalMap_Sampler,TexCoord).xyz*2-1);
       float3 LightDir = normalize(LightDirTS );

       float3 Reflection    = normalize(-reflect(LightDir,Normal));
       float4 totalSpecular = pow(max(0.0,saturate(dot(Reflection,ViewDirTS))),Material.Shininess) * Material.Specular * Lights[0].Specular;
       float4 totalAmbient  = Material.Ambient;
       float4 totalDiffuse  = Material.Diffuse * saturate(dot(LightDir,Normal));

       float4 final = saturate((totalAmbient + totalDiffuse)*tex2D(ColorMap_Sampler,TexCoord) + totalSpecular);      
       return final;
}

float4 Parallax_Ocllusion_Mapping_Rendering_PS_Main(VS_POMR_OUT input) : COLOR
{
       float3 ViewDirWS = normalize(input.ViewDirWS);      
       float3 NormalWS  = normalize(input.NormalWS);
       float3 ViewDirTS = normalize(input.ViewDirTS);
       float2 Offset    = input.ParallaxOffset;
       float3 NormalTS   = normalize(tex2D(NormalMap_Sampler,input.UV).xyz*2-1);
       float OcclusionShadow = 1;

      float2 fTexCoordsPerSize = input.UV * float2(256,256);

      float2 dxSize, dySize;
      float2 dx, dy;

      float4( dxSize, dx ) = ddx( float4( fTexCoordsPerSize, input.UV ) );
      float4( dySize, dy ) = ddy( float4( fTexCoordsPerSize, input.UV ) );
      float  fMipLevel;     
      float  fMipLevelInt;
      float  fMipLevelFrac;

      float  fMinTexCoordDelta;
      float2 dTexCoords;

      dTexCoords = dxSize * dxSize + dySize * dySize;

      fMinTexCoordDelta = max( dTexCoords.x, dTexCoords.y );

      fMipLevel = max( 0.5 * log2( fMinTexCoordDelta ), 0 );
      float2 texSample = input.UV;
      if(fMipLevel<=(float)LODThreshold)
      {
         int    Samples  = (int)lerp(MaximumSamples,MinimumSamples,saturate(dot(NormalWS,ViewDirWS)));
         float  StepSize = 1.0/(float)Samples;

         float2 OffsetStep    = Offset * StepSize;
         float2 CurrentOffset = input.UV;
         float  CurrentHeight = 0.0, LastHeight = 1.0;
         float  CurrentBound  = 1.0, ParallaxAmount = 0.0;

         float2 pt1 = 0, pt2 = 0;

         int    CurrentSampleID = 0;

         float2 texOffset2 = 0;

         while(CurrentSampleID<Samples)
         {
               CurrentOffset -= OffsetStep;
               CurrentBound  -= StepSize;
               CurrentHeight = tex2Dgrad(NormalMap_Sampler,CurrentOffset,dx,dy).a;

               if(CurrentHeight > CurrentBound)
               {
                  pt1 = float2( CurrentBound, CurrentHeight );
                  pt2 = float2( CurrentBound + StepSize, LastHeight );

                  texOffset2 = CurrentOffset – OffsetStep;
                  CurrentSampleID = Samples + 1;
               }
               else
               {
                  CurrentSampleID ++;
                  LastHeight = CurrentHeight;
               }
         }

         float fDelta2 = pt2.x – pt2.y;
         float fDelta1 = pt1.x – pt1.y;

         float fDenominator = fDelta2 – fDelta1;

         if ( fDenominator == 0.0f )
         {
             ParallaxAmount = 0.0f;
         }
         else
         {
             ParallaxAmount = (pt1.x * fDelta2 – pt2.x * fDelta1 ) / fDenominator;
         }

         float2 ParallaxOffset = Offset * (1.0 – ParallaxAmount);
         float2 TextureSample = input.UV – ParallaxOffset;
         texSample = TextureSample;
         if(fMipLevel>(float)(LODThreshold-1))
         {
            fMipLevelFrac = modf( fMipLevel, fMipLevelInt );
            texSample = lerp( TextureSample,input.UV,fMipLevelFrac );
         }
        float2 vLightRayTS = input.LightDir.xy * HeightMapScale;

        float sh0 =  tex2Dgrad( NormalMap_Sampler, TextureSample, dx, dy ).a;
        float shA = (tex2Dgrad( NormalMap_Sampler, TextureSample + vLightRayTS * 0.88, dx, dy ).a – sh0 – 0.88 ) *  1 * ShadowSoftening;
        float sh9 = (tex2Dgrad( NormalMap_Sampler, TextureSample + vLightRayTS * 0.77, dx, dy ).a – sh0 – 0.77 ) *  2 * ShadowSoftening;
        float sh8 = (tex2Dgrad( NormalMap_Sampler, TextureSample + vLightRayTS * 0.66, dx, dy ).a – sh0 – 0.66 ) *  4 * ShadowSoftening;
        float sh7 = (tex2Dgrad( NormalMap_Sampler, TextureSample + vLightRayTS * 0.55, dx, dy ).a – sh0 – 0.55 ) *  6 * ShadowSoftening;
        float sh6 = (tex2Dgrad( NormalMap_Sampler, TextureSample + vLightRayTS * 0.44, dx, dy ).a – sh0 – 0.44 ) *  8 * ShadowSoftening;
        float sh5 = (tex2Dgrad( NormalMap_Sampler, TextureSample + vLightRayTS * 0.33, dx, dy ).a – sh0 – 0.33 ) * 10 * ShadowSoftening;
        float sh4 = (tex2Dgrad( NormalMap_Sampler, TextureSample + vLightRayTS * 0.22, dx, dy ).a – sh0 – 0.22 ) * 12 * ShadowSoftening;

        OcclusionShadow = 1 – max( max( max( max( max( max( shA, sh9 ), sh8 ), sh7 ), sh6 ), sh5 ), sh4 );

        OcclusionShadow = OcclusionShadow * 0.6 + 0.4;   
      }

      float4 final = ComputeIllumination(input.LightDir,texSample,ViewDirTS)*OcclusionShadow;
      return final;
}

technique ParallaxOcclusionMappingRendering
{
     pass Pass0
     {
          VertexShader = compile vs_3_0 Parallax_Ocllusion_Mapping_Rendering_VS_Main();
          PixelShader  = compile ps_3_0 Parallax_Ocllusion_Mapping_Rendering_PS_Main();
     }
}

My Previous Works

Posted: 29 October 2009 in Previous Work

here are some screenshots of some of my previous works using GFXMaker

Tree_Shadows

space_journey2

space_journey3

SNOWSHOT

SNOWSHOT0

sea

SeaSHOT

FPSSHOT0

FPSSHOT1

FPSSHOT2

FPSSHOT3

FPSSHOT4

CLOCKSHOT

CLOCKSHOT1

CLOCKSHOT2

 

you might ask “why would i write my own printf ???”,the answer is : custom error reporting, custom logs …, in my case i wrote for my Game Engine so i wont bother myself using sprintf & then printing the result.

lets get it started !

the secret behind “printf” is “vprintf” it works like “printf” but instead of requiring a “format” string & some arguments , it requires a destination buffer( just like “sprintf” ) + a “format” string + an argument list wich is a “va_list” type ( typedef char * va_list) its just a char pointer.

here’s a piece of code:

#include <stdargs.h>
#include <stdio.h>

// prototype function
void InGamePrint(const char * textToPrint); // this could be your in game print function

void custum_print(const char* Format,…)
{
// the “…” indicates that this function accepts a variable number of arguments
char Buffer[512]; // you can define your own buffer’s size
va_start(args,Format);
vsprintf(Buffer,Format,args);
va_end(args);
// now everything is written on “Buffer” , so it is ready for use with “InGamePrint”

InGamePrint(Buffer);  // finaly print the text
}