Search code examples
c++openglshaderopengl-3

OpenGL - Shader loads but can only display 2D triangles


I have completely run out of ideas. I am trying to get my shaders to work in OpenGL, and have taken my code back to the super basics in an attempt to do so.

When I run my code using glDrawArrays I get a blue triangle, which tells me my shaders are working. I can also set the colour within the frag shader to prove that it fixes a colour. As you can see I used the variable 'numOfVerts' which I have tested, any value below 3 and as expected nothing draws, however any value above 3 draws the same basic 2D triangle and not a cube. The code for thisis as follows:

int DrawGLScene(GLvoid)                     
{

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer

    glUseProgram(myShader.handle());  // use the shader
    GLuint matLocation = glGetUniformLocation(myShader.handle(), "ProjectionMatrix");
    glUniformMatrix4fv(matLocation, 1, GL_FALSE, &ProjectionMatrix[0][0]);
    //Draw code
    glBindVertexArray(m_vaoID);     // select VAO
    glDrawArrays(GL_TRIANGLES, 0, numOfVerts); //draw some geometry 
    glBindBuffer(GL_ARRAY_BUFFER, 0);  //unbind the buffer

    glBindVertexArray(0);
    glUseProgram(0); //turn off the current shader
    return TRUE;                                        // Keep Going
}

The other draw method I have in an attempt to draw the full cube uses glDrawElements, however all this does is show me a blank white screen:

int DrawGLScene(GLvoid)                             
{

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer

    glUseProgram(myShader.handle());  // use the shader
    GLuint matLocation = glGetUniformLocation(myShader.handle(), "ProjectionMatrix");
    glUniformMatrix4fv(matLocation, 1, GL_FALSE, &ProjectionMatrix[0][0]);

    //draw objects
    glBindVertexArray(m_vaoID);     // select VAO       
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
    // Done
    glBindVertexArray(0); //unbind the vertex array object

    return TRUE;                                        // Keep Going
}

This is my vert shader:

#version 150
uniform mat4 ProjectionMatrix;

in  vec3 in_Position;  // Position coming in
in  vec3 in_Color;     // colour coming in
out vec3 ex_Color;     // colour leaving the vertex, this will be sent to the fragment shader

void main(void)
{
    gl_Position = ProjectionMatrix * vec4(in_Position, 1.0);

    ex_Color = in_Color;
}

This is my frag shader:

#version 150
in  vec3 ex_Color;  //colour arriving from the vertex
out vec4 out_Color; //colour for the pixel

void main(void)
{
    out_Color = vec4(ex_Color,1.0);
}

The following code is what I have stripped my program to in an attempt to get this to work and shows my 'drawTest' method with all related vertex and index arrays along with related setup code. The drawTest method is called ONCE in my init function:

/**includes**/
#define GLEW_STATIC
#include <GL/glew.h>
#include <GL/wglew.h>
#include "boost\lexical_cast.hpp"
#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
#include "Image_Loading/nvImage.h"
#include <map>
#include "MenuController.h"
#include <windows.h>        // Header File For Windows
#include <gl\gl.h>          // Header File For The OpenGL32 Library
#include <gl\glu.h>         // Header File For The GLu32 Library
#include "../glm/glm.hpp"
#include "../glm/gtc/matrix_transform.hpp"
#include "../glm/gtc/type_ptr.hpp"
#include "../glm/gtc/matrix_inverse.hpp"
#include <vector>
#include <algorithm>    // std::reverse
#include <iostream>
#include "console.h"
#include "DistanceCalculator.h"
#include "Shader.h"

/* Variables*/
glm::mat4 ProjectionMatrix; // matrix for the orthographic projection
unsigned int m_vaoID;           // vertex array object
unsigned int m_vboID[2];        // two VBOs - used for colours and vertex data
unsigned int ibo;
const int numOfVerts = 3;
const int numOfTris = 1;
float verts[9];
float cols[9];
Shader myShader;  ///shader object 


//index array to draw basic cube
GLubyte indicesTest5[] = { 
    0,1,2,2,3,0,      // back
    5,6,7,7,4,5,     // front
    1,2,7,7,6,1,      // left
    0,3,4,4,5,0,      // right
    0,1,6,6,5,0,     // top
    2,3,4,4,7,2   // bottom
};


/*Method to draw a simple cube using shaders*/
void drawTest(){

    int dim = 20;
    //8 vertices to define a basic cube
    GLfloat verticesTest2[] = {
        /*0*/dim, dim, -dim,
        /*1*/-dim, dim, -dim,
        /*2*/-dim, -dim, -dim,
        /*3*/dim, -dim, -dim,
        /*4*/dim, -dim, dim,
        /*5*/dim, dim, dim,
        /*6*/-dim, dim, dim,
        /*7*/-dim, -dim, dim
    };

    //Create a vec3 array that will store the x,y,z of the 8 vertices more understandably
    std::vector<glm::vec3 > cubeVerts;
    for (int i = 0; i < 24; i += 3){ //verticesTest2 size of
        glm::vec3 vert = glm::vec3(verticesTest2[i], verticesTest2[i+1], verticesTest2[i+2]);
        cubeVerts.push_back(vert);
    }

    /*****THIS PART JUST BUILDS A VERT NORMALS LIST****/
    //define a vector to store the x,y,z of each vertex normal (so 8 in total)
    std::vector<glm::vec3 > vertNormals;    
    //loop for the amount of vertices that we have
    for (int i = 0; i < cubeVerts.size(); i++){
        //define tracking variables
        int currentVertexIndex = i;
        int faceCount = 0;
        glm::vec3 faceNormalSum = glm::vec3(0, 0, 0);

        //loop for all 36 indices within the index array
        for (int j = 0; j < 36; j++){
            //if the current vertex has been located in the index array
            if (indicesTest5[j] == currentVertexIndex){
                //calculate a modulo of the position to determine the vertex position in the index array (1st, 2nd or 3rd vertex of the triangle)
                int positionMod = j % 3;                
                glm::vec3 v1, v2, v3;

                //if the first vertex in a triangle, or the first array element
                if (positionMod == 0 || j == 0){ 
                    v1 = cubeVerts.at(indicesTest5[j]);
                    v2 = cubeVerts.at(indicesTest5[j+1]);
                    v3 = cubeVerts.at(indicesTest5[j+2]);
                }

                //if second vertex in a triangle, or the second array element
                else if (positionMod == 1 || j ==1){ 
                    v1 = cubeVerts.at(indicesTest5[j-1]);
                    v2 = cubeVerts.at(indicesTest5[j]);
                    v3 = cubeVerts.at(indicesTest5[j+1]);
                }

                //if third vertex in a triangle, or the third array element
                else if (positionMod == 2 || j == 2){ 
                    v1 = cubeVerts.at(indicesTest5[j-2]);
                    v2 = cubeVerts.at(indicesTest5[j-1]);
                    v3 = cubeVerts.at(indicesTest5[j]);
                }

                //increment the amount of faces surrounding the current vertex
                faceCount++;

                /*calculate face normal*/               
                //calculate 2 vectors of the current triangle
                glm::vec3 V = v2 - v1;
                glm::vec3 W = v3 - v1;
                //calculate the cross product of these vectors to get the face normal
                glm::vec3 faceNormal = glm::cross(V,W);
                //normalize the obtained face normal
                faceNormal = glm::normalize(faceNormal);
                //add this to the current sum of the facenormals values
                faceNormalSum += faceNormal;
            }           
        }
        //Once all indices checked for the current vertex, divide each x,y,z value in the faceNormalSum by the amount of
        //surrounding faces detected to give a mean value and therefore the normal for that vertex
        vertNormals.push_back(glm::vec3(faceNormalSum.x / faceCount, faceNormalSum.y / faceCount, faceNormalSum.z / faceCount));
    }
    /*****END OF VERT NORMALS CREATION*****/    

    //Create a vector to store all the vertex normal values as floats (much like the vertices list)
    vector<float> vertNormalVector;
    vertNormalVector.clear();
    for (int i = 0; i < vertNormals.size(); i++){
        vertNormalVector.push_back(vertNormals.at(i).x);
        vertNormalVector.push_back(vertNormals.at(i).y);
        vertNormalVector.push_back(vertNormals.at(i).z);
    }

    //create a pointer for the vertexNormal vector
    float* vertexNormal = &vertNormalVector[0];

    vector<Vertex> vertices;    
    for (int x = 0; x < 8; x++){
        Vertex vertStruct = Vertex();
        vertStruct.position = cubeVerts.at(x);
        vertStruct.normal = vertNormals.at(x);
        vertices.push_back(vertStruct);
    }

    //By this point I simply have a vertices array, a vertex normal array and an array of indices.
    //So I can set all the buffers shown below  

    // VAO allocation
    glGenVertexArrays(1, &m_vaoID);
    // First VAO setup
    glBindVertexArray(m_vaoID);
    glGenBuffers(2, m_vboID);
    glBindBuffer(GL_ARRAY_BUFFER, m_vboID[0]);

    //initialises data storage of vertex buffer object
    glBufferData(GL_ARRAY_BUFFER, 24 * sizeof(GLfloat), verticesTest2, GL_STATIC_DRAW);
    GLint vertexLocation = glGetAttribLocation(myShader.handle(), "in_Position");
    glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(vertexLocation);

    //storage of normals (will simply act as colours for this example)
    glBindBuffer(GL_ARRAY_BUFFER, m_vboID[1]);
    glBufferData(GL_ARRAY_BUFFER, 24 * sizeof(GLfloat), vertexNormal, GL_STATIC_DRAW);
    GLint colorLocation = glGetAttribLocation(myShader.handle(), "in_Color");
    glVertexAttribPointer(colorLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(colorLocation);

    //index buffer for indices defined above
    glGenBuffers(1, &ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 36 * sizeof(unsigned int), indicesTest5, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
}

/*Resize method to update projection matrix*/
GLvoid ReSizeGLScene(GLsizei width, GLsizei height)     // Resize And Initialize The GL Window
{
    if (height == 0)                                        // Prevent A Divide By Zero By
    {
        height = 1;                                     // Making Height Equal One
    }       
    glViewport(0, 0, width, height);                        // Reset The Current Viewport

    // Calculate The Aspect Ratio Of The Window
    gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 0.1f, 100.0f);*/
    aspectRatio = (GLfloat)width / (GLfloat)height;
    screenHeight = (GLfloat)height;
    screenWidth = (GLfloat)width;

    ProjectionMatrix = glm::perspective(60.0f, (GLfloat)screenWidth / (GLfloat)screenHeight, 1.0f, 200.0f);
}


/*Init to load shader and call drawTest defined above*/
int InitGL(GLvoid)                                      // All Setup For OpenGL Goes Here
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);               // Black Background

    if (!myShader.load("BasicView", "../glslfiles/basic.vert", "../glslfiles/basic.frag"))
    {
        cout << "failed to load shader" << endl;
    }
    drawTest();
    return TRUE;                                        // Initialization Went OK
}


/*This code is where I set up my glew*/
/*AT THE END OF MY 'CreateGLWindow' METHOD*/
    HGLRC tempContext = wglCreateContext(hDC);
    wglMakeCurrent(hDC, tempContext);
    glewExperimental = TRUE;
    if (glewInit() != GLEW_OK) {
        std::cout << "glewInit failed, aborting." << std::endl;
    }
    int attribs[] =
    {
        WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
        WGL_CONTEXT_MINOR_VERSION_ARB, 2,
        WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
        0
    };
    if (wglewIsSupported("WGL_ARB_create_context") == 1)
    {
        hRC = wglCreateContextAttribsARB(hDC, 0, attribs);
        wglMakeCurrent(NULL, NULL);
        wglDeleteContext(tempContext);
        wglMakeCurrent(hDC, hRC);
    }
    else
    {   //It's not possible to make a GL 3.x context. Use the old style context (GL 2.1 and before)
        hRC = tempContext;
        cout << " not possible to make context " << endl;
    }

    ShowWindow(hWnd, SW_SHOW);                      // Show The Window
    SetForegroundWindow(hWnd);                      // Slightly Higher Priority
    SetFocus(hWnd);                                 // Sets Keyboard Focus To The Window
    ReSizeGLScene(width, height);                   // Set Up Our Perspective GL Screen

    if (!InitGL())                                  // Initialize Our Newly Created GL Window
    {
        KillGLWindow();                             // Reset The Display
        MessageBox(NULL, "Initialization Failed.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
        return FALSE;                               // Return FALSE
    }

    return TRUE;                                    // Success
}

Does anybody know why I can't seem to render more than a single 2D triangle? Also I have no compile/runtime errors with any of the above code.


Solution

  • This was solved by following Reto Koradi's comment advice, which is to ensure that the same type is used when defining the indices, when loading them into the buffer and when drawing.
    So the correct working code is as follows:

    To create the list of indices declare them as GLuint:

    GLuint indicesTest5[] = { 
    0,1,2,2,3,0,     // back
    5,6,7,7,4,5,     // front
    1,2,7,7,6,1,     // left
    0,3,4,4,5,0,     // right
    0,1,6,6,5,0,     // top
    2,3,4,4,7,2      // bottom
    };    
    

    Ensure GLuint is used when loading into the buffer:

    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 36 * sizeof(GLuint), indicesTest5, GL_STATIC_DRAW);
    

    Ensure GL_UNSIGNED_INT is used when drawing:

    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);