# Relief/Parallax Occlusion Mapping - How do you calculate the depth offset?

This topic is 746 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi, I have implemented relief mapping and am trying to calculate the correct world space depth offset, resulting from the view vector going into texture space. How would one do that?

Below is what the shader code looks like, it is similar to parallax occlusion mapping with an added binary search part, making it relief mapping. The last lines in the pixel shader is where I currently estimate the resulting depth based on the size of my model but it is not exact and generalized so I need to find a better way.

void VS_RenderGeometryDisplace(float4 position : POSITION0,
float2 texCoord : TEXCOORD0,
float3 normal : NORMAL0,
float3 binormal : BINORMAL0,
float3 tangent : TANGENT0,

out float4 outPosition : POSITION0,
out float3 outTexCoord : TEXCOORD0,
out float3 outNormal : TEXCOORD1,
out float3 outViewVec : TEXCOORD2,
out float3 outLightDir : TEXCOORD3,
out float3 outWorldPos : TEXCOORD4,
out float3x3 tangentToWorldSpace : TEXCOORD5)
{
outPosition = mul(position, worldViewProjMatrix);
outTexCoord.xy = texCoord;
outTexCoord.z = outPosition.z / cameraFarPlane; //depth stored in texCoord
outNormal = mul(float4(normal,0.0f),worldMatrix).xyz;

tangentToWorldSpace[0] = mul(float4(tangent, 0.0f), worldMatrix).xyz;
tangentToWorldSpace[1] = mul(float4(binormal, 0.0f), worldMatrix).xyz;
tangentToWorldSpace[2] = mul(float4(normal, 0.0f), worldMatrix).xyz;

float3x3 worldToTangentSpace = transpose(tangentToWorldSpace);
float3 worldPos = mul(position, worldMatrix).xyz;
float3 viewVec = worldPos - cameraPosWorld;
outViewVec = mul(viewVec, worldToTangentSpace);

outLightDir = mul(-lightDir, worldToTangentSpace);

outWorldPos = worldPos;
}

void PS_RenderGeometryDisplace(float3 texCoord : TEXCOORD0,
float3 inNormal : TEXCOORD1,
float3 inViewVec : TEXCOORD2,
float3 inLightDir : TEXCOORD3,
float3 inWorldPos : TEXCOORD4,
float3x3 tangentToWorldSpace : TEXCOORD5,

out float4 color0_normal : COLOR0,
out float4 color1_depth : COLOR1,
out float4 color2_diffuse : COLOR2,
out float4 color3_illum : COLOR3)
{
float3 camToPix = normalize(inWorldPos - cameraPosWorld);
float viewDot = dot(inNormal, camToPix);
viewDot *= viewDot; //add some bias and make it a positive number

float2 tex = texCoord.xy;

float fParallaxLimit = -length(inViewVec.xy) / inViewVec.z;
fParallaxLimit *= (0.2f + 0.8f * viewDot) * 0.85f; //Scale of displacement

float2 vOffsetDir = normalize(inViewVec.xy);
float2 vMaxOffset = vOffsetDir * fParallaxLimit;

int nMinSamples = 8;
int nMaxSamples = 14;
int nNumSamples = (int)lerp( (float)nMaxSamples, (float)nMinSamples, viewDot ); //Use more samples when viewing the surface at an angle

float fStepSize = 1.0f / (float)nNumSamples;
float2 texOffset = fStepSize * vMaxOffset;

float2 dx = ddx(tex);
float2 dy = ddy(tex);

float fCurrRayHeight = 1.0f;
float2 vCurrOffset = float2(0.0f, 0.0f);
float2 vLastOffset = float2(0.0f, 0.0f);
float fLastSampledHeight = 1.0f;
float fCurrSampledHeight = 1.0f;
int nCurrSample = 0;

//Parallax occlusion mapping / Relief mapping
//Raymarch from surface into texture space to find first spot that is above ray depth
while (nCurrSample < nNumSamples)
{
fCurrSampledHeight = tex2Dgrad(DiffuseMapSampler, tex + vCurrOffset, dx, dy).a;
if (fCurrSampledHeight >= fCurrRayHeight)
{
nCurrSample = nNumSamples + 1;
}
else
{
nCurrSample++;
fCurrRayHeight -= fStepSize;
vLastOffset = vCurrOffset;
vCurrOffset += texOffset;
fLastSampledHeight = fCurrSampledHeight;
}
}

//Binary search
float lastRayHeight = fCurrRayHeight + fStepSize;
for (int i=0; i < 8; i++)
{
float midRayHeight = (lastRayHeight + fCurrRayHeight) * 0.5f;
float2 midOffset = (vCurrOffset + vLastOffset) * 0.5f;
fCurrSampledHeight = tex2Dgrad(DiffuseMapSampler, tex + midOffset, dx, dy).a;
if (fCurrSampledHeight > midRayHeight)
{
vCurrOffset = midOffset;
fCurrRayHeight = midRayHeight;
}
else
{
vLastOffset = midOffset;
lastRayHeight = midRayHeight;
if (i == 7) vCurrOffset = midOffset;
}
}

tex += vCurrOffset; //New texture coordinates

//This is the new world position I'm trying to calculate accurately
float3 newWorldPos = inWorldPos + camToPix * (1.0f - fCurrSampledHeight) * (5.5f + 0.5f * viewDot);

float3 camPos = mul(float4(newWorldPos,1.0f), viewProjMatrix).xyz;
color1_depth = camPos.z / cameraFarPlane;

...



Suggestions on how to calculate the offset world position, the depth or about the code in general are welcome.

Edited by CarlML

##### Share on other sites

So I figured out a solution. I do a ray-plane intersection using the view ray and a plane that is moved down along the surface normal according to the trace depth multiplied with a user defined "reliefDepth" value that should correpond to the observable maximum world space displacement.

float offsetDepth = (reliefDepth * 0.2f + (reliefDepth * 0.8f) * viewDot) * (1.0f - fCurrSampledHeight);
float3 bottomPos = inWorldPos - inNormal * offsetDepth;
float d1 = dot(inNormal, bottomPos - inWorldPos);
float d2 = dot(camToPix, inNormal);
float3 newWorldPos = inWorldPos;
if (d2) newWorldPos += camToPix * (d1 / d2);

The "reliefDepth" variable is adjusted in the same way that the "fParallaxLimit" is, according to view angle. Those two values need to correspond for a good result.

Edited by CarlML

1. 1
2. 2
Rutin
19
3. 3
4. 4
5. 5

• 13
• 26
• 10
• 11
• 9
• ### Forum Statistics

• Total Topics
633736
• Total Posts
3013598
×