Parallax Occlusion Mapping

A place to show off your latest screenshots and for people to comment on them. Only start a new thread here if you have some nice images to show off!
Caphalor
Greenskin
Posts: 116
Joined: Tue Feb 06, 2007 8:54 pm
Location: Berlin, Germany
x 25

Re: Parallax Occlusion Mapping

Post by Caphalor »

You should also have a look at cone step mapping, it seems to be better then POM.


I found this on my computer, I hope it is the latest and best version (I didn't use the shader for months):

Shader:

Code: Select all

void POM_Vert_Multilight(float4 position   : POSITION, 
              float3 normal      : NORMAL, 
              float2 uv         : TEXCOORD0, 
              float3 tangent     : TANGENT0, 
              // outputs 
              out float4 oPosition    : POSITION, 
              out float2 oUv          : TEXCOORD0, 
             out float3 oEyeDir       : TEXCOORD1, 
             out float3 oEyeDirWS       : TEXCOORD2,              
             out float3 oNormal		  : TEXCOORD3,
		  	out float3 oAttenuation: TEXCOORD4,     
		  	out float3 oLightDir1    : TEXCOORD5,
		  	out float3 oLightDir2    : TEXCOORD6, 
		  	out float3 oLightDir3    : TEXCOORD7,
              // parameters 
              uniform float scale,
              uniform float4 lightPosition1,
              uniform float4 lightPosition2,
              uniform float4 lightPosition3,
              uniform float3 eyePosition, 
              uniform float4x4 worldViewProj,
              uniform float4x4 world_matrix,
              uniform float4 lightAttenuation1,
              uniform float4 lightAttenuation2,
              uniform float4 lightAttenuation3) 
{  
   // calculate output position 
   oPosition = mul(worldViewProj, position); 

   // pass the main uvs straight through unchanged 
   oUv = uv * scale; 

   float Dist1 = length(lightPosition1 - position);
   float Dist2 = length(lightPosition2 - position);
   float Dist3 = length(lightPosition3 - position);
   oAttenuation = float3(1/(lightAttenuation1.y + lightAttenuation1.z * Dist1 + lightAttenuation1.w * Dist1 * Dist1), 1/(lightAttenuation2.y + lightAttenuation2.z * Dist2 + lightAttenuation2.w * Dist2 * Dist2), 1/(lightAttenuation3.y + lightAttenuation3.z * Dist3 + lightAttenuation3.w * Dist3 * Dist3));
   
   // calculate tangent space light vector 
   // Get object space light direction 
   float3 lightDir1 = lightPosition1.xyz -  (position * lightPosition1.w);
   float3 lightDir2 = lightPosition2.xyz -  (position * lightPosition2.w);
   float3 lightDir3 = lightPosition3.xyz -  (position * lightPosition3.w);
   
   oEyeDirWS = eyePosition - position.xyz; 
   oNormal = normal; 
   
   normal = normalize(normal);
   tangent = normalize(tangent);
   float3 binormal = cross(tangent, normal); 
    
   // Form a rotation matrix out of the vectors 
   float3x3 rotation = float3x3(tangent, binormal, normal); 
    
   // Transform the light vector according to this matrix 
   oLightDir1 = (mul(rotation, lightDir1)); 
   oLightDir2 = (mul(rotation, lightDir2)); 
   oLightDir3 = (mul(rotation, lightDir3)); 
   oEyeDir = (mul(rotation, oEyeDirWS)); 

}

void POM_Frag_Multilight(
		  float2 uv	: TEXCOORD0,
		  float3 eyeDir : TEXCOORD1,
		  float3 eyeDirWS       : TEXCOORD2,   
		  float3 iNormal: TEXCOORD3,
		  float3 Attenuation: TEXCOORD4,
		  float3 lightVec1 : TEXCOORD5,
		  float3 lightVec2 : TEXCOORD6,
		  float3 lightVec3 : TEXCOORD7,

		  out float4 oColor	: COLOR, 

		  uniform float4 lightDiffuse1,
		  uniform float4 lightDiffuse2,
		  uniform float4 lightDiffuse3,
		  uniform float4 lightAmbient,
		  uniform float4 lightSpecular1,
		  uniform float4 lightSpecular2,
		   uniform float4 lightSpecular3,
		  uniform float spec_exponent,
		  uniform float spec_factor,
		  uniform float fHeightMapScale,

		uniform sampler2D normalHeightMap : register(s0),
		uniform sampler2D diffuseMap : register(s1)
		)
{
    // Compute the ray direction for intersecting the height field profile with 
    // current view ray. See the above paper for derivation of this computation. (Ati's comment)
         
    // Compute initial parallax displacement direction: (Ati's comment)
    float2 vParallaxDirection = normalize(  eyeDir.xy );
       
    // The length of this vector determines the furthest amount of displacement: (Ati's comment)
    float fLength         = length( eyeDir );
    float fParallaxLength = sqrt( fLength * fLength - eyeDir.z * eyeDir.z ) / eyeDir.z; 
       
    // Compute the actual reverse parallax displacement vector: (Ati's comment)
    float2 vParallaxOffsetTS = vParallaxDirection * fParallaxLength;
       
    // Need to scale the amount of displacement to account for different height ranges
    // in height maps. This is controlled by an artist-editable parameter: (Ati's comment)
    vParallaxOffsetTS *= fHeightMapScale; 
    	
	eyeDir = normalize(eyeDir);
	lightVec1 = normalize(lightVec1);
	lightVec2 = normalize(lightVec2);
	lightVec3 = normalize(lightVec3);
	float3 N = normalize( iNormal );
	eyeDirWS = normalize(eyeDirWS);
	float3 halfAngle1 = normalize(eyeDir + lightVec1); 
	float3 halfAngle2 = normalize(eyeDir + lightVec2); 
	float3 halfAngle3 = normalize(eyeDir + lightVec3); 
	      
	float2 newTexCoord = uv;	
      
	float nMinSamples = 6;
	float nMaxSamples = 70;	
	int nNumSamples = (int)(lerp( nMinSamples, nMaxSamples, 1-dot( eyeDirWS, N ) ));	//In reference shader: int nNumSamples = (int)(lerp( nMinSamples, nMaxSamples, dot( eyeDirWS, N ) ));
	float fStepSize = 1.0 / (float)nNumSamples;	
   	float fCurrHeight = 0.0;
    float fPrevHeight = 1.0;
    float fNextHeight = 0.0;
    int    nStepIndex = 0;
    float2 vTexOffsetPerStep = fStepSize * vParallaxOffsetTS;
    float2 vTexCurrentOffset = uv;
    float  fCurrentBound     = 1.0;
    float  fParallaxAmount   = 0.0;

    float2 pt1 = 0;
    float2 pt2 = 0;	    
         	      	
    while ( nStepIndex < nNumSamples ) 
    {
		vTexCurrentOffset -= vTexOffsetPerStep;

        // Sample height map which in this case is stored in the alpha channel of the normal map: (Ati's comment)
        fCurrHeight = tex2Dlod( normalHeightMap, float4(vTexCurrentOffset, 0, 0)).a; 

        fCurrentBound -= fStepSize;

        if ( fCurrHeight > fCurrentBound ) 
        {   
           pt1 = float2( fCurrentBound, fCurrHeight );
           pt2 = float2( fCurrentBound + fStepSize, fPrevHeight );

           nStepIndex = nNumSamples + 1;	//Exit loop
           fPrevHeight = fCurrHeight;
        }
        else
        {
           nStepIndex++;
           fPrevHeight = fCurrHeight;
        }
    } 
    float fDelta2 = pt2.x - pt2.y;
    float fDelta1 = pt1.x - pt1.y;
      
    float fDenominator = fDelta2 - fDelta1;
      
    // SM 3.0 requires a check for divide by zero, since that operation will generate
    // an 'Inf' number instead of 0, as previous models (conveniently) did: (Ati's comment)
    if ( fDenominator == 0.0f )
    {
        fParallaxAmount = 0.0f;
    }
    else
    {
        fParallaxAmount = (pt1.x * fDelta2 - pt2.x * fDelta1 ) / fDenominator;
    }
      
    float2 vParallaxOffset = vParallaxOffsetTS * (1 - fParallaxAmount );
	newTexCoord = uv - vParallaxOffset;  
    
  	//Do some ordinary Parallax Mapping / Normal Mapping using the new coordinates:
  	   	
	float3 PixelNormal = expand(tex2D(normalHeightMap, newTexCoord).xyz);
	PixelNormal = normalize(PixelNormal);
	float3 diffuse = tex2D(diffuseMap, newTexCoord).xyz;

	float NdotL = dot(normalize(lightVec1), PixelNormal);
	float NdotH = dot(normalize(halfAngle1), PixelNormal); 
	float4 Lit1 = lit(NdotL,NdotH,spec_exponent);	
	NdotL = dot(normalize(lightVec2), PixelNormal);
	NdotH = dot(normalize(halfAngle2), PixelNormal); 
	float4 Lit2 = lit(NdotL,NdotH,spec_exponent);	
	NdotL = dot(normalize(lightVec3), PixelNormal);
	NdotH = dot(normalize(halfAngle3), PixelNormal); 
	float4 Lit3 = lit(NdotL,NdotH,spec_exponent);		
	float3 AmbientCol = diffuse * lightAmbient;
	float3 DiffuseCol = (Lit1.y * lightDiffuse1 * Attenuation.x) + (Lit2.y * lightDiffuse2 * Attenuation.y) + (Lit3.y * lightDiffuse3 * Attenuation.z);
	float3 SpecularCol = lightSpecular1 * Lit1.z * spec_factor * Attenuation.x + lightSpecular2 * Lit2.z * spec_factor * Attenuation.y + lightSpecular3 * Lit3.z * spec_factor * Attenuation.z;
		
	oColor = float4(AmbientCol + diffuse * DiffuseCol + diffuse*SpecularCol, 1);
}
Program ref and abstract material:

Code: Select all

vertex_program POM_Vert_Multilight hlsl
{
 	source Lightening.hlsl
	entry_point POM_Vert_Multilight
	target vs_3_0 	 
 
 	default_params
	{
		param_named scale float 1
		param_named_auto lightPosition1 light_position_object_space 0
		param_named_auto lightPosition2 light_position_object_space 1
		param_named_auto lightPosition3 light_position_object_space 2
		param_named_auto eyePosition camera_position_object_space
		param_named_auto worldViewProj worldviewproj_matrix
		param_named_auto lightAttenuation1 light_attenuation 0 
		param_named_auto lightAttenuation2 light_attenuation 1 
		param_named_auto lightAttenuation3 light_attenuation 2 
	}  
}

fragment_program POM_Frag_Multilight hlsl
{
	source Lightening.hlsl 
	entry_point POM_Frag_Multilight
	target ps_3_0		

	default_params
	{
		param_named_auto lightDiffuse1 light_diffuse_colour 0
		param_named_auto lightDiffuse2 light_diffuse_colour 1
		param_named_auto lightDiffuse3 light_diffuse_colour 2
		param_named_auto lightSpecular1 light_specular_colour 0
		param_named_auto lightSpecular2 light_specular_colour 1
		param_named_auto lightSpecular3 light_specular_colour 2
		param_named_auto lightAmbient ambient_light_colour 0
		param_named spec_exponent float 127
		param_named spec_factor float 0.5	
	}
}

abstract material POM
{
	technique
	{	
		pass
		{
			vertex_program_ref POM_Vert_Multilight
			{
				param_named scale float $scale	
			}

			fragment_program_ref POM_Frag_Multilight
			{	
				param_named spec_exponent float $specular_exponent
				param_named spec_factor float $specular_factor
				param_named	fHeightMapScale float $depth
			}

			texture_unit
			{
				texture				$bump_map
				tex_coord_set		0
			}
			texture_unit 
			{
				texture				$diffuse_map
				tex_coord_set		0
			}			
		}		
	
	}
}
Example usage:

Code: Select all

material myMaterial : POM
{
	set $scale 0.5
	set $specular_exponent 127
	set $specular_factor 1
	set $depth 0.05
	set $bump_map bumpmap.tga
	set $diffuse_map diffusemap.tga
}
Last edited by Caphalor on Fri Jun 12, 2009 12:41 pm, edited 1 time in total.
Image
Generated with vBaum - voxel based procedural geometry generator with python interface.
User avatar
rafa.gdev
Halfling
Posts: 56
Joined: Mon Apr 14, 2008 10:10 pm
Location: Belo Horizonte, Brazil

Re: Parallax Occlusion Mapping

Post by rafa.gdev »

Oh, thats great! Thank you very much.
Yes, people definively prefer CSM over POM, but anyway, that's a good source to learn (I'm still in baby stage on programming shaders :lol: ).
lukeneorm
Halfling
Posts: 61
Joined: Wed Apr 01, 2009 12:03 am

Re: Parallax Occlusion Mapping

Post by lukeneorm »

I agree with rafa.gdev: it's a good way to start!
Thank you Caphalor! I'll try your shader asap! :)
style88
Gnoblar
Posts: 1
Joined: Tue Jan 05, 2010 1:15 am

Re: Parallax Occlusion Mapping

Post by style88 »

I can't seem to get this to work. Could someone post a working .zip file, please?
nickG
Greenskin
Posts: 122
Joined: Fri Jan 20, 2012 6:44 pm
Location: Russia,Moscow
x 1

Re: Parallax Occlusion Mapping

Post by nickG »

style88 wrote:I can't seem to get this to work. Could someone post a working .zip file, please?
+1
User avatar
mkultra333
Gold Sponsor
Gold Sponsor
Posts: 1894
Joined: Sun Mar 08, 2009 5:25 am
x 114

Re: Parallax Occlusion Mapping

Post by mkultra333 »

Glancing at this old thread, I see there were concerns about CSM being patent encumbered, in particular with this one here, http://www.patentstorm.us/patents/5355442/fulltext.html or via google, http://www.google.com.mx/patents/US5355442

According to that page, the patent has an "Estimated Expiration Date" of 16 December 2012. I guess that makes sense, a 20 year patent from the filing data of 16 December 1992. So at a glance, it would appear CSM is no longer patent encumbered.

Ah, software patents. In and of themselves the greatest invention ever.
"In theory there is no difference between practice and theory. In practice, there is." - Psychology Textbook.
Post Reply