Search code examples
javaopenglglsllwjgl

OpenGL VAO multiple VBO binding issue


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!


Solution

  • 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.