I'm following a video series about Open GL on YouTube using LWJGL, so far I've managed to render 3d models and texture them properly using the obj format. I want to use voxel-based models for my game so I went in the software MagicaVoxel, exported a textured example with the obj format, but the texture is not mapped correctly. Indeed, some colors seem to be correctly mapped, but other faces have the entire texture on them.
Here is a picture of the expected result:
and the actual result:
I think the problem comes with the texture and the way opengl interpolate it, the texture is a 1*256 line with colors, and on the obj file only the desired colors are put on the uv coordinates.
I made a simpler example to help understand what is going on: just 3 cubes aligned with each other, and a 3-pixel long texture with 3 different colors, here is the code of the obj file and the texture is too small to be seen but it's really just 3 colored pixels.
# normals
vn -1 0 0
vn 1 0 0
vn 0 0 1
vn 0 0 -1
vn 0 -1 0
vn 0 1 0
# texcoords
vt 0.25 0.5
vt 0.5 0.5
vt 0.75 0.5
# verts
v -0.1 0 0
v -0.1 0 -0.1
v -0.1 0.1 0
v -0.1 0.1 -0.1
v 0.2 0 0
v 0.2 0 -0.1
v 0.2 0.1 0
v 0.2 0.1 -0.1
v -0.1 0 0
v -0.1 0.1 0
v 0 0 0
v 0 0.1 0
v 0.1 0 0
v 0.1 0.1 0
v 0.2 0 0
v 0.2 0.1 0
v -0.1 0 -0.1
v -0.1 0.1 -0.1
v 0 0 -0.1
v 0 0.1 -0.1
v 0.1 0 -0.1
v 0.1 0.1 -0.1
v 0.2 0 -0.1
v 0.2 0.1 -0.1
v -0.1 0 0
v 0 0 0
v 0.1 0 0
v 0.2 0 0
v -0.1 0 -0.1
v 0 0 -0.1
v 0.1 0 -0.1
v 0.2 0 -0.1
v -0.1 0.1 0
v 0 0.1 0
v 0.1 0.1 0
v 0.2 0.1 0
v -0.1 0.1 -0.1
v 0 0.1 -0.1
v 0.1 0.1 -0.1
v 0.2 0.1 -0.1
# faces
f 3/2/1 2/2/1 1/2/1
f 4/2/1 2/2/1 3/2/1
f 5/1/2 6/1/2 7/1/2
f 7/1/2 6/1/2 8/1/2
f 11/2/3 10/2/3 9/2/3
f 12/2/3 10/2/3 11/2/3
f 13/3/3 12/3/3 11/3/3
f 14/3/3 12/3/3 13/3/3
f 15/1/3 14/1/3 13/1/3
f 16/1/3 14/1/3 15/1/3
f 17/2/4 18/2/4 19/2/4
f 19/2/4 18/2/4 20/2/4
f 19/3/4 20/3/4 21/3/4
f 21/3/4 20/3/4 22/3/4
f 21/1/4 22/1/4 23/1/4
f 23/1/4 22/1/4 24/1/4
f 29/2/5 26/2/5 25/2/5
f 30/3/5 27/3/5 26/3/5
f 30/2/5 26/2/5 29/2/5
f 31/1/5 28/1/5 27/1/5
f 31/3/5 27/3/5 30/3/5
f 32/1/5 28/1/5 31/1/5
f 33/2/6 34/2/6 37/2/6
f 34/3/6 35/3/6 38/3/6
f 37/2/6 34/2/6 38/2/6
f 35/1/6 36/1/6 39/1/6
f 38/3/6 35/3/6 39/3/6
f 39/1/6 36/1/6 40/1/6
As you can see for each face, the 3 UV coordinates picked up for the 3 vertices are the same, but in OpenGL, this is the result (they are supposed to be red, blue and yellow):
Here is my OBJ file reader code (in Java), that I call to create the vao and stuff to render:
public static RawModel loadObjModel(String fileName, Loader loader) {
FileReader fr = null;
try {
fr = new FileReader(new File("res/" + fileName + ".obj"));
} catch (FileNotFoundException e) {
System.err.println("Couldn't load file!");
e.printStackTrace();
}
BufferedReader reader = new BufferedReader(fr);
String line;
List<Vector3f> vertices = new ArrayList<Vector3f>();
List<Vector2f> textures = new ArrayList<Vector2f>();
List<Vector3f> normals = new ArrayList<Vector3f>();
List<Integer> indices = new ArrayList<Integer>();
float[] verticesArray = null;
float[] normalsArray = null;
float[] textureArray = null;
int[] indicesArray = null;
try {
while (true) {
line = reader.readLine();
String[] currentLine = line.split(" ");
if (line.startsWith("v ")) {
Vector3f vertex = new Vector3f(Float.parseFloat(currentLine[1]),
Float.parseFloat(currentLine[2]), Float.parseFloat(currentLine[3]));
vertices.add(vertex);
} else if (line.startsWith("vt ")) {
Vector2f texture = new Vector2f(Float.parseFloat(currentLine[1]),
Float.parseFloat(currentLine[2]));
textures.add(texture);
} else if (line.startsWith("vn ")) {
Vector3f normal = new Vector3f(Float.parseFloat(currentLine[1]),
Float.parseFloat(currentLine[2]), Float.parseFloat(currentLine[3]));
normals.add(normal);
} else if (line.startsWith("f ")) {
textureArray = new float[vertices.size() * 2];
normalsArray = new float[vertices.size() * 3];
break;
}
}
while (line != null) {
if (!line.startsWith("f ")) {
line = reader.readLine();
continue;
}
String[] currentLine = line.split(" ");
String[] vertex1 = currentLine[1].split("/");
String[] vertex2 = currentLine[2].split("/");
String[] vertex3 = currentLine[3].split("/");
processVertex(vertex1,indices,textures,normals,textureArray,normalsArray);
processVertex(vertex2,indices,textures,normals,textureArray,normalsArray);
processVertex(vertex3,indices,textures,normals,textureArray,normalsArray);
line = reader.readLine();
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
verticesArray = new float[vertices.size()*3];
indicesArray = new int[indices.size()];
int vertexPointer = 0;
for(Vector3f vertex:vertices){
verticesArray[vertexPointer++] = vertex.x;
verticesArray[vertexPointer++] = vertex.y;
verticesArray[vertexPointer++] = vertex.z;
}
for(int i=0;i<indices.size();i++){
indicesArray[i] = indices.get(i);
}
return loader.loadToVAO(verticesArray, indicesArray, textureArray);
}
private static void processVertex(String[] vertexData, List<Integer> indices,
List<Vector2f> textures, List<Vector3f> normals, float[] textureArray,
float[] normalsArray) {
int currentVertexPointer = Integer.parseInt(vertexData[0]) - 1;
indices.add(currentVertexPointer);
Vector2f currentTex = textures.get(Integer.parseInt(vertexData[1])-1);
textureArray[currentVertexPointer*2] = currentTex.x;
textureArray[currentVertexPointer*2+1] = 1 - currentTex.y;
Vector3f currentNorm = normals.get(Integer.parseInt(vertexData[2])-1);
normalsArray[currentVertexPointer*3] = currentNorm.x;
normalsArray[currentVertexPointer*3+1] = currentNorm.y;
normalsArray[currentVertexPointer*3+2] = currentNorm.z;
}
Here is my fragment shader:
#version 400 core
in vec2 pass_textureCoords;
out vec4 out_colour;
uniform sampler2D textureSampler;
void main(void){
out_colour = texture(textureSampler,pass_textureCoords);
}
Here is my vertex shader:
#version 400 core
in vec3 position;
in vec2 textureCoords;
out vec2 pass_textureCoords;
uniform mat4 transformationMatrix;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
void main(void){
gl_Position = projectionMatrix * viewMatrix * transformationMatrix * vec4(position.xyz,1.0);
pass_textureCoords = textureCoords;
}
And here is my render method called every frame:
public void render(Entity entity,StaticShader shader) {
TexturedModel texturedModel = entity.getTexturedModel();
RawModel model = texturedModel.getRawModel();
GL30.glBindVertexArray(model.getVaoID());
GL20.glEnableVertexAttribArray(0);
GL20.glEnableVertexAttribArray(1);
Matrix4f transformationMatrix = Maths.createTransformationMatrix(entity.getPosition(), entity.getRotation(), entity.getScale());
shader.loadTransformationMatrix(transformationMatrix);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, texturedModel.getTexture().getID());
GL11.glDrawElements(GL11.GL_TRIANGLES, model.getVertexCount(),GL11.GL_UNSIGNED_INT,0);
GL20.glDisableVertexAttribArray(0);
GL20.glDisableVertexAttribArray(1);
GL30.glBindVertexArray(0);
}
I don't understand what I'm missing, and again it already worked for a blender model with a large texture: