EDIT: I'm thinking the problem might be when I'm loading the vertices and indices. Maybe focus on that section :)
I'm trying to load a heightmap from a bmp file and displaying it in OpenGL. As with most things I try, everything compiles and runs without errors but nothing is drawn on the screen. I can't seem to isolate the issue that much, since all the code works on its own, but when combined to draw terrain, nothing works.
Terrain class
I have a terrain class. It has 2 VBOs, 1 IBO and 1 VAO. It also stores the vertices, indices, colours of the vertices and the heights. It is loaded from a bmp file.
Loading terrain:
Terrain* Terrain::loadTerrain(const std::string& filename, float height)
{
BitMap* bmp = BitMap::load(filename);
Terrain* t = new Terrain(bmp->width, bmp->length);
for(unsigned y = 0; y < bmp->length; y++)
{
for(unsigned x = 0; x < bmp->width; x++)
{
unsigned char color =
(unsigned char)bmp->pixels[3 * (y * bmp->width + x)];
float h = height * ((color / 255.0f) - 0.5f);
t->setHeight(x, y, h);
}
}
delete bmp;
t->initGL();
return t;
}
Initializing the buffers:
void Terrain::initGL()
{
// load vertices from heights data
vertices = new Vector4f[w * l];
int vertIndex = 0;
for(unsigned y = 0; y < l; y++)
{
for(unsigned x = 0; x < w; x++)
{
vertices[vertIndex++] = Vector4f((float)x, (float)y, heights[y][x], 1.0f);
}
}
// generate indices for indexed drawing
indices = new GLshort[(w - 1) * (l - 1) * 6]; // patch count * 6 (2 triangles per terrain patch)
int indicesIndex = 0;
for(unsigned y = 0; y < (l - 1); ++y)
{
for(unsigned x = 0; x < (w - 1); ++x)
{
int start = y * w + x;
indices[indicesIndex++] = (GLshort)start;
indices[indicesIndex++] = (GLshort)(start + 1);
indices[indicesIndex++] = (GLshort)(start + w);
indices[indicesIndex++] = (GLshort)(start + 1);
indices[indicesIndex++] = (GLshort)(start + 1 + w);
indices[indicesIndex++] = (GLshort)(start + w);
}
}
// generate colours for the vertices
colours = new Vector4f[w * l];
for(unsigned i = 0; i < w * l; i++)
{
colours[i] = Vector4f(0.0f, 1.0f, 0.0f, 1.0f); // let's make the entire terrain green
}
// THIS CODE WORKS FOR CUBES (BEGIN)
// vertex buffer object
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// index buffer object
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// colours vertex buffer object
glGenBuffers(1, &colour_vbo);
glBindBuffer(GL_ARRAY_BUFFER, colour_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(colours), colours, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// create vertex array object
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, colour_vbo);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBindVertexArray(0);
// THIS CODE WORKS FOR CUBES (END)
}
The part where I create the VBOs, IBO and VAO works fine for cubes, they are drawn nicely.
Rendering terrain:
void Terrain::render()
{
glUseProgram(shaderProgram);
glBindVertexArray(vao);
int indices_length = (w - 1) * (l - 1) * 6;
glDrawElements(GL_TRIANGLES, indices_length, GL_UNSIGNED_SHORT, 0);
}
Shaders
These are the vertex and fragment shaders.
Vertex:
#version 330
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 vertexColour;
out vec4 fragmentColour;
uniform vec3 offset;
uniform mat4 perspectiveMatrix;
void main()
{
vec4 cameraPos = position + vec4(offset.x, offset.y, offset.z, 0.0);
gl_Position = perspectiveMatrix * cameraPos;
fragmentColour = vertexColour;
}
Fragment:
#version 330
in vec4 fragmentColour;
out vec4 outputColour;
void main()
{
outputColour = fragmentColour;
}
Perspective matrix
Here are the settings for the "camera":
struct CameraSettings
{
static const float FRUSTUM_SCALE = 1.0f;
static const float Z_NEAR = 0.5f;
static const float Z_FAR = 3.0f;
static float perspective_matrix[16];
};
float CameraSettings::perspective_matrix[16] = {
FRUSTUM_SCALE,
0, 0, 0, 0,
FRUSTUM_SCALE,
0, 0, 0, 0,
(Z_FAR + Z_NEAR) / (Z_NEAR - Z_FAR),
-1.0f,
0, 0,
(2 * Z_FAR * Z_NEAR) / (Z_NEAR - Z_FAR),
0
};
The uniforms get filled in after initGL() is called:
// get offset uniform
offsetUniform = ShaderManager::getUniformLocation(shaderProgram, "offset");
perspectiveMatrixUniform = ShaderManager::getUniformLocation(shaderProgram, "perspectiveMatrix");
// set standard uniform data
glUseProgram(shaderProgram);
glUniform3f(offsetUniform, xOffset, yOffset, zOffset);
glUniformMatrix4fv(perspectiveMatrixUniform, 1, GL_FALSE, CameraSettings::perspective_matrix);
glUseProgram(0);
Could someone check out my code and give suggestions?
I'm pretty sure that when you say :
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
you actually want to say :
glBufferData(GL_ARRAY_BUFFER, sizeof (Vector4f) * w * l, vertices, GL_STATIC_DRAW);
(same to color buffer, etc)
Related
500x500 grid with 1000 sub Divisions:
Just one question.
Why is this happening ?
#include <iostream>
#include <sstream>
#include <vector>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "GameEngine.hpp"
#include "ShaderProgram.h"
#include "Camera.h"
#include "Mesh.h"
const char *title = "Terrain";
GameEngine engine;
OrbitCamera orbitCamera;
float gYaw = 0.0f;
float gPitch = 1.0f;
float gRadius = 200.0f;
const float MOUSE_SENSTIVITY = 0.25f;
bool gWireFrame = false;
void glfw_onKey(GLFWwindow *window, int key, int scancode, int action, int mode);
void glfw_onMouseMove(GLFWwindow *window, double posX, double posY);
void glfw_onMouseScroll(GLFWwindow *window, double deltaX, double deltaY);
int main()
{
if (!engine.init(1024, 768, title))
{
std::cerr << "OpenGL init failed" << std::endl;
std::cin.get();
return -1;
}
//set callbacks
glfwSetKeyCallback(engine.getWindow(), glfw_onKey);
glfwSetCursorPosCallback(engine.getWindow(), glfw_onMouseMove);
std::vector<Vertex> VER;
std::vector<glm::vec3> verts;
std::vector<unsigned int> indices;
std::vector<glm::vec3> norms;
int subDiv = 1000;
int width = 500;
int height = 500;
int size = 0;
for (int row = 0; row < subDiv; row++)
{
for (int col = 0; col < subDiv; col++)
{
float x = (float)((col * width) / subDiv - (width / 2.0));
float z = ((subDiv - row) * height) / subDiv - (height / 2.0);
glm::vec3 pos = glm::vec3(x, 0, z);
verts.push_back(pos);
}
}
size = subDiv * subDiv;
size = verts.size();
for (int row = 0; row < subDiv -1 ; row++)
{
for (int col = 0; col < subDiv -1; col++)
{
int row1 = row * (subDiv);
int row2 = (row+1) * (subDiv);
indices.push_back(row1+col);
indices.push_back(row1+col+1);
indices.push_back( row2+col+1);
indices.push_back(row1+col);
indices.push_back( row2+col+1);
indices.push_back(row2+col);
}
}
for (int i = 0; i < verts.size(); i++)
{
Vertex vertex;
vertex.position = verts[i];
vertex.normal = glm::vec3(0, 0, 0);
vertex.texCoords = glm::vec2(0, 0);
VER.push_back(vertex);
}
VER.begin();
for (int i = 0; i < indices.size(); i += 3)
{
Vertex a = VER[indices[i]];
Vertex b = VER[indices[i + 1]];
Vertex c = VER[indices[i + 2]];
glm::vec3 p = glm::cross(b.position - a.position, c.position - a.position);
VER[indices[i]].normal += p;
VER[indices[i + 1]].normal += p;
VER[indices[i + 2]].normal += p;
}
for (int i = 0; i < VER.size(); i++)
{
VER[i].normal = glm::normalize(VER[i].normal);
}
glm::vec3 cubePos = glm::vec3(0.0f, 0.0f, -5.0f);
GLuint vbo, vao, ibo;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, VER.size() * sizeof(Vertex), &VER[0], GL_STATIC_DRAW);
// Vertex Positions
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)0);
glEnableVertexAttribArray(0);
// Normals attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
// Vertex Texture Coords
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(6 * sizeof(GLfloat)));
glEnableVertexAttribArray(2);
int n = indices.size() * sizeof(unsigned int);
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
glBindVertexArray(0);
ShaderProgram shaderProgram;
shaderProgram.loadShaders("shaders/vert.glsl", "shaders/frag.glsl");
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
while (!glfwWindowShouldClose(engine.getWindow()))
{
glfwPollEvents();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 model, view, projection;
model = glm::mat4(1.0f);
orbitCamera.setLookAt(glm::vec3(0, 0, 0));
orbitCamera.rotate(gYaw, gPitch);
orbitCamera.setRadius(gRadius);
model = glm::translate(model, glm::vec3(0, 0, 0));
//model = glm::scale(model, glm::vec3(1, 0, 1));
//model = scaleMat;
projection = glm::perspective(glm::radians(45.0f), (float)engine.getWidth() / (float)engine.getHeight(), 0.00001f, 100.0f);
shaderProgram.use();
glm::vec3 viewPos;
viewPos.x = orbitCamera.getPosition().x;
viewPos.y = orbitCamera.getPosition().y;
viewPos.z = orbitCamera.getPosition().z;
shaderProgram.setUniform("projection", projection);
shaderProgram.setUniform("view", orbitCamera.getViewMatrix());
shaderProgram.setUniform("model", model);
shaderProgram.setUniform("lightPos", glm::vec3(5, 10, 10));
shaderProgram.setUniform("viewPos", viewPos);
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES,indices.size(), GL_UNSIGNED_INT, 0);
//glDrawArrays(GL_TRIANGLES, 0, VER.size());
glBindVertexArray(0);
glfwSwapBuffers(engine.getWindow());
}
//cleanup
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glfwTerminate();
return 0;
}
void glfw_onKey(GLFWwindow *window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, GL_TRUE);
}
if (key == GLFW_KEY_E && action == GLFW_PRESS)
{
gWireFrame = !gWireFrame;
if (gWireFrame)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
else
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
void glfw_onMouseMove(GLFWwindow *window, double posX, double posY)
{
static glm::vec2 lastMousePos = glm::vec2(0, 0);
if (glfwGetMouseButton(engine.getWindow(), GLFW_MOUSE_BUTTON_LEFT) == 1)
{
gYaw -= ((float)posX - lastMousePos.x) * MOUSE_SENSTIVITY;
gPitch += ((float)posY - lastMousePos.y) * MOUSE_SENSTIVITY;
}
if (glfwGetMouseButton(engine.getWindow(), GLFW_MOUSE_BUTTON_RIGHT) == 1)
{
float dx = 0.01f * ((float)posX - lastMousePos.x);
float dy = 0.01f * ((float)posY - lastMousePos.y);
gRadius += dx - dy;
}
lastMousePos.x = (float)posX;
lastMousePos.y = (float)posY;
}
This is the main code. Rest is just basic initializing code, nothing fancy.
I've tried changing the swapinterval but that doesn't seems to be the problem.
I can share code for the other classes if anyone wants to take a look. And I've also tried lowering the sub divisions.
Edit*
After increasing the value of far plane to 8000:
Still not crisp.
the edit with second image is telling you what is happening ... if tampering with znear/zfar changes output like that it means your depth buffer has low bitwidth to the range you want to use...
However increasing zfar should make things worse (you just for some reason don't see it maybe its cut off or some weird math accuracy singularity).
for me its usual to select the planes so:
zfar/znear < (2^depth_buffer_bitwidth)/2
check you depth_buffer_bitwidth
Try to use 24 bits (you might have 16 bits right now). That should work on all gfx cards these days. You can try 32 bits too but that will work only on newer cards. I am using this code to get the max I can:
What is the proper OpenGL initialisation on Intel HD 3000?
However you are using GLFW so you need to find how to do it in it ... probably there is some hint for this in it ...
increase znear as much as you can
tampering znear has much much more impact than zfar...
Use linear depth buffer
this is the best option for large depth range views like terrains that covers stuf in whole depth view range. See:
How to correctly linearize depth in OpenGL ES in iOS?
however you need shaders and new api for this... I do not think this is doable in old api but luckily you are on new api already ...
if none of above is enough
You can stack up more frustrums together at a cost of multiple rendering of the same geometry. for more info see:
Is it possible to make realistic n-body solar system simulation in matter of size and mass?
How do you initialize OpenGL?
Are you using GL_BLEND?
Using blending is nice to get anti-aliased polygon edges, however it also means your z-buffer gets updated even when a very translucent fragment is drawn. This prevents other opaque fragments with the same z-depth from being drawn, which might be what is causing those holes. You could try disabling GL_BLEND to see if the issue goes away.
What depth function are you using?
By default it is set to GL_LESS. You might want to try glDepthFunc(GL_LEQUAL); So fragments with the same z-depth will be drawn. However, due to rounding errors this might not solve your problem entirely.
I have done class which render 2d objects based on Dear ImGui DrawList, because it can draw many different variants of objects thanks index vector dynamic array and still stay well optimized. Dear ImGui can render 30k unfilled rects while having ~36fps and ~70MB on debug mode, without antialiasing (my computer). Mine very limited version draws 30k unfilled rects while having ~3 fps and ~130MB on debug mode.
class Renderer
{
public:
Renderer();
~Renderer();
void Create();
void DrawRect(float x, float y, float w, float h, GLuint color, float thickness);
void Render(float w, float h);
void Clear();
void ReserveData(int numVertices, int numElements);
void CreatePolygon(const Vector2* vertices, const GLuint verticesCount, GLuint color, float thickness);
GLuint vao, vbo, ebo;
GLShader shader;
Vertex* mappedVertex = nullptr;
GLuint* mappedElement = nullptr,
currentVertexIndex = 0;
std::vector<Vertex> vertexBuffer;
std::vector<GLuint> elementBuffer;
std::vector<Vector2> vertices;
};
const char* vtx =
R"(
#version 460 core
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec4 a_color;
out vec3 v_position;
out vec4 v_color;
uniform mat4 projection;
void main()
{
gl_Position = projection * vec4(a_position, 1.0);
v_color = a_color;
}
)";
const char* frag =
R"(
#version 460 core
layout (location = 0) out vec4 outColor;
in vec4 v_color;
void main()
{
outColor = v_color;
}
)";
void Renderer::Clear()
{
vertexBuffer.resize(0);
elementBuffer.resize(0);
vertices.resize(0);
mappedVertex = nullptr;
mappedElement = nullptr;
currentVertexIndex = 0;
}
void Renderer::Create()
{
glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo);
shader.VtxFromFile(vtx);
shader.FragFromFile(frag);
}
void Renderer::DrawRect(float x, float y, float w, float h, GLuint color, float thickness)
{
// Add vertices
vertices.push_back({ x, y });
vertices.push_back(Vector2(x, y + w));
vertices.push_back(Vector2( x, y ) + Vector2(w, h));
vertices.push_back(Vector2(x + w, y));
// Create rect
CreatePolygon(vertices.data(), vertices.size(), color, thickness);
}
void Renderer::Render(float w, float h)
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
shader.UseProgram();
shader.UniformMatrix4fv("projection", glm::ortho(0.0f, w, 0.0f, h));
GLuint elemCount = elementBuffer.size();
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void*)offsetof(Vertex, position));
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (const void*)offsetof(Vertex, position));
glBufferData(GL_ARRAY_BUFFER, vertexBuffer.size() * sizeof(Vertex), vertexBuffer.data(), GL_STREAM_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementBuffer.size() * sizeof(GLuint), elementBuffer.data(), GL_STREAM_DRAW);
const unsigned short* idxBufferOffset = 0;
glDrawElements(GL_TRIANGLES, elemCount, GL_UNSIGNED_INT, idxBufferOffset);
idxBufferOffset += elemCount;
glDeleteVertexArrays(1, &vao);
glDisable(GL_BLEND);
}
void Renderer::CreatePolygon(const Vector2* vertices, const GLuint verticesCount, GLuint color, float thickness)
{
// To create for example unfilled rect, we have to draw 4 rects with small sizes
// So, unfilled rect is built from 4 rects and each rect contains 4 vertices ( * 4) and 6 indices ( *6)
ReserveData(verticesCount * 4, verticesCount * 6);
for (GLuint i = 0; i < verticesCount; ++i)
{
const int j = (i + 1) == verticesCount ? 0 : i + 1;
const Vector2& position1 = vertices[i];
const Vector2& position2 = vertices[j];
Vector2 difference = position2 - position1;
difference *= difference.Magnitude() > 0 ? 1.0f / difference.Magnitude() : 1.0f;
const float dx = difference.x * (thickness * 0.5f);
const float dy = difference.y * (thickness * 0.5f);
mappedVertex[0].position = Vector2(position1.x + dy, position1.y - dx);
mappedVertex[1].position = Vector2(position2.x + dy, position2.y - dx);
mappedVertex[2].position = Vector2(position2.x - dy, position2.y + dx);
mappedVertex[3].position = Vector2(position1.x - dy, position1.y + dx);
mappedVertex[0].color = color;
mappedVertex[1].color = color;
mappedVertex[2].color = color;
mappedVertex[3].color = color;
mappedVertex += 4;
mappedElement[0] = currentVertexIndex;
mappedElement[1] = currentVertexIndex + 1;
mappedElement[2] = currentVertexIndex + 2;
mappedElement[3] = currentVertexIndex + 2;
mappedElement[4] = currentVertexIndex + 3;
mappedElement[5] = currentVertexIndex;
mappedElement += 6;
currentVertexIndex += 4;
}
this->vertices.clear();
}
void Renderer::ReserveData(int numVertices, int numElements)
{
currentVertexIndex = vertexBuffer.size();
// Map vertex buffer
int oldVertexSize = vertexBuffer.size();
vertexBuffer.resize(oldVertexSize + numVertices);
mappedVertex = vertexBuffer.data() + oldVertexSize;
// Map element buffer
int oldIndexSize = elementBuffer.size();
elementBuffer.resize(oldIndexSize + numElements);
mappedElement = elementBuffer.data() + oldIndexSize;
}
int main()
{
//Create window, init opengl, etc.
Renderer renderer;
renderer.Create();
bool quit=false;
while(!quit) {
//Events
//Clear color bit
renderer.Clear();
for(int i = 0; i < 30000; ++i)
renderer.DrawRect(100.0f, 100.0f, 50.0f, 50.0f, 0xffff0000, 1.5f);
renderer.Render(windowW, windowH);
//swap buffers
}
return 0;
}
Why is it that much slower?
How can I make it faster and less memory-consuming?
The biggest bottleneck in that code looks like your allocations are never amortized across frames, since you are clearing the buffers capacity instead of reusing them, leading you to lots of realloc/copies (probably Log2(n) reallocs/copies if your vector implementation grows by factor of 2). Try changing your .clear() call with .resize(0) and maybe you can have a more lazy/rare call to .clear() when things gets unused.
In debug or in release mode? Vectors are terribly slow in debug due to memory checking. Profiling should always be done in Release.
Profiling should be done both in Release and Debug/Unoptimized mode if you intend to ever use and work with your application in Debug/Unoptimized mode. The gross "zero-cost abstraction" lie of modern C++ is that it makes it a pain to work with a debugger because large applications don't run at correct frame-rate in "Debug" mode any more. Ideally you should always run all your applications in Debug mode. Do yourself a productivity favour and ALSO do some profiling/optimization for your worse case.
Good luck with your learning quest! :)
Solution
I do not use std::vector anymore. I use ImVector instead (it maybe your own implementation as well),
I set position directly to a Vector2.x/.y
I am trying to render 3D models with textures using Assimp. The conversion goes perfect, all textures positions and what not gets loaded. I have tested the texture images by drawing them to the screen in 2D.
For some reason it does not render the textures to the model.
I am a beginner in OpenGL so forgive me if i dont explain it right.
The tutorial I have based the code on is from here, but i stripped a big part since I have my own camera/movement system.
The model renders like this: http://i.stack.imgur.com/5sK9K.png
whilest the texture in use looks like this: http://i.stack.imgur.com/sWGp7.jpg
The relevant rendering code is the following:
Generating textures from data file:
int Mesh::LoadGLTextures(const aiScene* scene){
if (scene->HasTextures()) return -1; //yes this is correct
/* getTexture Filenames and Numb of Textures */
for (unsigned int m = 0; m<scene->mNumMaterials; m++){
int texIndex = 0;
aiReturn texFound;
aiString path; // filename
while ((texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path)) == AI_SUCCESS){
textureIdMap[path.data] = NULL; //fill map with textures, pointers still NULL yet
texIndex++;
}
}
int numTextures = textureIdMap.size();
/* create and fill array with GL texture ids */
GLuint* textureIds = new GLuint[numTextures];
/* get iterator */
std::map<std::string, GLuint>::iterator itr = textureIdMap.begin();
std::string basepath = getBasePath(path);
ALLEGRO_BITMAP *image;
for (int i = 0; i<numTextures; i++){
std::string filename = (*itr).first; // get filename
(*itr).second = textureIds[i]; // save texture id for filename in map
itr++; // next texture
std::string fileloc = basepath + filename; /* Loading of image */
image = al_load_bitmap(fileloc.c_str());
if (image) /* If no error occured: */{
GLuint texId = al_get_opengl_texture(image);
//glGenTextures(numTextures, &textureIds[i]); /* Texture name generation */
glBindTexture(GL_TEXTURE_2D, texId); /* Binding of texture name */
//redefine standard texture values
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* We will use linear
interpolation for magnification filter */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); /* We will use linear
interpolation for minifying filter */
textureIdMap[filename] = texId;
} else {
/* Error occured */
std::cout << "Couldn't load Image: " << fileloc.c_str() << "\n";
}
}
//Cleanup
delete[] textureIds;
//return success
return true;
}
Generating VBO/VAO:
void Mesh::genVAOsAndUniformBuffer(const aiScene *sc) {
struct MyMesh aMesh;
struct MyMaterial aMat;
GLuint buffer;
// For each mesh
for (unsigned int n = 0; n < sc->mNumMeshes; ++n){
const aiMesh* mesh = sc->mMeshes[n];
// create array with faces
// have to convert from Assimp format to array
unsigned int *faceArray;
faceArray = (unsigned int *)malloc(sizeof(unsigned int) * mesh->mNumFaces * 3);
unsigned int faceIndex = 0;
for (unsigned int t = 0; t < mesh->mNumFaces; ++t) {
const aiFace* face = &mesh->mFaces[t];
memcpy(&faceArray[faceIndex], face->mIndices, 3 * sizeof(unsigned int));
faceIndex += 3;
}
aMesh.numFaces = sc->mMeshes[n]->mNumFaces;
// generate Vertex Array for mesh
glGenVertexArrays(1, &(aMesh.vao));
glBindVertexArray(aMesh.vao);
// buffer for faces
glGenBuffers(1, &buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * mesh->mNumFaces * 3, faceArray, GL_STATIC_DRAW);
// buffer for vertex positions
if (mesh->HasPositions()) {
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * mesh->mNumVertices, mesh->mVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(vertexLoc);
glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0);
}
// buffer for vertex normals
if (mesh->HasNormals()) {
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * mesh->mNumVertices, mesh->mNormals, GL_STATIC_DRAW);
glEnableVertexAttribArray(normalLoc);
glVertexAttribPointer(normalLoc, 3, GL_FLOAT, 0, 0, 0);
}
// buffer for vertex texture coordinates
if (mesh->HasTextureCoords(0)) {
float *texCoords = (float *)malloc(sizeof(float) * 2 * mesh->mNumVertices);
for (unsigned int k = 0; k < mesh->mNumVertices; ++k) {
texCoords[k * 2] = mesh->mTextureCoords[0][k].x;
texCoords[k * 2 + 1] = mesh->mTextureCoords[0][k].y;
}
glGenBuffers(1, &buffer);
glEnableVertexAttribArray(texCoordLoc);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 2 * mesh->mNumVertices, texCoords, GL_STATIC_DRAW);
glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);
}
// unbind buffers
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// create material uniform buffer
aiMaterial *mtl = sc->mMaterials[mesh->mMaterialIndex];
aiString texPath; //contains filename of texture
if (AI_SUCCESS == mtl->GetTexture(aiTextureType_DIFFUSE, 0, &texPath)){
//bind texture
unsigned int texId = textureIdMap[texPath.data];
aMesh.texIndex = texId;
aMat.texCount = 1;
} else {
aMat.texCount = 0;
}
float c[4];
set_float4(c, 0.8f, 0.8f, 0.8f, 1.0f);
aiColor4D diffuse;
if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &diffuse))
color4_to_float4(&diffuse, c);
memcpy(aMat.diffuse, c, sizeof(c));
set_float4(c, 0.2f, 0.2f, 0.2f, 1.0f);
aiColor4D ambient;
if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &ambient))
color4_to_float4(&ambient, c);
memcpy(aMat.ambient, c, sizeof(c));
set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
aiColor4D specular;
if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &specular))
color4_to_float4(&specular, c);
memcpy(aMat.specular, c, sizeof(c));
set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
aiColor4D emission;
if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_EMISSIVE, &emission))
color4_to_float4(&emission, c);
memcpy(aMat.emissive, c, sizeof(c));
float shininess = 0.0;
unsigned int max;
aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS, &shininess, &max);
aMat.shininess = shininess;
glGenBuffers(1, &(aMesh.uniformBlockIndex));
glBindBuffer(GL_UNIFORM_BUFFER, aMesh.uniformBlockIndex);
glBufferData(GL_UNIFORM_BUFFER, sizeof(aMat), (void *)(&aMat), GL_STATIC_DRAW);
myMeshes.push_back(aMesh);
}
}
Rendering model:
void Mesh::recursive_render(const aiScene *sc, const aiNode* nd){
// draw all meshes assigned to this node
for (unsigned int n = 0; n < nd->mNumMeshes; ++n){
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, myMeshes[nd->mMeshes[n]].texIndex);
// bind VAO
glBindVertexArray(myMeshes[nd->mMeshes[n]].vao);
// draw
glDrawElements(GL_TRIANGLES, myMeshes[nd->mMeshes[n]].numFaces * 3, GL_UNSIGNED_INT, 0);
}
// draw all children
for (unsigned int n = 0; n < nd->mNumChildren; ++n){
recursive_render(sc, nd->mChildren[n]);
}
}
Any other relevant code parts can be found in my open github project https://github.com/kwek20/StrategyGame/tree/master/Strategy
Mesh.cpp is relevant, as well as main.cpp and Camera.cpp.
As far as I understaind I followed the guidelines well, created a VAO, created VBOs, added data and enabled the proper vertex array attriute tot render the scene with.
I have checked all the data variables and everything is filled according to plan
Could anyone here spot the mistake I have made and or explain it?
Some links are typed weird because of the limit I have :(
It would help if you posted your shaders also.
I can post some rendering code with textures if that helps you out:
Generating the texture for opengl and loading a grayscale (UC8) image with width and height into the GPU
void GLRenderer::getTexture(unsigned char * image, int width, int height)
{
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &mTextureID);
glBindTexture(GL_TEXTURE_2D, mTextureID);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, width, height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, image);
if (aux::checkGlErrors(__LINE__, __FILE__))assert(false);
glBindTexture(GL_TEXTURE_2D, 0);
}
Loading the vertices from assimp onto the gpu
//** buffer a obj file-style model, initialize the VAO
void GLRenderer::bufferModel(float* aVertexArray, int aNumberOfVertices, float* aNormalArray, int aNumberOfNormals, float* aUVList, int aNumberOfUVs, unsigned int* aIndexList, int aNumberOfIndices)
{
//** just to be sure we are current
glfwMakeContextCurrent(mWin);
//** Buffer all data in VBOs
glGenBuffers(1, &mVertex_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, mVertex_buffer_object);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * aNumberOfVertices * 3, aVertexArray, GL_STATIC_DRAW);
glGenBuffers(1, &mNormal_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, mNormal_buffer_object);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * aNumberOfNormals * 3, aNormalArray, GL_STATIC_DRAW);
glGenBuffers(1, &mUV_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, mUV_buffer_object);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * aNumberOfUVs * 2, aUVList, GL_STATIC_DRAW);
glGenBuffers(1, &mIndex_buffer_object);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndex_buffer_object);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * aNumberOfIndices, aIndexList, GL_STATIC_DRAW);
if (aux::checkGlErrors(__LINE__, __FILE__))assert(false);
//** VAO tells our shaders how to match up data from buffer to shader input variables
glGenVertexArrays(1, &mVertex_array_object);
glBindVertexArray(mVertex_array_object);
//** vertices first
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, mVertex_buffer_object);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
//** normals next
if (aNumberOfNormals > 0){
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, mNormal_buffer_object);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
}
//** UVs last
if (aNumberOfUVs > 0){
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, mUV_buffer_object);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, NULL);
}
//** indexing for reusing vertices in triangle-meshes
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndex_buffer_object);
//** check errors and store the number of vertices
if (aux::checkGlErrors(__LINE__, __FILE__))assert(false);
mNumVert = aNumberOfVertices;
mNumNormals = aNumberOfNormals;
mNumUVs = aNumberOfUVs;
mNumIndices = aNumberOfIndices;
}
The code above is called like:
//read vertices from file
std::vector<float> vertex, normal, uv;
std::vector<unsigned int> index;
//assimp-wrapping function to load obj to vectors
aux::loadObjToVectors("Resources\\vertices\\model.obj", vertex, normal, index, uv);
mPtr->bufferModel(&vertex[0], static_cast<int>(vertex.size()) / 3, &normal[0], static_cast<int>(normal.size()) / 3, &uv[0], static_cast<int>(uv.size()) / 2, &index[0], static_cast<int>(index.size()));
Then comes the shader-part:
In the vertex shader you just hand-through the UV-coordinate layer
#version 400 core
layout (location = 0) in vec3 vertexPosition_modelspace;
layout (location = 1) in vec3 vertexNormal_modelspace;
layout (location = 2) in vec2 vertexUV;
out vec2 UV;
[... in main then ...]
UV = vertexUV;
While in the fragment shader you assign the value to the pixel:
#version 400 core
in vec2 UV;
uniform sampler2D textureSampler;
layout(location = 0) out vec4 outColor;
[... in main then ...]
// you probably want to calculate lighting here then too, so its just the simplest way to get the texture inside
outColor = vec4(texture2D(textureSampler, UV).rgb, cosAngle);
//you can also check whether the UV coords are correctly bound by using:
outColor = vec4(UV.x, UV.y,1,1);
//and then checking the pixel-values in the resulting image (e.g. render it to a PBO and then download it onto the CPU for)
In the rendering loop also make sure that all the uniforms are correctly bound (especially texture related ones) and that the texture is active and bound
if (mTextureID != -1) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTextureID);
}
GLint textureLocation = glGetUniformLocation(mShaderProgram, "textureSampler");
glUniform1i(textureLocation, 0);
//**set the poligon mode
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//**drawElements because of indexing
glDrawElements(GL_TRIANGLES, mNumIndices, GL_UNSIGNED_INT, 0);
I hope I could help you!
Kind regards,
VdoP
I trying to use VAOs, VBOs and IBOs to draw a bunch of sphere over a plane. Before using these, everything was drawn as expected. After I started to use those, things got weird. I can't post my whole code here because I have 5 classes (but if necessary I can provide a link to my code), so I'll try to post what I think it's useful.
With this class I can draw a sphere:
SphereShaderProgram::SphereShaderProgram(std::string vertexShaderPath, std::string fragmentShaderPath) : ProgramManager(vertexShaderPath, fragmentShaderPath)
{
_sphereH = 20;
_sphereW = 20;
_vbo = 0;
_vao = 0;
_ibo = 0;
CreateProgram();
BuildSphere();
BuildVAO();
}
SphereShaderProgram::~SphereShaderProgram()
{
glDeleteVertexArrays(1, &_vao);
glDeleteBuffers(1, &_vbo);
glDeleteBuffers(1, &_ibo);
}
void SphereShaderProgram::DrawSphere(const glm::mat4 &Projection, const glm::mat4 &ModelView)
{
_ModelViewProjection = Projection * ModelView;
_ModelView = ModelView;
Bind(); //glUseProgram
glBindVertexArray(_vao);
LoadVariables();
glDrawElements(GL_TRIANGLES, _sphereIndexes.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
UnBind();
}
int SphereShaderProgram::Get1DIndex(int line, int column)
{
return line * (int) _sphereH + column;
}
void SphereShaderProgram::BuildSphere()
{
for (int l = 0; l < _sphereH - 1; l++)
{
for (int c = 0; c < _sphereW - 1; c++)
{
int v1_1 = Get1DIndex(l, c);
int v2_1 = Get1DIndex(l + 1, c + 1);
int v3_1 = Get1DIndex(l + 1, c);
int v1_2 = Get1DIndex(l, c);
int v2_2 = Get1DIndex(l, c + 1);
int v3_2 = Get1DIndex(l + 1, c + 1);
_sphereIndexes.push_back(v1_1);
_sphereIndexes.push_back(v2_1);
_sphereIndexes.push_back(v3_1);
_sphereIndexes.push_back(v1_2);
_sphereIndexes.push_back(v2_2);
_sphereIndexes.push_back(v3_2);
}
}
for (int l = 0; l < _sphereH; l++)
{
for (int c = 0; c < _sphereW; c++)
{
float theta = ((float) l / (_sphereH - 1)) * (float) PI;
float phi = ((float) c / (_sphereW - 1)) * 2 * (float) PI;
float x = sin(theta) * cos(phi);
float z = sin(theta) * sin(phi);
float y = cos(theta);
_sphereCoordinates.push_back(x);
_sphereCoordinates.push_back(y);
_sphereCoordinates.push_back(z);
}
}
}
void SphereShaderProgram::BuildVAO()
{
// Generate and bind the vertex array object
glGenVertexArrays(1, &_vao);
glBindVertexArray(_vao);
// Generate and bind the vertex buffer object
glGenBuffers(1, &_vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, _sphereCoordinates.size() * sizeof(float), &_sphereCoordinates[0], GL_STATIC_DRAW);
// Generate and bind the index buffer object
glGenBuffers(1, &_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, _sphereIndexes.size() * sizeof(unsigned int), &_sphereIndexes[0], GL_STATIC_DRAW);
glBindVertexArray(0);
}
void SphereShaderProgram::LoadUniformVariables()
{
glm::mat4 MVP = _ModelViewProjection;
glm::mat4 MV = _ModelView;
glm::mat3 N = glm::transpose(glm::inverse(glm::mat3(MV)));
glm::vec4 AC = glm::vec4(0.2, 0.2, 0.2, 1.0);
glm::vec4 DC = glm::vec4(0.7, 0.0, 0.0, 1.0);
glm::vec4 SC = glm::vec4(0.1, 0.1, 0.1, 1.0);
glm::vec3 LP = glm::vec3(1.0, 6.0, 4.0);
// OpenGL Matrices
GLuint ModelViewProjection_location = glGetUniformLocation(GetProgramID(), "mvpMatrix");
glUniformMatrix4fv(ModelViewProjection_location, 1, GL_FALSE, glm::value_ptr(MVP));
GLuint ModelView_location = glGetUniformLocation(GetProgramID(), "mvMatrix");
glUniformMatrix4fv(ModelView_location, 1, GL_FALSE, glm::value_ptr(MV));
GLuint Normal_location = glGetUniformLocation(GetProgramID(), "normalMatrix");
glUniformMatrix3fv(Normal_location, 1, GL_FALSE, glm::value_ptr(N));
// Lighting
GLuint AmbientColor_location = glGetUniformLocation(GetProgramID(), "ambientColor");
glUniform4fv(AmbientColor_location, 1, glm::value_ptr(AC));
GLuint DiffuseColor_location = glGetUniformLocation(GetProgramID(), "diffuseColor");
glUniform4fv(DiffuseColor_location, 1, glm::value_ptr(DC));
GLuint SpecularColor_location = glGetUniformLocation(GetProgramID(), "specularColor");
glUniform4fv(SpecularColor_location, 1, glm::value_ptr(SC));
GLuint LightPosition_location = glGetUniformLocation(GetProgramID(), "vLightPosition");
glUniform3fv(LightPosition_location, 1, glm::value_ptr(LP));
}
void SphereShaderProgram::LoadAtributeVariables()
{
// Vertex Attributes
GLuint VertexPosition_location = glGetAttribLocation(GetProgramID(), "vPosition");
glEnableVertexAttribArray(VertexPosition_location);
glVertexAttribPointer(VertexPosition_location, 3, GL_FLOAT, GL_FALSE, 0, 0);
}
void SphereShaderProgram::LoadVariables()
{
LoadUniformVariables();
LoadAtributeVariables();
}
And with that, a plane:
PlaneShaderProgram::PlaneShaderProgram(std::string vertexShaderPath, std::string fragmentShaderPath) : ProgramManager(vertexShaderPath, fragmentShaderPath)
{
CreateProgram();
_vbo = 0;
_vao = 0;
_ibo = 0;
BuildPlane();
BuildVAO();
}
PlaneShaderProgram::~PlaneShaderProgram()
{
glDeleteVertexArrays(1, &_vao);
glDeleteBuffers(1, &_vbo);
glDeleteBuffers(1, &_ibo);
}
void PlaneShaderProgram::DrawPlane(const glm::mat4 &Projection, const glm::mat4 &ModelView)
{
_ModelViewProjection = Projection * ModelView;
_ModelView = ModelView;
Bind();
glBindVertexArray(_vao);
LoadVariables();
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
UnBind();
}
void PlaneShaderProgram::BuildPlane()
{
_coordinates[0] = -1.0f;
_coordinates[1] = 0.0f;
_coordinates[2] = -1.0f;
_coordinates[3] = -1.0f;
_coordinates[4] = 0.0f;
_coordinates[5] = 1.0f;
_coordinates[6] = 1.0f;
_coordinates[7] = 0.0f;
_coordinates[8] = 1.0f;
_coordinates[9] = 1.0f;
_coordinates[10] = 0.0f;
_coordinates[11] = -1.0f;
_indexes[0] = 0;
_indexes[1] = 1;
_indexes[2] = 2;
_indexes[3] = 0;
_indexes[4] = 2;
_indexes[5] = 3;
}
void PlaneShaderProgram::BuildVAO()
{
// Generate and bind the vertex array object
glGenVertexArrays(1, &_vao);
glBindVertexArray(_vao);
// Generate and bind the vertex buffer object
glGenBuffers(1, &_vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(GLfloat), _coordinates, GL_STATIC_DRAW);
// Generate and bind the index buffer object
glGenBuffers(1, &_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(GLuint), _indexes, GL_STATIC_DRAW);
glBindVertexArray(0);
}
void PlaneShaderProgram::LoadUniformVariables()
{
// OpenGL Matrices
GLuint ModelViewProjection_location = glGetUniformLocation(GetProgramID(), "mvpMatrix");
glUniformMatrix4fv(ModelViewProjection_location, 1, GL_FALSE, glm::value_ptr(_ModelViewProjection));
}
void PlaneShaderProgram::LoadAtributeVariables()
{
// Vertex Attributes
GLuint VertexPosition_location = glGetAttribLocation(GetProgramID(), "vPosition");
glEnableVertexAttribArray(VertexPosition_location);
glVertexAttribPointer(VertexPosition_location, 3, GL_FLOAT, GL_FALSE, 0, 0);
}
void PlaneShaderProgram::LoadVariables()
{
LoadUniformVariables();
LoadAtributeVariables();
}
This, on the other hand, is my main:
int main(void)
{
// Set the error callback
glfwSetErrorCallback(ErrorCallback);
// Initialize GLFW
if (!glfwInit())
{
printf("Error initializing GLFW!\n");
exit(EXIT_FAILURE);
}
// Set the GLFW window creation hints - these are optional
glfwWindowHint(GLFW_SAMPLES, 4);
// Create a window and create its OpenGL context
GLFWwindow* window = glfwCreateWindow(width, height, "OpenGL 4 Base", NULL, NULL);
// If the window couldn't be created
if (!window)
{
fprintf(stderr, "Failed to open GLFW window.\n");
glfwTerminate();
exit(EXIT_FAILURE);
}
// Sets the context of the specified window on the calling thread
glfwMakeContextCurrent(window);
// Initialize GLEW
glewExperimental = true;
GLenum glewError = glewInit();
if (glewError != GLEW_OK)
{
printf("Error initializing GLEW! %s\n", glewGetErrorString(glewError));
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwSetKeyCallback(window, KeyCallback);
glfwSetWindowSizeCallback(window, WindowSizeCallback);
glfwSetScrollCallback(window, ScrollCallback);
// Set the view matrix
glm::mat4 ModelView = glm::lookAt(glm::vec3(0.0f, 7.0f, 15.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
// Init matrix stack
glm_ModelViewMatrix.push(ModelView);
PlaneShaderProgram PlaneShaderProgram("FloorVertexShader.txt", "FloorFragShader.txt");
SphereShaderProgram SphereShaderProgram("ADSPerVertexVertexShader.txt", "ADSPerVertexFragShader.txt");
//SphereShaderProgram SphereShaderProgram = SphereShaderProgram("ADSPerPixelVertexShader.txt", "ADSPerPixelFragShader.txt");
// Set a background color
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
// 3D objects
glEnable(GL_DEPTH_TEST);
float d = 2.0f;
float p0 = -10.0f + d / 2;
// Main Loop
while (!glfwWindowShouldClose(window))
{
// Clear color buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Clone current modelview matrix, which can now be modified
glm_ModelViewMatrix.push(glm_ModelViewMatrix.top());
{
//------- ModelView Transformations
// Zoom in/out
glm_ModelViewMatrix.top() = glm::translate(glm_ModelViewMatrix.top(), glm::vec3(0.0, 0.0, zoom));
// Rotation
glm_ModelViewMatrix.top() = glm::rotate(glm_ModelViewMatrix.top(), beta, glm::vec3(1.0, 0.0, 0.0));
glm_ModelViewMatrix.top() = glm::rotate(glm_ModelViewMatrix.top(), alpha, glm::vec3(0.0, 0.0, 1.0));
//------- Draw the plane
glm_ModelViewMatrix.push(glm_ModelViewMatrix.top());
{
glm_ModelViewMatrix.top() = glm::scale(glm_ModelViewMatrix.top(), glm::vec3(7.0f, 1.0f, 7.0f));
PlaneShaderProgram.DrawPlane(Projection, glm_ModelViewMatrix.top());
}
glm_ModelViewMatrix.pop();
//------- Draw spheres
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
glm_ModelViewMatrix.push(glm_ModelViewMatrix.top());
{
glm_ModelViewMatrix.top() = glm::scale(glm_ModelViewMatrix.top(), glm::vec3(0.5f, 0.5f, 0.5f));
glm_ModelViewMatrix.top() = glm::translate(glm_ModelViewMatrix.top(), glm::vec3(p0 + i * d, 1.0f, p0 + j * d));
SphereShaderProgram.DrawSphere(Projection, glm_ModelViewMatrix.top());
}
glm_ModelViewMatrix.pop();
}
}
}
glm_ModelViewMatrix.pop();
// Swap buffers
glfwSwapBuffers(window);
// Get and organize events, like keyboard and mouse input, window resizing, etc...
glfwPollEvents();
}
// Close OpenGL window and terminate GLFW
glfwDestroyWindow(window);
// Finalize and clean up GLFW
glfwTerminate();
exit(EXIT_SUCCESS);
}
Instantiating the plane and then the sphere program, I get the following result (no plane at all):
Changing the order, that is the result:
I'm trying to find a clue about what I'm missing, because I don't have any idea about what is wrong. Before using VAOs (just using glVertexAttribPointer and glDrawElements), everything was drawn correctly.
Thank you in advance.
The problem is with the placement of the glVertexAttribPointer() call. You're calling it in the LoadAtributeVariables() method, which in turn is called from the Draw*() method.
This should really be part of the VAO setup, for a couple of reasons:
It's inefficient to make the call on every redraw. This call sets up state that is part of the VAO state. That's the whole idea of using VAOs in the first place. You can set up all this state once during setup, and then only need to bind the VAO again before the draw call, which sets up all the state again with a single call.
In your case, the VBO is not bound at the time you make the call. glVertexAttribPointer() sets up the attribute to pull data from the currently bound VBO, i.e. the buffer bound as GL_ARRAY_BUFFER.
The first problem is only a performance issue. The second is the reason why your code does not work, since you do not have the correct VBO bound when glVertexAttribPointer() is called.
To fix this, you only need to move the LoadAtributeVariables() call into BuildVAO(), at this location:
// Generate and bind the vertex buffer object
glGenBuffers(1, &_vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, _sphereCoordinates.size() * sizeof(float), &_sphereCoordinates[0], GL_STATIC_DRAW);
LoadAtributeVariables();
and remove it from where it currently is, so that it is not called before each draw call anymore.
I'm trying to draw a terrain with GL_TRIANGLE_STRIP and glDrawElements but I'm having a really hard time understanding the indices thing behind glDrawElements...
Here's what I have so far:
void Terrain::GenerateVertexBufferObjects(float ox, float oy, float oz) {
float startWidth, startLength, *vArray;
int vCount, vIndex = -1;
// width = length = 256
startWidth = (width / 2.0f) - width;
startLength = (length / 2.0f) - length;
vCount = 3 * width * length;
vArray = new float[vCount];
for(int z = 0; z < length; z++) {
// vIndex == vIndex + width * 3 || width * 3 = 256 * 3 = 768
for(int x = 0; x < width; x++) {
vArray[++vIndex] = ox + startWidth + (x * stepWidth);
vArray[++vIndex] = oy + heights[z][x];
vArray[++vIndex] = oz + startLength + (z * stepLength);
}
}
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vCount, vArray, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void Terrain::DrawVBO(unsigned int texID, float ox, float oy, float oz) {
float terrainLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };
if(!generatedVBOs) {
GenerateVertexBufferObjects(ox, oy, oz);
generatedVBOs = true;
}
unsigned int indices[] = { 0, 768, 3, 771 };
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * 4, indices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexPointer(3, GL_FLOAT, 0, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, terrainLight);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
I believe my vArray is correct, I use the same values when drawing with glBegin(GL_TRIANGLE_STRIP)/glEnd which works just fine.
My guess was to use just the index of the x coordinate for each vertex. But I have no idea if that's the right way to use indices with glDrawElements.
0: Index of the x coordinate from the first vertex of the triangle. Location: (-128, -128).
768: Index of the x coordinate from the second vertex of the triangle. Location: (-128, -127)
3: Index of the x coordinate from the third vertex of the triangle. Location: (-127, -128)
771: Index of the x coordinate from the fourth vertex, which will draw a second triangle. Location: (-127, -127).
I think everything is making sense so far?
What's not working is that the location values above (which I doubled checked on vArray and they are correct) are not the same which glDrawElements is using. Two triangles are drawn but they are a lot bigger than what they should be. It starts correctly at (-128, -128) but it goes to something like (-125, -125) instead of (-127, -127).
I can't understand what I'm doing wrong here...
Using something like the following solves my problem:
unsigned int indices[] = { 0, 256, 1, 257 };
I think it's safe to assume that the index is the x coordinate and that OpenGL is expecting that to be followed by y and z but we shouldn't increase by 3 ourselves, the server does it for us.
And now that I think about it, glDrawElements has the word element on it, which in this case is a vertex with 3 coordinates as specified in glVertexPointer and we need to pass the indices to the element, not the vertex.
I feel so dumb now...