10
votes

I try to implement normal mapping using LibGDX. So I got some positive results when I calculate diffuse and specular color in vertex shader (at least I think so).

Vertex shader:

attribute vec4 a_position;
attribute vec2 a_texCoord0;
attribute vec3 a_normal;    

varying vec2 v_texCoord;
varying float v_diffuse;
varying vec3 v_specular;
varying vec3 v_lightVec;

uniform mat4 u_worldTrans;
uniform mat4 u_projTrans;
uniform mat4 u_matViewInverseTranspose;
uniform mat4 u_matModelView;

const vec3 lightVector = vec3(0.0,0.0,-1.0);

void main()
{
  // Output the unmodified vertex position. 
  gl_Position = u_projTrans * u_worldTrans * a_position;

  mat3 normalMatrix = mat3(u_matViewInverseTranspose);
  // compute the transformed normal
  vec3 n = normalize(normalMatrix * a_normal);

  // compute the light vector pointing toward the sun, in model coordinates
  // x,y compose the longitude and z the (seasonal) lattitude of the nadir point.
  //vec3 lightVec = normalize(vec3(u_matViewInverseTranspose * vec4(u_lightVec, 1.0)));
  vec3 lightVec = normalize(normalMatrix * lightVector);

  // Calculate a diffuse light intensity
  //v_diffuse = dot(lightVec, n);
  v_diffuse = clamp(dot(n, lightVec), 0.0, 1.0);

  vec4 ecPosition = u_matModelView * a_position;
  // compute the reflection vector
  vec3 reflectVec = reflect(-lightVec, n);
  // compute a unit vector in direction of viewing position
  vec3 viewVec    = normalize(vec3(-ecPosition));

  // Calculate specular light intensity, scale down and apply a tint. 
  float specIntensity = pow(max(dot(reflectVec, viewVec), 0.0), 8.0);       
  v_specular            = specIntensity  * 
                        //gloss color
                        vec3(1.,.7,.3) * 
                        //gloss intensity
                        .7;     

  v_texCoord.y = 1.-a_texCoord0.y;
  v_texCoord.x = a_texCoord0.x; 

  vec3 lightDir = normalize(lightVector - u_matModelView * a_position);

  vec3 tangent=a_tangent;
  vec3 t = normalize(normalMatrix * tangent);
  vec3 b = cross (n, t);    

  vec3 v;
  v.x = dot (lightDir, t);
  v.y = dot (lightDir, b);
  v.z = dot (lightDir, n);  

  v_lightVec = normalize (v);           
}

Fragment shader:

precision mediump float;

varying vec2 v_texCoord;
varying float v_diffuse;
varying vec3 v_specular;
varying vec3 v_lightVec;

uniform sampler2D u_texture;
uniform sampler2D u_normalMap;

void main()
{   
  vec3 ground = texture2D(u_texture, v_texCoord).rgb;

  vec3 normal  = normalize(2.0 * texture2D (u_normalMap, v_texCoord).rgb - 1.0);
  float lamberFactor = max (dot (normal, v_lightVec), 0.0);   

  vec3 color = ( ground.rgb * v_diffuse * lamberFactor + v_specular);

  gl_FragColor = vec4 (color, 1.0);             
}

Result: Correct result

As you can see the result is rendered correctly. Specular spot behaves like from many examples. But I need to implement specular color in fragment shader to get more impressive picture. So I found example from here and now I'm trying to make it works.

Vertex shader:

attribute vec4 a_position;
attribute vec2 a_texCoord0;
attribute vec3 a_normal;
attribute vec3 a_tangent; 

varying vec2 v_texCoord;   
varying vec3 v_lightVec;
varying vec3 v_eyeVec;  //Added

uniform mat4 u_worldTrans;
uniform mat4 u_projTrans;
uniform mat4 u_matViewInverseTranspose;
uniform mat4 u_matModelView;

const vec3 lightVector = vec3(0.0,0.0,-1.0);

void main()
{
  // Output the unmodified vertex position. 
  gl_Position = u_projTrans * u_worldTrans * a_position;

  mat3 normalMatrix = mat3(u_matViewInverseTranspose);
  // compute the transformed normal
  vec3 n = normalize(normalMatrix * a_normal);

  v_texCoord.y = 1.-a_texCoord0.y;
  v_texCoord.x = a_texCoord0.x; 

  vec3 lightDir = normalize(lightVector - u_matModelView * a_position);

  vec3 tangent=a_tangent;
  vec3 t = normalize(normalMatrix * tangent);
  vec3 b = cross (n, t);    

  vec3 v;
  v.x = dot (lightDir, t);
  v.y = dot (lightDir, b);
  v.z = dot (lightDir, n);  

  v_lightVec = normalize (v);

  //Added
  vec3 ecPosition = u_matModelView * a_position;  

  vec3 tmp = vec3(-ecPosition);
  v_eyeVec.x = dot(tmp, t);
  v_eyeVec.y = dot(tmp, b);
  v_eyeVec.z = dot(tmp, n);
  v_eyeVec = normalize (v_eyeVec);  
}

Fragment shader:

precision mediump float;

varying vec2 v_texCoord;
varying vec3 v_lightVec;
varying vec3 v_eyeVec; 

uniform sampler2D u_texture;
uniform sampler2D u_normalMap;

void main()
{   
  vec3 ground = texture2D(u_texture, v_texCoord).rgb;       
  vec3 normal  = normalize(2.0 * texture2D (u_normalMap, v_texCoord).rgb - 1.0);

  //Added
  float distSqr = dot(v_lightVec, v_lightVec);
  float att = clamp(1.0 - .25 * sqrt(distSqr), 0.0, 1.0);
  vec3 lVec = v_lightVec * inversesqrt(distSqr);

  vec3 vVec = normalize(v_eyeVec);  
  vec3 bump = normalize( texture2D(u_normalMap, v_texCoord).xyz * 2.0 - 1.0);

  float diffuse = max( dot(lVec, bump), 0.0 );

  vec3 specular = pow(clamp(dot(reflect(-lVec, bump), v_eyeVec), 0.0, 1.0), 8.0 ) *
                        //gloss color
                        vec3(1.,.7,.3) * 
                        //gloss intensity
                        .7; 

  vec3 color = ( ground.rgb * diffuse + specular) * att;          
  gl_FragColor = vec4 (color, 1.0);
}    

Result:

Wrong result

Specular spot is wrong. I thought it happens because of wrong matrix calculation. If it's true why do first couple of shaders work correctly? How do I get model-view matrix, normal matrix and others in LibGDX?

 viewInvTraMatrix.set(camera.view);     
 viewInvTraMatrix.mul(renderable.worldTransform);
 //model-view matrix
 program.setUniformMatrix("u_matModelView", viewInvTraMatrix);
 viewInvTraMatrix.inv(); //inverse
 viewInvTraMatrix.tra(); //transpose
 //normal matrix
 program.setUniformMatrix("u_matViewInverseTranspose", viewInvTraMatrix);

 //other matrix
 program.setUniformMatrix("u_worldTrans", renderable.worldTransform);
 program.setUniformMatrix("u_projTrans", camera.combined);

So, my question is what is wrong in the last couple of shaders?

1
Why don't you take a look here. Its a bit long, but it might helpFish
You've pointed on shadow mapping, But I am asking about normal mapping! Be attentive!Nolesh
Bump and normal are computing the same value, and I've used something like specpower 3 instead of 8, but otherwise the pixelshader seems correct. I'm not sure about the "matViewInverseTranspose", since it is supposed to hold the inverse transpose of the model matrix only. Since you also used it as the "matModelView", it seems it also holds the view transform. But the material matrix should only be computed on the model matrix.StarShine
AFAIK the standard shaders that libgdx provides already support normal mapping. Why don't you just use those?noone
Maybe I'm wrong and this has been removed. I found github.com/libgdx/libgdx/tree/… which seems to still have it.noone

1 Answers

7
votes

The problem was in my model(mesh). After some time of digging I found out that my mesh doesn't have tangents and binormals. It happend because I used Blender 2.58 which cannot export model to FBX with tangent space. Fortunately, they fixed this bug in Blender 2.71 and later. So, when you export your model, you should tick Tangent Space parameter and choose version FBX 7.4 binary. To ensure that your mesh contains tangents and binormals, you can use fbx-converter tool to convert your fbx file to g3dj format instead of default g3db by using additional option like below:

fbx-converter.exe -o g3dj yourModel.fbx yourConvertedModel.g3dj

Then open it in some program like Notepad and check that attributes contain TANGENT and BINORMAL

"version": [  0,   1], 
"id": "", 
"meshes": [
    {
        "attributes": ["POSITION", "NORMAL", "TANGENT", "BINORMAL", "TEXCOORD0"], 
        "vertices": [
             0.257400,  58.707802, -0.257400,  0.000000,  1.000000, -0.000000,  0.941742,

Only one thing I don't understand, why does lamberFactor work with wrong tangent attribute(actually, without it)? Most likely, diffuse color is wrong calculated, but in my example(on sphere) it looks pretty satisfactorily. I hope it will help somebody else.

EDIT

By the way, to get a normal & a model-view matrices, I use the following code:

myMatrix.set(camera.view);  
myMatrix.mul(renderable.worldTransform);
program.setUniformMatrix(u_matModelView, myMatrix); //pass model-view matrix
myMatrix.inv();
myMatrix.tra();
program.setUniformMatrix(u_matNormal, myMatrix); //pass normal matrix