使用 LibGDX 的法线贴图 GLSL

Normal mapping GLSL using LibGDX

我尝试使用 LibGDX 实现法线贴图。所以当我在顶点着色器中计算漫反射和镜面反射颜色时,我得到了一些积极的结果(至少我是这么认为的)。

顶点着色器:

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);           
}

片段着色器:

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);             
}

结果:

如您所见,结果已正确呈现。高光点的行为与许多示例中的一样。但我需要在片段着色器中实现镜面反射颜色以获得更令人印象深刻的画面。所以我从 here 中找到了例子,现在我正在努力让它发挥作用。

顶点着色器:

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);  
}

片段着色器:

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);
}    

结果:

高光点错误。我认为这是由于错误的矩阵计算而发生的。如果这是真的,为什么前几个着色器可以正常工作? 如何在 LibGDX 中获取 model-view 矩阵、normal 矩阵和其他矩阵?

 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);

所以,我的问题是最后几个着色器有什么问题?

问题出在我的模型(网格)中。经过一段时间的挖掘,我发现我的网格没有 tangentsbinormals。发生这种情况是因为我使用了 Blender 2.58,它无法使用正切 space 将模型导出到 FBX。幸运的是,他们在 Blender 2.71 及更高版本中修复了此错误。因此,当您导出模型时,您应该勾选 Tangent Space 参数并选择版本 FBX 7.4 binary。为确保您的网格包含切线和双法线,您可以使用 fbx-converter 工具将 fbx 文件转换为 g3dj 格式而不是默认的 g3db 格式,方法如下:

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

然后在Notepad等程序中打开它并检查属性是否包含TANGENTBINORMAL

"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,

只有一件事我不明白,为什么 lamberFactor 使用错误的切线属性(实际上,没有它)?最有可能的是,漫反射颜色计算错误,但在我的示例(在球体上)中它看起来非常令人满意。我希望它能帮助其他人。

编辑

顺便说一下,为了得到一个普通矩阵和一个模型视图矩阵,我使用了下面的代码:

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