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.
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);