Tangent space and parallactic shader..

Hello all, I am trying to implement a parallactic GLSL shader, but I got stuck on handedness of my generated tangent space. I can’t for the life of me find a way to make this thing work properly. Here’s a screenshot of what I m getting

http://www.ramiriccardo.com/nlcachef/sshots/parallax-seams.jpg

The problem is with those visible triangles on the wing. It should be smooth, but it is not.

Here is my code to generate the tangent space…

    private void createTangentSpace(){
        //Creates a tangent space to be used with
        //per-pixel lighting
        meshes.reset();
        Mesh currentMesh=null;
        while((currentMesh=(Mesh)meshes.getNext())!=null){
            for(int i=0;i<currentMesh.numFaces;i++){
                //Find the tangent basis
                Vector3f p1=new Vector3f(currentMesh.vertices[currentMesh.faces[i].vertices[0]].m_location);
                Vector3f p2=new Vector3f(currentMesh.vertices[currentMesh.faces[i].vertices[1]].m_location);
                Vector3f p3=new Vector3f(currentMesh.vertices[currentMesh.faces[i].vertices[2]].m_location);
                
                Vector2f uv1=new Vector2f(currentMesh.vertices[currentMesh.faces[i].vertices[0]].m_texCoords);
                Vector2f uv2=new Vector2f(currentMesh.vertices[currentMesh.faces[i].vertices[1]].m_texCoords);
                Vector2f uv3=new Vector2f(currentMesh.vertices[currentMesh.faces[i].vertices[2]].m_texCoords);
                
                Vector3f Edge1 = p2.subtract(p1);
                Vector3f Edge2 = p3.subtract(p1);
                Vector2f Edge1uv = uv2.subtract(uv1);
                Vector2f Edge2uv = uv3.subtract(uv1);
                
                float cp = Edge1uv.y * Edge2uv.x - Edge1uv.x * Edge2uv.y;
                
                if ( cp != 0.0f ) {
                    float mul = 1.0f / cp;
                    currentMesh.faces[i].tangent   = Edge1.mulByf(-Edge2uv.y).sum(Edge2.mulByf(Edge1uv.y)).mulByf(mul);
                    currentMesh.faces[i].biTangent = Edge1.mulByf(-Edge2uv.x).sum(Edge2.mulByf(Edge1uv.x)).mulByf(mul);
                    
                    currentMesh.faces[i].tangent.normalize();
                    currentMesh.faces[i].biTangent.normalize();
                }
            }
            
            //Now smoothen the vectors accross the vertices.
            for(int i=0;i<currentMesh.vertices.length;i++){
                int[] neighbors = currentMesh.findVertexOwnerFaces(i);
                Vector3f vecTangent=new Vector3f();
                Vector3f vecBiTangent=new Vector3f();
                
                Vector3f vecNormal=new Vector3f(currentMesh.vertices[i].m_normal[0],
                                                currentMesh.vertices[i].m_normal[1],
                                                currentMesh.vertices[i].m_normal[2]);
                
                for(int n=0;n<neighbors.length;n++){
                    vecTangent.sumOW(currentMesh.faces[neighbors[n]].tangent);
                    vecBiTangent.sumOW(currentMesh.faces[neighbors[n]].biTangent);
                }
                vecTangent.normalizeOW();
                vecBiTangent.normalizeOW();
                
                //Gram-Schmidt orthogonalization
                vecTangent.subtractOW(vecNormal.mulByf(vecTangent.dotProduct(vecNormal)));
                vecTangent.normalize();
                
                vecBiTangent.subtractOW(vecNormal.mulByf(vecBiTangent.dotProduct(vecNormal)));
                vecBiTangent.normalize();                
                
                currentMesh.vertices[i].m_tangent[0]=vecBiTangent.x;
                currentMesh.vertices[i].m_tangent[1]=vecBiTangent.y;
                currentMesh.vertices[i].m_tangent[2]=vecBiTangent.z;

                currentMesh.vertices[i].m_bitangent[0]=vecTangent.x;
                currentMesh.vertices[i].m_bitangent[1]=vecTangent.y;
                currentMesh.vertices[i].m_bitangent[2]=vecTangent.z;                
            }
        }
    }

The Vertex shader:


attribute vec3 rm_Tangent;
attribute vec3 rm_Binormal;
varying	vec3 g_lightVec;
varying	vec3 g_viewVec;

void main()
{
float u_invRad =1.0/50.0;
	gl_Position = ftransform();
	gl_TexCoord[0] = gl_MultiTexCoord0;
	
	vec3 binormal=cross(rm_Tangent.xyz, gl_Normal);

	mat3 TBN_Matrix = gl_NormalMatrix * mat3(rm_Tangent, rm_Binormal, gl_Normal);
	vec4 mv_Vertex = gl_ModelViewMatrix * gl_Vertex;
	g_viewVec = vec3(-mv_Vertex) * TBN_Matrix;
	vec4 light = gl_ModelViewMatrix * gl_LightSource[1].position;
	vec3 lightVec = u_invRad * (light.xyz - mv_Vertex.xyz);
	g_lightVec = lightVec * TBN_Matrix;
}

And finally the Fragment shader:


uniform sampler2D bump_map;
uniform sampler2D rgb_map;

varying	vec3 g_lightVec;
varying	vec3 g_viewVec;

void main()
{   
        vec2 cBumpSize=0.01 * vec2 (2.0, -1.0);
	float LightAttenuation = clamp(1.0 - dot(g_lightVec, g_lightVec), 0.0, 1.0);
	vec3 lightVec = normalize(g_lightVec);
	vec3 viewVec = normalize(g_viewVec);
	
	float height = texture2D(bump_map, gl_TexCoord[0].xy).r;
	height = height * cBumpSize.x + cBumpSize.y;

	vec2 newUV = gl_TexCoord[0].xy + viewVec.xy * height;
	vec4 color_base = texture2D(rgb_map,newUV);
	vec3 bump = texture2D(bump_map, newUV.xy).rgb * 2.0 - 1.0;
	bump = normalize(bump);
	
	float diffuse = clamp(dot(lightVec, bump), 0.0, 1.0);
	float specular = pow(clamp(dot(reflect(-viewVec, bump), lightVec), 0.0, 1.0), 16.0);
	gl_FragColor =  3.0*color_base * gl_LightSource[1].diffuse
					* (diffuse * base + 3.0*specular);
        gl_FragColor = 0.2*color_base+(0.2* gl_FragColor)+gl_FragColor;
    gl_FragColor.a = 1.0;	
}

I m losing it trying to figure out what I m doing wrong… I’d be very grateful if anyone could help me out.
Thanks.

After having a quick look over your code it looks to be the calculation of the averages… I’m not quite sure where because your code is written in a different way to which I do it.

Here is the way I do it, hopefully it will help you out:

For each face I calculate the TBN, then add it to any attached vertices like so (The updateFacePerVertex is what you are after):


public class IndexedFace extends RealtimeObject {
  private int[] vertIndex = new int[3];
  private int[] texIndex = new int[3];

  private Vector3f normal = new Vector3f( 0, 0, 0 );
  private Vector3f biTangent = new Vector3f( 0, 0, 0 );
  private Vector3f tangent = new Vector3f( 0, 0, 0 );

  private static final RealtimeObject.Factory<IndexedFace> FACTORY = new RealtimeObject.Factory<IndexedFace>() {
    protected IndexedFace create() {
      return new IndexedFace();
    }
  };

  private IndexedFace() {
  }

  @NotNull
  public static IndexedFace newInstance() {
    return FACTORY.object();
  }

  private Vector3f v2v1 = new Vector3f();
  private Vector3f v3v1 = new Vector3f();

  public synchronized void updateFacePerVertex( List<CalculatingVector3f> verts, List<TextureCoord> texCoords ) {
    //Calculate the vectors from the current vertex to the two other vertices in the triangle
    v2v1.sub( verts.get( vertIndex[1] ).vector, verts.get( vertIndex[0] ).vector );
    v3v1.sub( verts.get( vertIndex[2] ).vector, verts.get( vertIndex[0] ).vector );

    // Calculate c2c1_T and c2c1_B
    float c2c1_T = texCoords.get( texIndex[1] ).getU() - texCoords.get( texIndex[0] ).getU();
    float c2c1_B = texCoords.get( texIndex[1] ).getV() - texCoords.get( texIndex[0] ).getV();

    // Calculate c3c1_T and c3c1_B
    float c3c1_T = texCoords.get( texIndex[2] ).getU() - texCoords.get( texIndex[0] ).getU();
    float c3c1_B = texCoords.get( texIndex[2] ).getV() - texCoords.get( texIndex[0] ).getV();

    float fDenominator = c2c1_T * c3c1_B - c3c1_T * c2c1_B;
    float fScale1 = 1.0f / fDenominator;

    tangent.x = ( c3c1_B * v2v1.x - c2c1_B * v3v1.x ) * fScale1;
    tangent.y = ( c3c1_B * v2v1.y - c2c1_B * v3v1.y ) * fScale1;
    tangent.z = ( c3c1_B * v2v1.z - c2c1_B * v3v1.z ) * fScale1;

    biTangent.x = ( -c3c1_T * v2v1.x + c2c1_T * v3v1.x ) * fScale1;
    biTangent.y = ( -c3c1_T * v2v1.y + c2c1_T * v3v1.y ) * fScale1;
    biTangent.z = ( -c3c1_T * v2v1.z + c2c1_T * v3v1.z ) * fScale1;

    normal.cross( tangent, biTangent );
    normal.normalize();

    verts.get( vertIndex[0] ).addComponent( normal, biTangent, tangent );
    verts.get( vertIndex[1] ).addComponent( normal, biTangent, tangent );
    verts.get( vertIndex[2] ).addComponent( normal, biTangent, tangent );

    verts.get( vertIndex[0] ).setTextureCoord( texCoords.get( texIndex[0] ) );
    verts.get( vertIndex[1] ).setTextureCoord( texCoords.get( texIndex[1] ) );
    verts.get( vertIndex[2] ).setTextureCoord( texCoords.get( texIndex[2] ) );
}

Then I iterate though these verts and get the average normal ect:


  public void addComponent( Vector3f normal, Vector3f biNormal, Vector3f tangent ) {
    this.normal.add( normal );
    this.biNormal.add( biNormal );
    this.tangent.add( tangent );
  }

  public Vector3f getVertexNormal() {
    Vector3f buffer = new Vector3f();
    buffer.normalize( normal );
    return buffer;
  }

Thank you very much, I’ll try that as soon as I can, will let you know if I manage to fix my problem.

I still have a problem somewhere, but the whole thing got much better. I should be able to tackle it with some more effort~
Thanks for your help.

Here’s how it looks now, can still see triangles a bit, but altogether it’s almost acceptable.
http://www.ramiriccardo.com/nlcachef/sshots/parallax-seams2.jpg

http://www.ramiriccardo.com/nlcachef/sshots/parallax-seams2.jpg