Search code examples
opengllwjglvertex-shader

opengl texture coordinates not interpolated correctly due to gl_Position


I am working on opengl joint animation using LWJGL java. All my joint's and vertices get's transformed correctly as expected but weird stuff starts happening when I render my model using texture

VertexShader Code

# version 430 core

uniform mat4
projection,
view,
model;
in vec3 vertex;
uniform mat4 rotate;

in vec2 texCoord;
out vec2 vertexTexCoord;

uniform mat4 joints[16];
in ivec4 jointIndices;
in vec4  weights;

in vec3 normal;
const vec3 directions[3]={vec3(0,-1,0),vec3(0,1,0),vec3(0,0,-1)};
out vec3 vertexNormal;
out vec3 lighting[3];


void main()
{
 mat4 modelRotate=(model*rotate);
 vec4 finalVertex=vec4(0.0);
 vec4 finalNormal=vec4(0.0);

 for(int i=0;i<4;i++)
 {
   mat4 jointTransform=joints[jointIndices[i]];  

   vec4 modelVertex=vec4(vertex,1.0);
   vec4 posVertex=jointTransform*modelVertex;
   finalVertex+=posVertex*weights[i];

   vec4 modelNormal=vec4(normal,0.0);
   vec4 poseNormal=jointTransform*modelNormal;
   finalNormal+=poseNormal*weights[i];
 }
 gl_Position=projection*view*modelRotate*vec4(vertex,1.0);

 vertexNormal=(modelRotate*finalNormal).xyz;
 for(int i=0;i<3;i++){lighting[i]=directions[i]*-1;}

 vertexTexCoord=texCoord;
}

FragmentShader Code

#version 430 core

in vec3 vertexNormal;
in vec3 lighting[3];

in vec2 vertexTexCoord;
uniform sampler2D tex;

out vec4 pixelColor;

void main()
{
 vec3 nNormal=normalize(vertexNormal); 

 vec3 lightColor=vec3(0.0);
 for(int i=0;i<3;i++)
 {
  vec3 nLight=normalize(lighting[i]);  
  float nDot=max(0.0,dot(nNormal,nLight));
  lightColor+=vec3(1,1,1)*nDot;
 }

 pixelColor=vec4(vertexTexCoord.x,vertexTexCoord.y,0,0);
}

I am not using lighting in my model because that has been tested and works fine but it's my texture Coordinates that are incorrect and so I output them in my shader as an Red,Green combo for debugging

There are 2 lines of code in each of my shader that's need's attention

VertexShader:   gl_Position
FragmentShader: pixelColor

when

gl_Position=projection*view*modelRotate*vec4(vertex,1.0);
pixelColor=vec4(vertexTexCoord.x,vertexTexCoord.y,0,0);

That is when my model uses the original input vertices and not the transformed finalVertex

Output enter image description here

As you can see the texture coordinates at every surface is different as it should be that is it is correct.

And so when I render my model using the correct texture with these texture coordinates by changing my pixelColor to

pixelColor=texture(tex,vertexTexCoord);

Output enter image description here

My model is rendered correctly with the texture at the correct places.

Now things get realy weird from this point Changing my shader code to

gl_Position=projection*view*modelRotate*finalVertex;
pixelColor=vec4(vertexTexCoord.x,vertexTexCoord.y,0,0);

Now my model uses the final transformed vertex from each joint and my fragment shader outputs the texture coordinates. The output should be the same as above literaly nothing has changed but then I get this

Output enter image description here

Now The texture coordinates are exatly the same throughout the model and don't get interpolated at all. They are uniform throughout!!!

And now when I use these wrong texture coordinates for sampling by changing my fragment shader code to

pixelColor=texture(tex,vertexTexCoord);

The output is as expected not the one I expected :(

Output: enter image description here

If it helps here is my model texture. I downloaded it from tutorials enter image description here

It seem's that the texture coordinates are stuck to the top left of the image which might explain why my model is full black as there is lot's of black areas in the top left corner

So summarizing By changing one single line of code in my vertex shader from

gl_Position=projection*view*modelRotate*vec4(vertex,1.0);
                    TO
gl_Position=projection*view*modelRotate*finalVertex;

It has changed my life from This enter image description here

To This :(

enter image description here

Any suggestion from anywhere be it remodeling my character or my texture or creating a new rendering engine anything would be hugely appreciated.

Writing the code to Load the model and the joint transforms and hierarchy took me 4 days of work

2 week's later and I still can't figure out what's wrong with my shader's . Thank you

I have an simple static shader here that can load an uniform matrix

public abstract class StaticShader
{
 private int
 programID=0,
 vertexShaderID=0,
 fragmentShaderID=0,
 infoLogSize=0;

 private final FloatBuffer mat4fBuffer=BufferUtils.createFloatBuffer(16);

 protected StaticShader(Object vertexShader,Object fragmentShader)
 {
   programID=GL20.glCreateProgram();  
   vertexShaderID=loadShader(vertexShader,GL20.GL_VERTEX_SHADER);
   fragmentShaderID=loadShader(fragmentShader,GL20.GL_FRAGMENT_SHADER);

   GL20.glAttachShader(programID,vertexShaderID);
   GL20.glAttachShader(programID,fragmentShaderID);
   bindAttributes();   



   GL20.glLinkProgram(programID);
   if(GL20.glGetProgrami(programID,GL20.GL_LINK_STATUS)==GL11.GL_FALSE)
   {  
    infoLogSize=GL20.glGetProgrami(programID,GL20.GL_INFO_LOG_LENGTH);
    System.err.println(GL20.glGetProgramInfoLog(programID,infoLogSize));
    System.err.println("COULD NOT LINK SHADER");
    System.exit(-1);
   } 

   GL20.glValidateProgram(programID); 
   if(GL20.glGetProgrami(programID,GL20.GL_VALIDATE_STATUS)==GL11.GL_FALSE)
   {
    infoLogSize=GL20.glGetProgrami(programID,GL20.GL_INFO_LOG_LENGTH);  
    System.err.println(GL20.glGetProgramInfoLog(programID,infoLogSize));
    System.err.println("COULD NOT VALIDATE SHADER");
    System.exit(-1);
   } 
 }

 protected void bindAttribute(int attribno,String variable){GL20.glBindAttribLocation(programID,attribno,variable);}
 abstract void bindAttributes();

 private int loadShader(Object src,int shaderType)
 {
     StringBuilder source=Utils.loadSource(src);

     int shaderID=GL20.glCreateShader(shaderType);
     GL20.glShaderSource(shaderID,source);
     GL20.glCompileShader(shaderID);

     if(GL20.glGetShaderi(shaderID,GL20.GL_COMPILE_STATUS)==GL11.GL_FALSE)
     {
       infoLogSize=GL20.glGetShaderi(shaderID,GL20.GL_INFO_LOG_LENGTH);
       System.err.println(GL20.glGetShaderInfoLog(shaderID,infoLogSize));
       System.err.println("COULD NOT COMPILE SHADER");
       System.exit(-1);
     }    

     return shaderID;
 }

 public void start(){GL20.glUseProgram(programID);}
 public void stop(){GL20.glUseProgram(0);}
 public void release()
 {
   GL20.glUseProgram(0);

   GL20.glDetachShader(programID,vertexShaderID);
   GL20.glDetachShader(programID,fragmentShaderID);

   GL20.glDeleteShader(vertexShaderID);
   GL20.glDeleteShader(fragmentShaderID);  
 }

 public void loadMatrix(String name,Matrix4f mat)
 {
  start(); 
  mat.store(mat4fBuffer);
  mat4fBuffer.flip();
  GL20.glUniformMatrix4(GL20.glGetUniformLocation(programID,name),false,mat4fBuffer);
  stop();
 }
}

From this shader i inheret MyShader class

public class MyShader extends StaticShader
{
  private static final String
  VERTEX_SHADER="/main/animate.VS",
  FRAGMENT_SHADER="/main/animate.FS";

  private Texture tex;

  public MyShader()
  {
    super(VERTEX_SHADER,FRAGMENT_SHADER);
    Matrix4f mat=new Matrix4f();

     try
     {
      InputStream is=MyShader.class.getResourceAsStream("Character Texture.png");
      tex=TextureLoader.getTexture(".png",is,true);
      is.close();
     }
     catch(Exception ex){ex.printStackTrace();}


     float aspectRatio=(float)Display.getWidth()/(float)Display.getHeight();
     Utils.perspective(50,0.1f,1000,aspectRatio,mat);
     super.loadMatrix("projection",mat);/*PERSPECTIVE MATRIX*/

     Vector3f location=new Vector3f(0,4,12);
     Vector3f lookAt=new Vector3f(0,4,0);
     Vector3f up=new Vector3f(0,1,0);
     Utils.lookAt(location,lookAt,up,mat);
     super.loadMatrix("view",mat);        /*VIEW MATRIX*/

     mat.setIdentity();
     mat.scale(new Vector3f(1.2f,1,1));
     super.loadMatrix("model",mat);       /*MODEL MATRIX*/

     mat.setIdentity();
     mat.rotate((float)Math.toRadians(90),new Vector3f(-1,0,0));
     super.loadMatrix("rotate",mat);     /*FLIP MODEL BY 90 DEGRESS*/

     for(int i=0;i<16;i++)
     {
      mat.setIdentity();/*LOAD ALL JOINT TRANSFORM'S AS IDENTITY*/ 
      super.loadMatrix("joints["+String.valueOf(i)+"]",mat);
     }
  }

  public void bindAttributes()
  {
   super.bindAttribute(0,"vertex");
   super.bindAttribute(1,"normal");
   super.bindAttribute(2,"texCoord");
   super.bindAttribute(3,"jointIndices");
   super.bindAttribute(4,"weights");   
  }

  public void start()
  {
   super.start();
   GL13.glActiveTexture(GL13.GL_TEXTURE0);
   GL11.glBindTexture(GL11.GL_TEXTURE_2D,tex.getTextureID());
  }

  public void release()
  {
   tex.release();
   super.release();
  }
}

Next we have an geometry rendererwhich load's the model from an Binary file and render's the stuff to the screen.

public class MeshRender
{
 private final int vao;
 private final ArrayList<Integer> vbos=new ArrayList<Integer>();
 private final ArrayList<Integer> indexLocations=new ArrayList<Integer>();
 private final int vertexCount;

 public MeshRender(int vertices)
 {
  vao=GL30.glGenVertexArrays();
  GL30.glBindVertexArray(vao);
  vertexCount=vertices;
 }

 public void createAttribute(int index,int vectype,FloatBuffer buffer)
 {
  int vbo=GL15.glGenBuffers();
  GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,vbo);
  GL15.glBufferData(GL15.GL_ARRAY_BUFFER,buffer,GL15.GL_DYNAMIC_DRAW);
  GL20.glVertexAttribPointer(index,vectype,GL11.GL_FLOAT,false,0,0);
  GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,0);

  indexLocations.add(index);
  vbos.add(vbo);
 }

 public void createAttribute(int index,int vectype,IntBuffer buffer)
 {
  int vbo=GL15.glGenBuffers();
  GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,vbo);
  GL15.glBufferData(GL15.GL_ARRAY_BUFFER,buffer,GL15.GL_DYNAMIC_DRAW);
  GL20.glVertexAttribPointer(index,vectype,GL11.GL_INT,false,0,0);
  GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,0);

  indexLocations.add(index);
  vbos.add(vbo);   
 }

 public void unBind(){GL30.glBindVertexArray(0);}

 public void render()
 {
  GL30.glBindVertexArray(vao);
  for(int index: indexLocations){GL20.glEnableVertexAttribArray(index);}
  GL11.glDrawArrays(GL11.GL_TRIANGLES,0,vertexCount);
  for(int index: indexLocations){GL20.glDisableVertexAttribArray(index);}
  GL30.glBindVertexArray(0);
 }

 public void release()
 {
  for(int vbo: vbos){GL15.glDeleteBuffers(vbo);}
  GL30.glDeleteVertexArrays(vao);
 } 

 public static MeshRender createMesh()
 {
  MeshRender mesh=null;
  try
  {
   Model model=Model.readFromFile("/main/Model.data");
   FloatBuffer fBuffer;
   IntBuffer iBuffer;

   fBuffer=model.toFBuffer(0);

   mesh=new MeshRender(fBuffer.capacity()/3);
   mesh.createAttribute(0,3,fBuffer);/*VERTICES'      INDEX=0  FLOAT'S=3*/

   fBuffer=model.toFBuffer(1);
   mesh.createAttribute(1,3,fBuffer);/*NORMAL'S       INDEX=1  FLOAT'S=3*/

   fBuffer=model.toFBuffer(2);
   mesh.createAttribute(2,2,fBuffer);/*TEX COORD'S    INDEX=2  FLOAT'S=2*/

   iBuffer=model.toIBuffer(3);
   mesh.createAttribute(3,4,iBuffer);/*JOINT INDICES  INDEX=3  INT'S=4*/

   fBuffer=model.toFBuffer(4);
   mesh.createAttribute(4,4,fBuffer);/*WEIGHT'S       INDEX=4  FLOAT'S=4*/

   mesh.unBind();        
  }
  catch(Exception ex){ex.printStackTrace();}
  return mesh;
 }
}

Class Model is the actual data loaded from the file which work's fine.But u can load any information here of your wish.

And finaly my shader and geometry used together in my main class

public class Main
{
 /*DISPLAY ATTRIBUTES*/   
private static ContextAttribs createDisplayAttributes()
 {
   ContextAttribs attribs=new ContextAttribs(4,2)
   .withForwardCompatible(true)
   .withProfileCore(true);

   return attribs;
 }

 private static void initDisplay(String title)
 {
  try
  {
   Display.create(new PixelFormat(),createDisplayAttributes());

   Display.setTitle(title);

   Display.setDisplayMode(new DisplayMode(1500,705));

   Display.setLocation(0,-2);

   Display.setResizable(false);
  }
  catch(Exception e)
  {
   e.printStackTrace();
  }    
 }
 /*DISPLAY ATTRIBUTES*/

 private static void startFrame()
 {
  GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
  GL11.glEnable(GL11.GL_DEPTH_TEST);
  GL11.glClearColor(0,0,1,0);
 }

 public static void main(String args[])
 {
  initDisplay("ANIMATION TEST");

  MeshRender mesh=MeshRender.createMesh();
  MyShader myShader=new MyShader();

  while(!Display.isCloseRequested())
  {
   startFrame();

   myShader.start();
   mesh.render();
   myShader.stop();

   updateDisplay();
  }

  mesh.release();
  myShader.release();

  mesh.release();
  releaseOpenGL();
 }

 private static void updateDisplay()
 {
  Display.update();
  Display.sync(60);
 }

 private static void releaseOpenGL()
 {
   try
   {    
    Mouse.destroy();
    Keyboard.destroy();
    Display.releaseContext();
    Display.destroy();
   }
   catch(Exception ex){ex.printStackTrace();}
 }

my suspension is loading an matrix to an array of uniforms but Im not sure .Thank u


Solution

  • Well I figured out that the problem was not with my shader or my model but with my MeshRenderer when linking data.

    If you have to link Integer data to your shader[linking jointId's] you have to use

    GL30.glVertexAttribIPointer(index,vecType,GL11.GL_INT,0,0)
                          And not
    GL20.glVertexAttribPointer(index,vectype,GL11.GL_INT,false,0,0);
    

    Don't know why they made an separate method for linking integer's but that makes my texture work perfectly. Case closed