Related
I have been learning OpenGL recently and I have got bit confused about shaders and the VBOs.
Basically, I am confused on how my vertex shader knows about the colour and position data in my VBOs.
To my knowledge glBindAttribLocation(program, attribute, variable); is what sets the attribute in the shader but when I comment out that code, the triangle still renders fine with it's colours.
Here is my code:
public static void main(String[] args) {
String vertexSource = "#version 400 core\n"
+ "in vec3 position;\n"
+ "in vec3 colour;\n"
+ "out vec3 vertexColour;\n"
+ "uniform mat4 model;\n"
+ "uniform mat4 view;\n"
+ "uniform mat4 projection;\n"
+ "void main() {\n"
+ "vertexColour = colour;\n"
+ "mat4 mvp = projection * view * model;\n"
+ "gl_Position = vec4(position, 1.0);\n"
+ "}\n";
String fragmentSource = "#version 400 core\n"
+ "in vec3 vertexColour;\n"
+ "out vec4 colours;\n"
+ "void main()\n"
+ "{\n"
+ " colours = vec4(vertexColour, 1);\n"
+ "}";
glfwSetErrorCallback(errorCallBack);
if (!glfwInit()) {
throw new IllegalStateException("Unable to initialize GLFW");
}
glfwDefaultWindowHints();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
glfwWindowHint(GLFW_VISIBLE, GL_TRUE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
long window = glfwCreateWindow(640, 480, "my window!", 0, 0);
glfwSetKeyCallback(window, keyCallback);
if (window == 0) {
glfwTerminate();
throw new RuntimeException("Failed to create the GLFW window");
}
glfwMakeContextCurrent(window);
GL.createCapabilities();
float[] vertexPoints = {
0f,0.5f,0f,
-0.5f,-0.5f,0f,
0.5f,-0.5f,0f
};
float[] colours = {
0.1f,0.5f,0.5f,
0.3f,0.7f,0.9f,
0.4f,0.9f,0.1f
};
ArrayList<Integer> vaos = new ArrayList<Integer>();
ArrayList<Integer> vbos = new ArrayList<Integer>();
int vaoID = glGenVertexArrays();
vaos.add(vaoID);
glBindVertexArray(vaoID);
FloatBuffer vertices = BufferUtils.createFloatBuffer(vertexPoints.length);
vertices.put(vertexPoints);
vertices.flip();
int vboID = GL33.glGenBuffers();
vbos.add(vboID);
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
//add it to the vao at position 0, size 3, with data types of float, normalised = false,
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
//unbind currently bound vbo
glBindBuffer(GL_ARRAY_BUFFER, 0);
//and one for the colour data
vboID = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, colours, GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//finished with vao now unbind it
glBindVertexArray(0);
//vao and vbo stuff is now finished with
//now for the shader stuff
int vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShaderID, vertexSource);
glCompileShader(vertexShaderID);
if (!checkForSuccess(vertexShaderID)) {
glfwTerminate();
throw new IllegalStateException("vertex shader failed: " + glGetShaderInfoLog(vertexShaderID));
}
int fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShaderID, fragmentSource);
glCompileShader(fragmentShaderID);
if (!checkForSuccess(vertexShaderID)) {
glGetShaderInfoLog(vertexShaderID);
glfwTerminate();
throw new IllegalStateException("fragment shader failed");
}
//shader program
int shaderProgramID = glCreateProgram();
glAttachShader(shaderProgramID, vertexShaderID);
glAttachShader(shaderProgramID, fragmentShaderID);
glLinkProgram(shaderProgramID);
//error test
int status = glGetProgrami(shaderProgramID, GL_LINK_STATUS);
if (status != GL_TRUE) {
glfwTerminate();
throw new RuntimeException(glGetProgramInfoLog(shaderProgramID));
}
glUseProgram(shaderProgramID);
//setting position attribute in the shader:
int positionAttribute = glGetAttribLocation(shaderProgramID, "position");
glEnableVertexAttribArray(positionAttribute);
glBindAttribLocation(shaderProgramID, positionAttribute, "position");
//setting colour attribute in the shader:
int colourAttribute = glGetAttribLocation(shaderProgramID, "colour");
glEnableVertexAttribArray(colourAttribute);
glBindAttribLocation(shaderProgramID, colourAttribute, "colour");
glClearColor(0.5f,0.5f,0.5f,1f);
//this will create a wire frame view
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glfwShowWindow(window); //optional
while (!glfwWindowShouldClose(window)) {
double time = glfwGetTime();
glfwPollEvents();
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(vaoID);
//enables the position attribute in the vertex shader
glEnableVertexAttribArray(0);
//enables the colour attribute in the vertex shader
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLES, 0, vertexPoints.length);
//disables the position and colour attribute in the vertex shader
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glfwSwapBuffers(window);
}
//clear vaos and vbos
for (int vao : vaos) {
glDeleteVertexArrays(vao);
}
for (int vbo : vbos) {
glDeleteBuffers(vbo);
}
//delete shaders
glDetachShader(shaderProgramID, vertexShaderID);
glDetachShader(shaderProgramID, fragmentShaderID);
glDeleteShader(vertexShaderID);
glDeleteShader(fragmentShaderID);
glDeleteProgram(shaderProgramID);
Callbacks.glfwFreeCallbacks(window);
glfwDestroyWindow(window);
glfwTerminate();
}
I have provided my full code file on github (250 lines) here: https://github.com/OneEgg42/opengl
Would someone be so kind and explain to me how this works?
Thanks in advance!
The relation between a VBO and a shader is not established by glBindAttribLocation.
The relevant lines are actually
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
where you tell OpenGL that the attribute with location 0 (the first parameter in the first glVertexAttribPointer call) should read the first three floats from vboID.
glBindAttribLocation is responsible for binding a certain attribute to a specific location. Since this has to be done before linking the shader, they aren't doing anything in your code. Even more so, because this code tells the attribute to use the location that it already has:
int positionAttribute = glGetAttribLocation(shaderProgramID, "position");
glEnableVertexAttribArray(positionAttribute);
glBindAttribLocation(shaderProgramID, positionAttribute, "position");
You basically query the location, and then assign the location again to the attribute.
Two side notes:
At the moment your code works by pure luck. You assume in the glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); line that the "position" attribute is at location 0, which it probably is as long as it is the first attribute written in the shader. Same for colors. It is very likely that your code breaks when you exchange the order of the two attribute definitions in your shader.
Either query the attribute location with glGetAttribLocation and use the result instead of 0 and 1, or use glBindAttribLocation before linking to make sure that the attributes are at the location you expect them.
The calls to glEnableVertexAttribArray should be in the VAO setup code and not in the render loop.
Setup:
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
Render:
glBindVertexArray(vaoID);
glDrawArrays(GL_TRIANGLES, 0, vertexPoints.length);
This is the part of the source code where the problem resides:
GL.createCapabilities();
// Define the viewport dimensions
glViewport(0, 0, 300, 300);
int shaderProgram;
final String vertexShader = "#version 330 core\n in vec3 position; // The position variable has attribute position 0\n out vec4 vertexColor; // Specify a color output to the fragment shader\n void main()\n {\n gl_Position = vec4(position, 1.0); // See how we directly give a vec3 to vec4's constructor\n vertexColor = vec4(0.5f, 0.0f, 0.0f, 1.0f); // Set the output variable to a dark-red color\n }";
String fragmentShader = "#version 330 core\n in vec3 ourColor;\n"
+ "in vec2 TexCoord;\n"
+ "out vec4 color;\n"
+ "uniform sampler2D ourTexture1;\n"
+ "void main()\n"
+ "{\n"
+ "color = vec4(ourColor, 1.0f);\n"
+ "}";
int vertex, fragment;
// Vertex Shader
vertex = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
GL20.glShaderSource(vertex, vertexShader);
GL20.glCompileShader(vertex);
// Fragment Shader
fragment = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
GL20.glShaderSource(fragment, fragmentShader);
GL20.glCompileShader(fragment);
//create program and bind shaders to program
shaderProgram = GL20.glCreateProgram();
GL20.glAttachShader(shaderProgram, vertex);
GL20.glAttachShader(shaderProgram, fragment);
GL20.glLinkProgram(shaderProgram);
int vlength = GL20.GL_SHADER_SOURCE_LENGTH;
int iscompiled = GL20.glGetProgrami(fragment, GL20.GL_COMPILE_STATUS);
if(iscompiled == GL_FALSE)
{
System.out.println(glGetString(GL_VERSION));
System.out.println("not compiled");
System.out.println(GL11.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION));
System.out.println(GL20.glGetShaderInfoLog(vertex));
return;
}
int isLinked = GL20.glGetProgrami(shaderProgram, GL20.GL_LINK_STATUS);
if(isLinked == GL_FALSE)
{
System.out.println("failed linking");
return;
}
This is the vertex shader:
#version 330 core
layout (location = 0) in vec3 position; // The position variable has attribute position 0
out vec4 vertexColor; // Specify a color output to the fragment shader
void main()
{
gl_Position = vec4(position, 1.0); // See how we directly give a vec3 to vec4's constructor
vertexColor = vec4(0.5f, 0.0f, 0.0f, 1.0f); // Set the output variable to a dark-red color
}
This is the fragment shader:
#version 330 core
in vec4 vertexColor; // The input variable from the vertex shader (same name and same type)
out vec4 color;
void main()
{
color = vertexColor;
}
Both of the shaders don't compile. Niether the vertex or fragment shader. What needs to be fixed?
I am using the core profile of opengl version 3.3.
The operating system being run is windows vista home premium 64 bit.
int iscompiled = GL20.glGetProgrami(fragment, GL20.GL_COMPILE_STATUS);
You cannot query a program for compilation status. That is a property of each shader object seperately, and can be queried via glGetShaderiv(). Using GL_COMPILE_STATUS for glGetProgramiv() will just result in an GL_INVALID_ENUM error.
I am rendering a mesh using GLSL shaders and a VBO, and the VBO stores 4 attributes; positionXYZ, normalXYZ, textureUV, colourRGBA. Everything works, except for the UVs (possibly the normals too, but I haven't got a way to test them yet).
What is happening is, the texture UV positions in the array are offset to the normal x and y position in the array. The array is structured as VVVNNNTTCCCC (vertex position, normal, texture, colour) by the way. I am pretty sure that the problem is somewhere in sending the VBO to the shaders. I know for certain that the data in the VBO is in the correct order.
This is my rendering code:
The VBO class
public final class Mesh
{
public static final int FLOAT_SIZE_BYTES = 4;
public static final int FLOATS_PER_POSITION = 3;
public static final int FLOATS_PER_NORMAL = 3;
public static final int FLOATS_PER_TEXTURE = 2;
public static final int FLOATS_PER_COLOUR = 4;
public static final int VERTEX_SIZE_FLOATS = FLOATS_PER_POSITION + FLOATS_PER_NORMAL + FLOATS_PER_TEXTURE + FLOATS_PER_COLOUR;
public static final int VERTEX_SIZE_BYTES = VERTEX_SIZE_FLOATS * FLOAT_SIZE_BYTES;
public static final int POSITION_OFFSET_FLOATS = 0;
public static final int NORMAL_OFFSET_FLOATS = POSITION_OFFSET_FLOATS + FLOATS_PER_POSITION;
public static final int TEXTURE_OFFSET_FLOATS = NORMAL_OFFSET_FLOATS + FLOATS_PER_NORMAL;
public static final int COLOUR_OFFSET_FLOATS = TEXTURE_OFFSET_FLOATS + FLOATS_PER_TEXTURE;
public static final int POSITION_OFFSET_BYTES = POSITION_OFFSET_FLOATS * FLOAT_SIZE_BYTES;
public static final int NORMAL_OFFSET_BYTES = NORMAL_OFFSET_FLOATS * FLOAT_SIZE_BYTES;
public static final int TEXTURE_OFFSET_BYTES = TEXTURE_OFFSET_FLOATS * FLOAT_SIZE_BYTES;
public static final int COLOUR_OFFSET_BYTES = COLOUR_OFFSET_FLOATS * FLOAT_SIZE_BYTES;
public static final int POSITION_STRIDE_BYTES = VERTEX_SIZE_BYTES;
public static final int NORMAL_STRIDE_BYTES = VERTEX_SIZE_BYTES;
public static final int TEXTURE_STRIDE_BYTES = VERTEX_SIZE_BYTES;
public static final int COLOUR_STRIDE_BYTES = VERTEX_SIZE_BYTES;
public final static int VERTICES_PER_FACE = 3;
public static final int ATTRIBUTE_LOCATION_POSITION = 0;
public static final int ATTRIBUTE_LOCATION_NORMAL = 1;
public static final int ATTRIBUTE_LOCATION_TEXTURE = 2;
public static final int ATTRIBUTE_LOCATION_COLOUR = 3;
private int vaoID;
private int iboID;
private int indexCount;
private Mesh(int vaoID, int iboID, int indexCount)
{
this.vaoID = vaoID;
this.iboID = iboID;
this.indexCount = indexCount;
}
public void draw(AbstractShaderProgram shader, Texture texture)
{
glEnable(GL_TEXTURE_2D);
if (texture != null) texture.bind(shader);
else Texture.MISSING_TEXTURE.bind(shader);
glBindVertexArray(vaoID);
glEnableVertexAttribArray(ATTRIBUTE_LOCATION_POSITION);
// glEnableVertexAttribArray(ATTRIBUTE_LOCATION_NORMAL);
// glEnableVertexAttribArray(ATTRIBUTE_LOCATION_TEXTURE);
// glEnableVertexAttribArray(ATTRIBUTE_LOCATION_COLOUR);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID);
glDrawElements(GL_TRIANGLES, indexCount, GL_FLOAT, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(ATTRIBUTE_LOCATION_POSITION);
// glDisableVertexAttribArray(ATTRIBUTE_LOCATION_NORMAL);
// glDisableVertexAttribArray(ATTRIBUTE_LOCATION_TEXTURE);
// glDisableVertexAttribArray(ATTRIBUTE_LOCATION_COLOUR);
glBindVertexArray(0);
glDisable(GL_TEXTURE_2D);
}
public static Mesh compile(List<Face> faces)
{
if (faces.size() <= 0)
throw new RuntimeException("Failed to compile mesh. No faces were provided.");
HashMap<Vertex, Integer> indexMap = new HashMap<>();
ArrayList<Vertex> vertices = new ArrayList<>();
int vertexCount = 0;
for (Face face : faces)
{
for (Vertex vertex : face.getVertices())
{
if (!indexMap.containsKey(vertex))
{
indexMap.put(vertex, vertexCount++);
vertices.add(vertex);
}
}
}
int indicesCount = faces.size() * VERTICES_PER_FACE;
int dataSize = vertexCount * VERTEX_SIZE_FLOATS;
FloatBuffer vertexData = BufferUtils.createFloatBuffer(dataSize);
if (vertexData == null)
System.err.println("Failed to allocate FloatBuffer with size " + dataSize);
for (Vertex vertex : vertices)
{
vertexData.put(vertex.getPosition().x);
vertexData.put(vertex.getPosition().y);
vertexData.put(vertex.getPosition().z);
// vertexData.put(vertex.getNormal() == null ? 1.0F : vertex.getNormal().x);
// vertexData.put(vertex.getNormal() == null ? 1.0F : vertex.getNormal().y);
// vertexData.put(vertex.getNormal() == null ? 1.0F : vertex.getNormal().z);
// vertexData.put(vertex.getTexture() == null ? 0.0F : vertex.getTexture().x);
// vertexData.put(vertex.getTexture() == null ? 0.0F : vertex.getTexture().y);
// vertexData.put(vertex.getColour() == null ? 1.0F : vertex.getColour().getRGBA().x);
// vertexData.put(vertex.getColour() == null ? 1.0F : vertex.getColour().getRGBA().y);
// vertexData.put(vertex.getColour() == null ? 1.0F : vertex.getColour().getRGBA().z);
// vertexData.put(vertex.getColour() == null ? 1.0F : vertex.getColour().getRGBA().w);
}
vertexData.flip();
IntBuffer indices = BufferUtils.createIntBuffer(indicesCount);
for (Face face : faces)
{
for (Vertex vertex : face.getVertices())
{
int index = indexMap.get(vertex);
indices.put(index);
}
}
indices.flip();
int vaoID = glGenVertexArrays();
glBindVertexArray(vaoID);
int vboID = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);
glVertexAttribPointer(ATTRIBUTE_LOCATION_POSITION, FLOATS_PER_POSITION, GL_FLOAT, false, 0, 0);
// glVertexAttribPointer(ATTRIBUTE_LOCATION_NORMAL, FLOATS_PER_NORMAL, GL_FLOAT, false, NORMAL_STRIDE_BYTES, NORMAL_OFFSET_BYTES);
// glVertexAttribPointer(ATTRIBUTE_LOCATION_TEXTURE, FLOATS_PER_TEXTURE, GL_FLOAT, false, TEXTURE_STRIDE_BYTES, TEXTURE_OFFSET_BYTES);
// glVertexAttribPointer(ATTRIBUTE_LOCATION_COLOUR, FLOATS_PER_COLOUR, GL_FLOAT, false, COLOUR_STRIDE_BYTES, COLOUR_OFFSET_BYTES);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
int iboID = glGenBuffers();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
return new Mesh(vaoID, iboID, indicesCount);
}
}
The vertex shader:
#version 400 core
in vec3 vertPosition;
in vec3 vertNormal;
in vec2 vertTexture;
in vec4 vertColour;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
out vec3 pVertPosition;
out vec3 pVertNormal;
out vec2 pVertTexture;
out vec4 pVertColour;
void main()
{
pVertPosition = vertPosition;
pVertNormal = vertNormal;
pVertTexture = vertTexture;
pVertColour = vertColour;
gl_Position = vec4(vec3(vertPosition.xy + vertTexture, vertPosition.z), 1.0);
}
The fragment shader:
#version 400 core
in vec3 ppVertPosition;
in vec3 ppVertNormal;
in vec2 ppVertTexture;
in vec4 ppVertColour;
uniform sampler2D texture;
void main()
{
gl_FragColor = texture2D(texture, ppVertTexture);
}
There is a geometry shader in between these, but it is currently redundant and just pases the information straight to the fragment shader (that is why the out and in variables don't match.) Also, the reason the textureUV is added to the vertex position in the fragment shader was to debug what the texture UV values being passed actually were. This is how I know the UVs are offset to the normal xy. If I put the texture UVs into the normal xy, they work perfectly fine.
If there is any extra code you would like to see, that I haven't included, I'll add it. I haven't added everything, for example, the whole VBO class, because it is too much code. I have only included what I think is relevant and where I think the problem is.
Edit #1:
The variable locations in the shader, such as vertPosition and vertNormal are bound. This is my code to bind them
glBindAttribLocation(program, Mesh.ATTRIBUTE_LOCATION_POSITION, "vertPosition");
glBindAttribLocation(program, Mesh.ATTRIBUTE_LOCATION_NORMAL, "vertNormal");
glBindAttribLocation(program, Mesh.ATTRIBUTE_LOCATION_TEXTURE, "vertTexture");
glBindAttribLocation(program, Mesh.ATTRIBUTE_LOCATION_COLOUR, "vertColour");
Changing the vertex shader to use layouts, like so, yields the ex\ct same result;
layout(location = 0) in vec3 vertPosition;
layout(location = 1) in vec3 vertNormal;
layout(location = 2) in vec2 vertTexture;
layout(location = 3) in vec4 vertColour;
Edit #2
I decided to post the entire Mesh class, rather than just parts of it. I have also tried to implement VAOs instead of VBOs, but it isn't working.
You are mixing standard pipeline functionality with custom shader variables.
Calling glEnableClientState(GL_VERTEX_ARRAY); tells OpenGL to use a certain data bind point, all good.
Calling glVertexPointer( tells OpenGL where to find its vertices. Since you enabled the correct array previously, all is still good.
Then we get to the vertex shader and you use in vec3 vertPosition; but GLSL doesn't know that you want your vertex data there. Sure, we can see the name is "vertPosition", but GLSL shouldn't have to guess the data you want based on the variable name of course! Instead, the default pipeline behavior for GLSL is to use gl_Vertex, a pre built GLSL variable tied to GL_VERTEX_ARRAY.
So why does it work for positions? Sheer luck with the variables you define being allocated the pre-built constants by chance, I guess.
What you should do is switch glEnableClientState to glEnableVertexAttribArray, use Layouts to assign each variable a number, and call glVertexAttribPointer instead of glVertexPointer to link that number to the correct data.
Now, the variables you declared, like vertPosition, point to the right data in your buffers, not by chance, but because you told them to!
This is the correct way to do things in modern OpenGL, using the pre built variables like gl_Vertex and functions like glEnableClientState is considered old and bad because its inflexible.
You can also omit the layouts (because it requires OGL 4+, which not everyone has), but that requires some more work before linking the shaders.
Good luck!
More info on converting your code
(I hope im right with this, I cant comment to actually verify that this is the issue)
Okay, I got it working... finally. I have no idea what the initial problem was with the VBO, but once I switched to use a VAO, and not render with glClientState, it worked fine. Also, the problem I was having with the VAO not rendering anything what-so-ever was the line:
glDrawElements(GL_TRIANGLES, indexCount, GL_FLOAT, 0);
should have been
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0);
I am writing a modern openGL model loader, and am running into some issues using shader attribute locations other than 0. I can get the position attribute into the shader, but not the values needed for Blinn-Phong shading (bound in the program to locations: Ks-3, Ka-4, Kd-5). Here is the code I am using to load and render the OBJ models:
public class Model{
private List<Vector3f> vertices=new ArrayList<>();
private List<Vector3f> normals=new ArrayList<>();
private ArrayList<Integer> vaos=new ArrayList<>();
private float[] faces=new float[fvaoSize];
private float[] ks=new float[kvaoSize];
private float[] ka=new float[kvaoSize];
private float[] kd=new float[kvaoSize];
private int currentPlace=0,kcurrentPlace=0;
private static final int fvaoSize=glGetInteger(GL12.GL_MAX_ELEMENTS_VERTICES)-(glGetInteger(GL12.GL_MAX_ELEMENTS_VERTICES)%12);
private static final int kvaoSize=(fvaoSize/4)*3;
private int facesd=0;
private HashMap<String,Material> materials=new HashMap<>();
#SuppressWarnings("unused")
private int plainFaceVAO=0,texFaceVAO=0,normalsFaceVAO=0,normalsTexVAO=0;
#SuppressWarnings("unused")
private int faceOffset=0,
texOffset=0,texTexture=0,texCoords=0,
normalOffset=0,normalCoords=0,
normalTexOffset=0,normalTexNormalCoords,normalTexTexCoords,normalTexTexture=0;
#SuppressWarnings("unused")
private Shader faceShader=null,texShader=null,normalsShader=null,normalTexShader=null;
private Material currMtl=null;
public Model(File f,Shader faceShader,Shader texShader,Shader normalsShader,Shader normalTexShader){
loadModelData(f);
this.faceShader=faceShader;
this.texShader=texShader;
this.normalsShader=normalsShader;
this.normalTexShader=normalTexShader;
faceOffset=glGetUniformLocation(faceShader.getID(),"trans");
texOffset=glGetUniformLocation(texShader.getID(),"trans");
texTexture=glGetAttribLocation(texShader.getID(),"tex");
texCoords=glGetAttribLocation(texShader.getID(),"texCoords");
normalOffset=glGetUniformLocation(normalsShader.getID(),"trans");
normalCoords=glGetAttribLocation(normalsShader.getID(),"normalsCoords");
}
#SuppressWarnings("null")
private void loadModelData(File f){
try(BufferedReader br=new BufferedReader(new FileReader(f))){
String line="";
while((line=br.readLine())!=null){
String[] words=line.split(" ");
//System.out.println(line);
if(line.startsWith("#"))
continue;
switch(words[0]){
case OBJ_VERTEX:
vertices.add(new Vector3f(parseFloat(words[1]),parseFloat(words[2]),-parseFloat(words[3])));
break;
case OBJ_VERTEX_NORMAL:
normals.add(new Vector3f(parseFloat(words[1]),parseFloat(words[2]),-parseFloat(words[3])));
break;
case OBJ_FACE:
facesd++;
FaceType cft=null;
int slashCount=0;
for(char c:words[1].toCharArray()){
if(c=='/')
slashCount++;
}
if(slashCount==0){
cft=FaceType.COORDSONLY;
}else if(slashCount==1){
cft=FaceType.TEXTURE;
}else if(slashCount==2){
if(words[0].contains("//")){
cft=FaceType.NORMALS;
}else{
cft=FaceType.NORMALS_AND_TEXTURE;
}
}
switch(cft){
case COORDSONLY:
Vector3f pos1=vertices.get(Integer.parseInt(words[1])-1);
Vector3f pos2=vertices.get(Integer.parseInt(words[2])-1);
Vector3f pos3=vertices.get(Integer.parseInt(words[3])-1);
Vec3 Ks=(currMtl.getKs()!=null?currMtl.getKs():new Vec3(1));
Vec3 Ka=(currMtl.getKa()!=null?currMtl.getKa():new Vec3(1));
Vec3 Kd=(currMtl.getKd()!=null?currMtl.getKd():new Vec3(1));
float[] temp=new float[]
{
pos1.x,pos1.y,pos1.z,1.0f,
pos2.x,pos2.y,pos2.z,1.0f,
pos3.x,pos3.y,pos3.z,1.0f
};
for(int i=0;i<12;i++){
faces[currentPlace+i]=temp[i];
}
float[] ktemp=new float[]
{
Ks.x,Ks.y,Ks.z,
Ks.x,Ks.y,Ks.z,
Ks.x,Ks.y,Ks.z
};
for(int i=0;i<9;i++){
ks[kcurrentPlace+i]=ktemp[i];
}
ktemp=new float[]
{
Ka.x,Ka.y,Ka.z,
Ka.x,Ka.y,Ka.z,
Ka.x,Ka.y,Ka.z
};
for(int i=0;i<9;i++){
ka[kcurrentPlace+i]=ktemp[i];
}
ktemp=new float[]
{
Kd.x,Kd.y,Kd.z,
Kd.x,Kd.y,Kd.z,
Kd.x,Kd.y,Kd.z
};
for(int i=0;i<9;i++){
kd[kcurrentPlace+i]=ktemp[i];
}
kcurrentPlace+=9;
currentPlace+=12;
if(currentPlace==fvaoSize){
int fvbo=glGenBuffers();
FloatBuffer vertexPositionsBuffer=BufferUtils.createFloatBuffer(fvaoSize);
vertexPositionsBuffer.put(faces);
vertexPositionsBuffer.flip();
glBindBuffer(GL_ARRAY_BUFFER, fvbo);
glBufferData(GL_ARRAY_BUFFER, vertexPositionsBuffer, GL_STATIC_DRAW);
int ksvbo=glGenBuffers();
FloatBuffer ksBuffer=BufferUtils.createFloatBuffer(kvaoSize);
ksBuffer.put(ks);
ksBuffer.flip();
glBindBuffer(GL_ARRAY_BUFFER, ksvbo);
glBufferData(GL_ARRAY_BUFFER, ksBuffer, GL_STATIC_DRAW);
int kavbo=glGenBuffers();
FloatBuffer kaBuffer=BufferUtils.createFloatBuffer(kvaoSize);
kaBuffer.put(ka);
kaBuffer.flip();
glBindBuffer(GL_ARRAY_BUFFER, kavbo);
glBufferData(GL_ARRAY_BUFFER, kaBuffer, GL_STATIC_DRAW);
int kdvbo=glGenBuffers();
FloatBuffer kdBuffer=BufferUtils.createFloatBuffer(kvaoSize);
kdBuffer.put(kd);
kdBuffer.flip();
glBindBuffer(GL_ARRAY_BUFFER, kdvbo);
glBufferData(GL_ARRAY_BUFFER, kdBuffer, GL_STATIC_DRAW);
int vao = glGenVertexArrays();
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, fvbo);
glVertexAttribPointer(0, 4, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, ksvbo);
glVertexAttribPointer(3, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, kavbo);
glVertexAttribPointer(4, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, kdvbo);
glVertexAttribPointer(5, 3, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glBindVertexArray(0);
glDeleteBuffers(fvbo);
glDeleteBuffers(ksvbo);
glDeleteBuffers(kavbo);
glDeleteBuffers(kdvbo);
ksBuffer=null;
kaBuffer=null;
kdBuffer=null;
vertexPositionsBuffer=null;
vaos.add(vao);
glBindBuffer(GL_ARRAY_BUFFER, 0);
currentPlace=0;
kcurrentPlace=0;
faces=new float[fvaoSize];
ks=new float[kvaoSize];
ka=new float[kvaoSize];
kd=new float[kvaoSize];
}
break;
case NORMALS:
throw new RuntimeException("File is unsupported.");
case NORMALS_AND_TEXTURE:
throw new RuntimeException("File is unsupported.");
case TEXTURE:
throw new RuntimeException("File is unsupported.");
default:
throw new RuntimeException("File is unsupported.");
}
break;
case OBJ_MTLLIB:
materials=MTLLibLoader.loadMTLLib(f.toPath().getParent()+"/"+words[1]);
break;
case OBJ_USEMTL:
System.out.println("Using Material "+words[1]+": Exists in hmap: "+materials.containsKey(words[1]));
currMtl=materials.get(words[1]);
break;
default:
break;
}
}
int fvbo=glGenBuffers();
FloatBuffer vertexPositionsBuffer=BufferUtils.createFloatBuffer(fvaoSize);
vertexPositionsBuffer.put(faces);
vertexPositionsBuffer.flip();
glBindBuffer(GL_ARRAY_BUFFER, fvbo);
glBufferData(GL_ARRAY_BUFFER, vertexPositionsBuffer, GL_STATIC_DRAW);
int ksvbo=glGenBuffers();
FloatBuffer ksBuffer=BufferUtils.createFloatBuffer(kvaoSize);
ksBuffer.put(ks);
ksBuffer.flip();
glBindBuffer(GL_ARRAY_BUFFER, ksvbo);
glBufferData(GL_ARRAY_BUFFER, ksBuffer, GL_STATIC_DRAW);
int kavbo=glGenBuffers();
FloatBuffer kaBuffer=BufferUtils.createFloatBuffer(kvaoSize);
kaBuffer.put(ka);
kaBuffer.flip();
glBindBuffer(GL_ARRAY_BUFFER, kavbo);
glBufferData(GL_ARRAY_BUFFER, kaBuffer, GL_STATIC_DRAW);
int kdvbo=glGenBuffers();
FloatBuffer kdBuffer=BufferUtils.createFloatBuffer(kvaoSize);
kdBuffer.put(kd);
kdBuffer.flip();
glBindBuffer(GL_ARRAY_BUFFER, kdvbo);
glBufferData(GL_ARRAY_BUFFER, kdBuffer, GL_STATIC_DRAW);
int vao = glGenVertexArrays();
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, fvbo);
glVertexAttribPointer(0, 4, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, ksvbo);
glVertexAttribPointer(3, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, kavbo);
glVertexAttribPointer(4, 3, GL_FLOAT, false, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, kdvbo);
glVertexAttribPointer(5, 3, GL_FLOAT, false, 0, 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glBindVertexArray(0);
glDeleteBuffers(fvbo);
glDeleteBuffers(ksvbo);
glDeleteBuffers(kavbo);
glDeleteBuffers(kdvbo);
ksBuffer=null;
kaBuffer=null;
kdBuffer=null;
vertexPositionsBuffer=null;
vaos.add(vao);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}catch(FileNotFoundException e){
// TODO Auto-generated catch block
e.printStackTrace();
}catch(IOException e){
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Object \""+f.getName().substring(0,f.getName().lastIndexOf("."))+"\" loaded, has "+facesd+" faces");
}
public void drawModel(Camera c,Vec3 offset){
//System.err.format("rendering model, %d vaos\n",vaos.size());
//Matrix4f modelMat=RenderMatrixHelper.getModelMatrix(offset,new Vec3(0));
faceShader.useShader();
c.useCameraView();
glUniform4f(faceOffset,offset.x,offset.y,offset.z,0f);
for(Integer i:vaos){
glBindVertexArray(i);
glDrawArrays(GL_TRIANGLES, 0, fvaoSize/4);
}
Shader.stopShader();
glBindVertexArray(0);
}
public int getNumFaces(){
return facesd;
}
private enum FaceType{
COORDSONLY,
TEXTURE,
NORMALS,
NORMALS_AND_TEXTURE,
}
}
I think I have correctly bound the K-VBO's to the correct location, but the shader is rendering them as black. Here is the vertex shader code:
#version 330
in vec4 position;
in vec3 normals;
in vec2 texCoords;
uniform vec4 trans;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 model;
void main()
{
gl_Position = projection*view* (position+trans);//-camera_position;
}
The fragment shader outputs black if the Kd has a value of (0,0,0), white otherwise. In my code, I have checked and know that Kd is (.64,.64,.64)
And the fragment shader:
#version 330
out vec4 outputColor;
in vec3 Ks;
in vec3 Ka;
in vec3 Kd;
uniform mat4 view;
uniform mat4 projection;
void main()
{
if(Kd==vec3(0f)){
outputColor=vec4(0f);
}else{
outputColor=vec4(1f);
}
}
I can't find the issue, but if anyone could it would be a great help. Thanks!
I didn't look at your code in full detail, but there seems to be a basic misunderstanding about how vertex attributes work. In the vertex shader, you have these attributes, which look fine:
in vec4 position;
in vec3 normals;
in vec2 texCoords;
But then in the fragment shader, you have these definitions:
in vec3 Ks;
in vec3 Ka;
in vec3 Kd;
Based on the code, it looks like you're planning to use these as vertex attributes. This will not work. You can't feed vertex attributes directly into the fragment shader. They need to be in variables in the vertex shader. If you need the (interpolated) values in the fragment shader, you need to pass them from vertex shader to fragment shader using out variables in the vertex shader, and in variables in the fragment shader.
If you do error checking, I'm surprised that your shader program even links. in variables in the fragment shader (at least as long as they are used in the code) need to have matching out variables in the vertex shader, which you do not have e.g. for Kd.
Of course if your material properties are constant at least for a single object, you could also make them uniforms instead of attributes.
One other aspect to watch out for once you have the more basic problems addressed is the location of the attributes. You have values 3, 4 and 5 hardwired for the material attributes. You will need to use calls like glGetAttribLocation(), glSetAttribLocation(), or location qualifiers in the shader code, to ensure that the locations you use for calls like glVertexAttribPointer() match up with the attribute locations in the shader program.
I've been following the tutorial at http://developer.android.com/resources/tutorials/opengl/opengl-es20.html for OpenGL ES on android. I've gotten to the, "Apply Projection and Camera View" section however I always seem to get a blank screen with no triangle, the previous section worked perfectly fine. I also tried just copy pasting the entire tutorial into my code but got the same result. Changing the line:
gl_Position = uMVPMatrix * vPosition;
to:
gl_Position = vPosition;
puts the application back to the first section (triangle stretches depending on screen orientation). Any idea what the problem is? Here's the code I have so far just in case I missed something:
public class GLTest20Renderer implements Renderer {
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix; \n" +
"attribute vec4 vPosition; \n" +
"void main(){ \n" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = uMVPMatrix * vPosition; \n" +
"} \n";
private final String fragmentShaderCode =
"precision mediump float; \n" +
"void main(){ \n" +
" gl_FragColor = vec4 (0.63671875, 0.76953125, 0.22265625, 1.0); \n" +
"} \n";
private FloatBuffer triangleVB;
private int mProgram;
private int maPositionHandle;
private int muMVPMatrixHandle;
private float[] mMVPMatrix = new float[16];
private float[] mMMatrix = new float[16];
private float[] mVMatrix = new float[16];
private float[] mProjMatrix = new float[16];
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
initShapes();
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // creates OpenGL program executables
// get handle to the vertex shader's vPosition member
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
}
public void onDrawFrame(GL10 unused) {
GLES20.glClear( GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT );
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// Prepare the triangle data
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 12, triangleVB);
GLES20.glEnableVertexAttribArray(maPositionHandle);
// Apply a ModelView Projection transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
}
private void initShapes() {
float triangleCoords[] = {
// X, Y, Z
-0.5f, -0.25f, 0,
0.5f, -0.25f, 0,
0.0f, 0.559016994f, 0
};
// initialize vertex Buffer for triangle
ByteBuffer vbb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
triangleCoords.length * 4);
vbb.order(ByteOrder.nativeOrder());// use the device hardware's native byte order
triangleVB = vbb.asFloatBuffer(); // create a floating point buffer from the ByteBuffer
triangleVB.put(triangleCoords); // add the coordinates to the FloatBuffer
triangleVB.position(0); // set the buffer to read the first coordinate
}
private int loadShader(int type, String shaderCode) {
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
I'm running all this on a Samsung Galaxy S2.
Fixed, just changed the near point in the lookat to be under 3:
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 2, 7);