Simple Parallax Mapping not working properly

Sorry, posted this in the wrong subforum.

Hi, after my per pixel lightning and bump map shaders which work fine I now try to realize parallax mapping, first without occlusion and relief.

I read many tutorials about it and understood the math behind it.

In Vertex Shader my view or eye vector is converted to tangentspace:

I read out the height from the heightmap with and multiply it with the uniforms depthStrength and add bias:

offset_depth = (texture2D(normalMap, texCoord).a) * depthStrength + bias;

this offset gets multiplied with the viewVector which is in tangentspace

offset = offset_depth * viewVector_t.xy;

then I use this offset for my diffuse and normal texture coordinates:

diffuseMap = texture2D(textureDiffuse, texCoord + offset);
normal_t = texture2D(normalMap, texCoord + offset).rgb * 2.0 - 1.0;

here my vertex shader:

<div class=“ubbcode-block”><div class=“ubbcode-header”>Warning, Spoiler: <input type=“button” class=“form-button” value=“Show” onclick=“if (this.parentNode.parentNode.getElementsByTagName(‘div’)[1].getElementsByTagName(‘div’)[0].style.display != ‘’) { this.parentNode.parentNode.getElementsByTagName(‘div’)[1].getElementsByTagName(‘div’)[0].style.display = ‘’;this.innerText = ‘’; this.value = ‘Hide’; } else { this.parentNode.parentNode.getElementsByTagName(‘div’)[1].getElementsByTagName(‘div’)[0].style.display = ‘none’; this.innerText = ‘’; this.value = ‘Show’; }” />]<div style=“display: none;”>

//uniforms

uniform float LightSourceconstantAttenuation, LightSourcelinearAttenuation, LightSourcequadraticAttenuation;
uniform vec4 LightSourcePosition;

//varyings

varying float attenuation, nxOL ;
varying vec3 viewVector_t, lightVector_t, viewVector_v, normal_v;

//attributes

attribute vec3 Tangent;

//constants

//functions initialising

vec3 ConvertToTangent(vec3 c_vec, vec3 tangent, vec3 binormal, vec3 normal);

////////////////
//////Main//////
////////////////

void main()
{

////////////////
//initialising//
////////////////

float dist;
vec3 position_v, tangent_v, viewVector_v, binormal_v, v,
normal_v, lightVector_v, lightVector_w;

/////////////////////
//global references//
/////////////////////

gl_Position = ftransform();
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;

///////////////
////vectors////
///////////////

///////////////
//viewspace_v//
///////////////
normal_v = normalize(gl_NormalMatrix * gl_Normal);
tangent_v = normalize(gl_NormalMatrix * Tangent);
binormal_v = cross(normal_v, tangent_v);
position_v = vec3(gl_ModelViewMatrix * gl_Vertex);
viewVector_v = normalize(-position_v);
lightVector_v = LightSourcePosition.xyz - position_v;

////////////////
//worldspace_w//
////////////////

lightVector_w = LightSourcePosition.xyz - gl_Vertex.xyz;

//////////////////
//tangentspace_t//
//////////////////

v = ConvertToTangent(lightVector_v, tangent_v, binormal_v, normal_v);
lightVector_t = v;
v = ConvertToTangent(viewVector_v, tangent_v, binormal_v, normal_v);
viewVector_t = v;

////////////////
//calculations//
////////////////

dist = length(lightVector_w);

lightVector_v = normalize(lightVector_v);

attenuation = 1.0 / (LightSourceconstantAttenuation +
LightSourcelinearAttenuation * dist +
LightSourcequadraticAttenuation * pow(dist,2.0));

//parallax offset

nxOL = dot(normal_v, viewVector_v);

}

/////////////////////
//////Functions//////
/////////////////////

//This function converts the incoming vector c_vec to tangent space;

vec3 ConvertToTangent(vec3 c_vec, vec3 t, vec3 b, vec3 n) {
vec3 v;
v.x = dot(c_vec, t);
v.y = dot(c_vec, b);
v.z = dot(c_vec, n);
return v;
}
[/QUOTE]</div>

here the fragment shader:

<div class=“ubbcode-block”><div class=“ubbcode-header”>Warning, Spoiler: <input type=“button” class=“form-button” value=“Show” onclick=“if (this.parentNode.parentNode.getElementsByTagName(‘div’)[1].getElementsByTagName(‘div’)[0].style.display != ‘’) { this.parentNode.parentNode.getElementsByTagName(‘div’)[1].getElementsByTagName(‘div’)[0].style.display = ‘’;this.innerText = ‘’; this.value = ‘Hide’; } else { this.parentNode.parentNode.getElementsByTagName(‘div’)[1].getElementsByTagName(‘div’)[0].style.display = ‘none’; this.innerText = ‘’; this.value = ‘Show’; }” />]<div style=“display: none;”>
//uniforms

uniform float FrontMaterialshininess, depthStrength, bias;
uniform vec4 LightSourcediffuse, LightSourceambient, LightSourcespecular;
uniform sampler2D textureDiffuse, normalMap;

//varyings

varying float attenuation, nxOL;
varying vec3 viewVector_t, lightVector_t, viewVector_v;

//attributes

//constants

//functions initialising

////////////////
//////Main//////
////////////////

void main()
{

////////////////
//initialising//
////////////////
vec2 offset, texCoord;
vec3 reflectVector, normal_t;
vec4 diffuse, specular, diffuseMap;
float nxDir, specularPower, nxHalf, offset_depth;

///////////
//vectors//
///////////

//////////////////
//tangentspace_t//
//////////////////

viewVector_t = normalize(viewVector_t);
lightVector_t = normalize(lightVector_t);

////////////
//textures//
////////////
texCoord = vec2(gl_TexCoord[0].s * 12.0, gl_TexCoord[0].t * 3.5);

offset_depth = (texture2D(normalMap, texCoord).a) * depthStrength + bias;
offset = offset_depth * viewVector_t.xy;

diffuseMap = texture2D(textureDiffuse, texCoord + offset);
normal_t = texture2D(normalMap, texCoord + offset).rgb * 2.0 - 1.0;

///////////
//diffuse//
///////////

nxDir = max(0.0, dot(normal_t, lightVector_t));
diffuse = LightSourcediffuse * nxDir * attenuation;

////////////
//Specular//
////////////

specular = vec4 (0.0);
specularPower = 0.0;

if(nxDir != 0.0) {
reflectVector = normalize(reflect(-lightVector_t, normal_t));
reflectVector = normalize(2.0 * dot(normal_t, lightVector_t)* normal_t - lightVector_t);
nxHalf = max(0.0, dot(reflectVector, normal_t));
specularPower = pow(nxHalf, FrontMaterialshininess);
specular = LightSourcespecular * specularPower * attenuation;
}

////////////
//Parallax//
////////////

////////////////
//output color//
////////////////

 gl_FragColor = ((diffuse + LightSourceambient) ) * vec4(diffuseMap.rgb, 1.0) +
                 (specular * diffuseMap.a);

}
[/QUOTE]</div>

At some positions the parallax effect looks fine, if I rotate the cylinder, the offset is wrong (red) and should be like the yellow arrows.

Since hours I try to solve this but I have no idea.

Have you tried swizzling your view vector in the offset calculation? Like viewVector_t.yz etc. Anyway it looks like the viewvector is off. I remember having to swizzle my viewvector after transforming it by TBN.

Make the offset_depth large to make the error more obvious, and move the camera about you might get an idea of what is wrong that way.

i tried swizzling them and inverting one, and this error occurred always,

I just followed many tutorials and some example demo shaders which are working fine.

And even tried this shader from yoyoo, but this shader doesnt works for myself either.

here my new code:

<div class=“ubbcode-block”><div class=“ubbcode-header”>Warning, Spoiler: <input type=“button” class=“form-button” value=“Show” onclick=“if (this.parentNode.parentNode.getElementsByTagName(‘div’)[1].getElementsByTagName(‘div’)[0].style.display != ‘’) { this.parentNode.parentNode.getElementsByTagName(‘div’)[1].getElementsByTagName(‘div’)[0].style.display = ‘’;this.innerText = ‘’; this.value = ‘Hide’; } else { this.parentNode.parentNode.getElementsByTagName(‘div’)[1].getElementsByTagName(‘div’)[0].style.display = ‘none’; this.innerText = ‘’; this.value = ‘Show’; }” />]<div style=“display: none;”>
//uniforms

uniform float LightSourceconstantAttenuation, LightSourcelinearAttenuation, LightSourcequadraticAttenuation;
uniform vec4 LightSourcePosition;

//attributes

attribute vec3 tangent;
attribute vec3 binormal;

//varyings

varying float attenuation, nxOL ;
varying vec3 viewVector_t, lightVector_t, viewVector_v, normal_t, view_c_to_view_w;

//constants

////////////////
//////Main//////
////////////////

void main()
{

////////////////
//initialising//
////////////////

float dist;
vec3 position_v, tangent_v, viewVector_v, binormal_v,
normal_v, lightVector_v, lightVector_w;

/////////////////////
//global references//
/////////////////////

gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;

///////////////
////vectors////
///////////////

///////////////
//viewspace_v//
///////////////
normal_v = normalize(gl_NormalMatrix * gl_Normal);
tangent_v = normalize(gl_NormalMatrix * tangent);
binormal_v = normalize(gl_NormalMatrix * binormal);
position_v = vec3(gl_ModelViewMatrix * gl_Vertex);
viewVector_v = normalize(-position_v);
lightVector_v = LightSourcePosition.xyz - position_v;

////////////////
//worldspace_w//
////////////////

lightVector_w = LightSourcePosition.xyz - gl_Vertex.xyz;

//////////////////
//tangentspace_t//
//////////////////

mat3 TBN_Matrix = gl_NormalMatrix * mat3(tangent, binormal, gl_Normal);

viewVector_t = position_v * TBN_Matrix ;
lightVector_t = lightVector_v * TBN_Matrix;
normal_t = normal_v * TBN_Matrix;

////////////////
//calculations//
////////////////

dist = length(lightVector_w);

lightVector_v = normalize(lightVector_v);

attenuation = 1.0 / (LightSourceconstantAttenuation +
LightSourcelinearAttenuation * dist +
LightSourcequadraticAttenuation * pow(dist,2.0));

//parallax offset

nxOL = dot(normal_v, viewVector_v);

 gl_Position = ftransform();

}

[/QUOTE]</div>

Fragment:

<div class=“ubbcode-block”><div class=“ubbcode-header”>Warning, Spoiler: <input type=“button” class=“form-button” value=“Show” onclick=“if (this.parentNode.parentNode.getElementsByTagName(‘div’)[1].getElementsByTagName(‘div’)[0].style.display != ‘’) { this.parentNode.parentNode.getElementsByTagName(‘div’)[1].getElementsByTagName(‘div’)[0].style.display = ‘’;this.innerText = ‘’; this.value = ‘Hide’; } else { this.parentNode.parentNode.getElementsByTagName(‘div’)[1].getElementsByTagName(‘div’)[0].style.display = ‘none’; this.innerText = ‘’; this.value = ‘Show’; }” />]<div style=“display: none;”>
//uniforms

uniform float FrontMaterialshininess, depthStrength, bias;
uniform vec4 LightSourcediffuse, LightSourceambient, LightSourcespecular;
uniform sampler2D textureDiffuse, normalMap;

//varyings

varying float attenuation, nxOL;
varying vec3 viewVector_t, lightVector_t, viewVector_v, normal_t,
view_c_to_view_w;

//attributes

//constants

//functions initialising

////////////////
//////Main//////
////////////////

void main()
{

////////////////
//initialising//
////////////////
vec2 offset, texCoord;
vec3 reflectVector, normal_t;
vec4 diffuse, specular, diffuseMap;
float nxDir, specularPower, nxHalf, offset_depth;

///////////
//vectors//
///////////

//////////////////
//tangentspace_t//
//////////////////

viewVector_t = normalize(viewVector_t);
lightVector_t = normalize(lightVector_t);

texCoord = vec2(gl_TexCoord[0].s * 6.0, gl_TexCoord[0].t * 3.0);

////////////
//parallax//
////////////
view_c_to_view_w= normalize(view_c_to_view_w);
offset_depth = (texture2D(normalMap, texCoord).a) * depthStrength - bias;
offset = offset_depth * viewVector_t.xy * nxOL;

////////////
//textures//
///////////

diffuseMap = texture2D(textureDiffuse, texCoord + offset);
normal_t = texture2D(normalMap, texCoord + offset).rgb * 2.0 - 1.0;

///////////
//diffuse//
///////////

nxDir = max(0.0, dot(normal_t, lightVector_t));
diffuse = LightSourcediffuse * nxDir * attenuation;

////////////
//Specular//
////////////

specular = vec4 (0.0);
specularPower = 0.0;

if(nxDir != 0.0) {
reflectVector = normalize(reflect(-lightVector_t, normal_t));
reflectVector = normalize(2.0 * dot(normal_t, lightVector_t)* normal_t - lightVector_t);
nxHalf = max(0.0, dot(reflectVector, normal_t));
specularPower = pow(nxHalf, FrontMaterialshininess);
specular = LightSourcespecular * specularPower * attenuation;
}

////////////
//Parallax//
////////////

////////////////
//output color//
////////////////

  gl_FragColor = vec4(
 
 -viewVector_t.x, 
 -viewVector_t.y,
 -viewVector_t.z,
 
 0);  

 gl_FragColor = ((diffuse + LightSourceambient) ) * vec4(diffuseMap.rgb, 1.0) +
                 (specular * diffuseMap.a);

}
[/QUOTE]</div>

Maybe someone could just test my code and check the directions of the offset and make a screenshot.

I haven’t read the whole code, but i assume the view-vector is of unit length so multiplying is not enough because only the z-length of the vector should be offset_depth, so you should multiply offset_depth by offset_depth / viewVector_t.z. If I didn’t make clear what I mean, I could draw a picture. And even though this is probably not your mistake, I hope it helped.

Edit:
I noticed you wrote normal_v * TBN_Matrix. Is vector * matrix even defined? And wouln’t the right way be the other way round. However you multiplied both factors with the NormalMatrix, so it’s taken into account twice, right? Is all that intended?

Edit 2:
In OpenGL you specify the columns of a matrix, not the rows, so shouldn’t you take the transpose the TBN before multiplying it with your vectors?

Edit 3 :D:
In an article by Eric Lengyel binormal is claimed to be a wrong term for the bitangent. Is that true? (still everybody uses “binormal”) (Computing Tangent Space Basis Vectors for an Arbitrary Mesh)

In an article by Eric Lengyel binormal is claimed to be a wrong term for the bitangent. Is that true?

It’s a practical joke. He made it up to cause folks a lot of unnecessary confusion ;p

Old habits die hard. Speaking of old habits, got to give up cow tipping…

Thanks for the reply.

you can call your binormal or tangent whatevery you want

In Rendermonkey at Stream Mapping you must initialise this attributes:

This was my main error most of the times, I dont know where my tangents and binormals came from, because they were not initialised.

Now it works fine, but The TBN Matrix must be the right handed variable, even if this is strange, in math matrix calculations
the left matrix rows must be equal to the right matrix columns, but with 1x3 Vector * 3x3 Matrix this is not given.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.