I have a issue with OpenGL normals.
I'm rendering the dragon model, but I have some weird normal patterns.
Here is screenshot from my render:
render_screen
this is my buffer creation methods:
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
if (has_position) {
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * 3 * sizeof(float), vertices.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(kVertexArray);
glVertexAttribPointer(kVertexArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
}
if (has_normal) {
glGenBuffers(1, &normal_buffer);
glBindBuffer(GL_ARRAY_BUFFER, normal_buffer);
glBufferData(GL_ARRAY_BUFFER, normals.size() * 3 * sizeof(float), normals.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(kNormalArray);
glVertexAttribPointer(kNormalArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
}
if (has_tex_coord) {
glGenBuffers(1, &tex_coord_buffer);
glBindBuffer(GL_ARRAY_BUFFER, tex_coord_buffer);
glBufferData(GL_ARRAY_BUFFER, tex_coords.size() * 3 * sizeof(float), tex_coords.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(kTexCoordArray);
glVertexAttribPointer(kTexCoordArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
}
if (has_index) {
glGenBuffers(1, &index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * 3 * sizeof(unsigned short), indices.data(), GL_STATIC_DRAW);
glBindVertexArray(0);
}
and draw with:
glDrawElements(GL_TRIANGLES, indices.size() * 3, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));
Wavefront obj loader method:
bool Loadfs() {
CVector3<float> vertex;
CVector3<unsigned short> vindex, tindex, nindex;
while(file_stream->offset_ < file_stream->size_) {
if (file_stream->buffer_[file_stream->offset_] == '#') {
char comment[512] = {0};
file_stream->ReadLine(comment); // check return code -> "its local variable return address"
file_stream->SkipLine();
}
else if (file_stream->buffer_[file_stream->offset_] == 'v') {
file_stream->offset_++;
if (file_stream->buffer_[file_stream->offset_] == ' ') {
has_position = true;
file_stream->offset_++;
file_stream->SkipWhiteSpace();
vertex.x = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.y = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.z = file_stream->ReadFloat();
file_stream->SkipLine();
vertices.push_back(vertex);
}
else if (file_stream->buffer_[file_stream->offset_] == 'n') {
has_normal = true;
file_stream->offset_++;
file_stream->SkipWhiteSpace();
vertex.x = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.y = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.z = file_stream->ReadFloat();
file_stream->SkipLine();
normals.push_back(vertex);
}
else if (file_stream->buffer_[file_stream->offset_] == 't') {
has_tex_coord = true;
file_stream->offset_++;
file_stream->SkipWhiteSpace();
vertex.x = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.y = file_stream->ReadFloat();
file_stream->SkipWhiteSpace();
vertex.z = file_stream->ReadFloat();
file_stream->SkipLine();
tex_coords.push_back(vertex);
}
}
else if (file_stream->buffer_[file_stream->offset_] == 'f') {
has_index = true;
file_stream->offset_++;
file_stream->SkipWhiteSpace();
//
if (has_position) {
vindex.x = file_stream->ReadShort();
}
if (has_tex_coord) {
file_stream->offset_++;
tindex.x = file_stream->ReadShort();
}
if (has_normal) {
file_stream->offset_++;
nindex.x = file_stream->ReadShort();
}
file_stream->SkipWhile(' ');
//
if (has_position) {
vindex.y = file_stream->ReadShort();
}
if (has_tex_coord) {
file_stream->offset_++;
tindex.y = file_stream->ReadShort();
}
if (has_normal) {
file_stream->offset_++;
nindex.y = file_stream->ReadShort();
}
file_stream->SkipWhile(' ');
//
if (has_position) {
vindex.z = file_stream->ReadShort();
}
if (has_tex_coord) {
file_stream->offset_++;
tindex.z = file_stream->ReadShort();
}
if (has_normal) {
file_stream->offset_++;
nindex.z = file_stream->ReadShort();
}
vi.push_back(--vindex);
ti.push_back(--tindex);
ni.push_back(--nindex);
//indices.push_back(--vindex);
}
else file_stream->SkipLine();
}
indices.insert(indices.end(), vi.begin(), vi.end());
return true;
}
Here vs:glsl main methods:
position_ = MV * vec4(vVertex, 1.0);
normal_ = normalize(N * vNormal);
texture_ = vTexture;
//shadow_ = S * M * vec4(vVertex, 1.0);
gl_Position = MVP * vec4(vVertex, 1.0);
and fs.glsl main methods:
vec3 rgb = vec3(1.0, 1.0, 1.0);
rgb = PhongShade(g_light, g_material, position_, normal_);
_frag_color = vec4(rgb, 1.0);
//_frag_color = texel * vec4(ambient + diffuse + specular, 1.0);
Anyone got any thoughts?
The problem is that OpenGL uses a single index buffer to index the vertex position, texture coordinates and normal buffers, using the same index for each (3 indices for a triangle), whereas the Wavefront obj format allows each face to specify separate indices for the vertex position, texture coordinate and normal independently (9 indices in total for a triangle).
It's not clear from your code but most likely you are not actually using the ti and ni index arrays that you are reading in, but are creating your index_buffer from vi, so the vertex indices are being used to index into normal_buffer and tex_coord_buffer, giving the strange results.
To fix this issue you need to create buffers of vertex coordinates, tex coordinates and normal coordinates with an entry for each unique combination of vertex position index, tex coord index and normal index that is used in a face definition in the obj file, then make your index buffer index into these arrays such that a particular face vertex has the same index for each. Ideally this would be done as a pre-processing step instead of at load time.
Alternatively, abandon indexed rendering with glDrawElements and use glDrawArrays instead. Write out one entry to each of the arrays for each face vertex, based on the face vertex indices for the position, tex coord and normal, and then render that. This is less efficient however.
Another solution (if it's possible) is to export the obj file in such a way that each face vertex uses the same indices for position, tex coord and normal.
Related
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.
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)
I want to render a Quad via VAO, IBO and VBO but nothing is drawn. I'm using glDrawRangeElements in OS X OpenGL 3.2 Core context. The screen is completely black without any error. GLFW3 is used to create window and context.
Window opening/Context creation code
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
_mainWindow = glfwCreateWindow(width, height, title, monitor, NULL);
if(_mainWindow == NULL)
{
return false;
}
_mainWindowWidth = width;
_mainWindowHeight = height;
glfwSetKeyCallback(_mainWindow, _onKeyEvent);
glfwMakeContextCurrent(_mainWindow);
glewExperimental = GL_TRUE;
glewInit();
_openGLVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
Shader sources (compiled successfully). Custom FragColor is binded.
_vertexShaderSource =
"#version 150 core\n"
"in vec3 in_Position;\n"
"in vec3 in_Normal;\n"
"in vec2 in_TexCoord;\n"
"uniform mat4 ModelViewProjection;\n"
"void main()\n"
"{\n"
" gl_Position = ModelViewProjection * vec4(in_Position, 1.0);\n"
"}\n";
_fragmentShaderSource =
"#version 150 core\n"
"out vec4 FColor;"
"void main()\n"
"{\n"
" FColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
"}\n";
Vertices
Vertex* vertices = new Vertex[6];
vertices[0].nz = 1.0f;
vertices[1].nz = 1.0f;
vertices[2].nz = 1.0f;
vertices[3].nz = 1.0f;
vertices[4].nz = 1.0f;
vertices[5].nz = 1.0f;
vertices[1].y = height;
vertices[1].v0 = 1.0f;
vertices[2].x = width;
vertices[2].y = height;
vertices[2].u0 = 1.0f;
vertices[2].v0 = 1.0f;
vertices[4].x = width;
vertices[4].u0 = 1.0f;
vertices[5].x = width;
vertices[5].y = height;
vertices[5].u0 = 1.0f;
vertices[5].v0 = 1.0f;
_mesh->setVertices(vertices, 6);
vertices = NULL;
Uint16* indices = new Uint16[6];
indices[0] = 0;
indices[1] = 2;
indices[2] = 3;
indices[3] = 0;
indices[4] = 1;
indices[5] = 3;
_mesh->setIndices(indices);
indices = NULL;
Buffer update (checked the data, it seems to be correct)
// Update VBO
if(_vertexBufferObjectID == 0)
{
glGenBuffers(1, &_vertexBufferObjectID);
}
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferObjectID);
float* data = new float[_vertexCount * sizeof(Vertex) / sizeof(float)];
Uint64 begin = 0;
for(Uint32 i = 0; i < _vertexCount; i++)
{
begin = i * 8;
data[begin] = _vertices[i].x;
data[begin + 1] = _vertices[i].y;
data[begin + 2] = _vertices[i].z;
data[begin + 3] = _vertices[i].nx;
data[begin + 4] = _vertices[i].ny;
data[begin + 5] = _vertices[i].nz;
data[begin + 6] = _vertices[i].u0;
data[begin + 7] = _vertices[i].v0;
}
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * _vertexCount, &data[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
delete[] data;
data = NULL;
// Update IBO
if(_indexBufferObjectID == 0)
{
glGenBuffers(1, &_indexBufferObjectID);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObjectID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Uint16) * _vertexCount, &_indices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// Update VAO
if(_vertexArrayObjectID == 0)
{
glGenVertexArrays(1, &_vertexArrayObjectID);
}
glBindVertexArray(_vertexArrayObjectID);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferObjectID);
// Vertices
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), ((char*)NULL + (0)));
// Normals
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), ((char*)NULL + (12)));
// TexCoords
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), ((char*)NULL + (24)));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObjectID);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
Rendering code
glUseProgram(material->_programID);
GLuint mvp = glGetUniformLocation(material->_programID, "ModelViewProjection");
glUniformMatrix4fv(mvp, 1, false, glm::value_ptr(camera_mvp));
glBindVertexArray(node->_mesh->_vertexArrayObjectID);
// Draw
glDrawRangeElements(GL_TRIANGLES, 0, 3, node->_mesh->_vertexCount, GL_UNSIGNED_SHORT, NULL);
glBindVertexArray(0);
Note:
camera_mvp is Orthographic
0.00333333_0___________0 0
0__________0.00333333__0 0
0__________0__________-1 0
599________599_________0 1
Program Linking
_programID = glCreateProgram();
glAttachShader(_programID, vertex_shader);
glAttachShader(_programID, fragment_shader);
glLinkProgram(_programID);
glGetProgramiv(_programID, GL_LINK_STATUS, &result);
glGetProgramiv(_programID, GL_INFO_LOG_LENGTH, &loglen);
if(loglen > 0)
{
char* log = new char[loglen];
glGetProgramInfoLog(_programID, loglen, 0, log);
_lastInfoLog = log;
delete log;
log = NULL;
}
if(result == GL_FALSE)
{
glDeleteProgram(_programID);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
return false;
}
glUseProgram(_programID);
glBindAttribLocation(_programID, 0, "in_Position");
glBindAttribLocation(_programID, 1, "in_Normal");
glBindAttribLocation(_programID, 2, "in_TexCoord");
glBindFragDataLocation(_programID, 0, "FColor");
glUseProgram(0);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
You don't have layout in #version 150 so you have to use glGetAttribLocation() to ask OpenGL where it put your attributes and adjust the first parameter of your glVertexAttribPointer() calls appropriately.
Make sure you bind your program before calling glGetAttribLocation().
I'm using VC++10 + OpenGL + the Assimp Library to consume and then render some 3D models.
The code is rendering the positions correctly, but for some reason the textures are seriously bugged. My texcoords appear to be loading correctly as are the texture files themselves - however I can't help but feel that the issue must be located with the loaded textures themselves.
www.flickr.com/photos/95269725#N02/8685913640/in/photostream
{ i seem to have a lack of rep to post inline images }
********** EDIT1 : ***********
So, I've been using the awesome GDebugger application to debug and interrogate the OpenGL pipeline in realtime. 2 things stand out really :
1. The biggy here is that the loaded texture is meant to look like this ->
http://www.flickr.com/photos/95269725#N02/8688860034/in/photostream
but actually looks like this when loaded into OpenGL memory :
http://www.flickr.com/photos/95269725#N02/8688860042/in/photostream/
2. Not sure if this is still applicable(as discussed in the comments), however the GL_TEXTURE_2D state variable is always FALSE throughout the game loop.
So I'm going to have to play with the texture loading code to see if I can get any traction there and post another update.
A few big relevant code chunks{sorry!} :
* Vertex Shader *
#version 420
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 texCoord;
uniform mat4 cameraToClipMatrix;
uniform mat4 modelToCameraMatrix;
out vec2 oTexCoord;
out vec4 oNormal;
void main()
{
oTexCoord = texCoord;
vec4 cameraPos = modelToCameraMatrix * vec4(position,1.0);
gl_Position = cameraToClipMatrix * cameraPos;
oNormal = normalize(vec4(modelToCameraMatrix * vec4(normal,0.0)));
}
* Fragment Shader *
#version 420
in vec4 Normal;
in vec2 TexCoord;
layout (location = 0) out vec4 FragColor;
uniform sampler2D gSampler;
void main()
{
FragColor = texture(gSampler, TexCoord);
//FragColor = vec4(1.1, 0.0, 1.1, 1.0);
}
* GL Init etc *
void GLSystem::init() {
InitializeProgram();
glClearColor(0.75f, 0.75f, 1.0f, 1.0f);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LEQUAL);
glDepthRange(0.0f, 1.0f);
}
void GLSystem::InitializeProgram()
{
std::vector<GLuint> shaderList;
shaderList.push_back(LoadShader(GL_VERTEX_SHADER, "VertShader1.vert"));
shaderList.push_back(LoadShader(GL_FRAGMENT_SHADER, "FragShader1.frag"));
theProgram = CreateProgram(shaderList);
modelToCameraMatrixUnif = glGetUniformLocation(theProgram, "modelToCameraMatrix"); // view matrix
cameraToClipMatrixUnif = glGetUniformLocation(theProgram, "cameraToClipMatrix"); // projection matrix
m_samplerUnif = glGetUniformLocation(theProgram, "gSampler"); // grab the gSampler uniform location reference in the fragment shader
float fzNear = 1.0f; float fzFar = 45.0f;
cameraToClipMatrix[0].x = fFrustumScale;
cameraToClipMatrix[1].y = fFrustumScale;
cameraToClipMatrix[2].z = (fzFar + fzNear) / (fzNear - fzFar);
cameraToClipMatrix[2].w = -1.0f;
cameraToClipMatrix[3].z = (2 * fzFar * fzNear) / (fzNear - fzFar);
glUseProgram(theProgram);
glUniformMatrix4fv(cameraToClipMatrixUnif, 1, GL_FALSE, glm::value_ptr(cameraToClipMatrix));
glUseProgram(0);
}
* Texture Loading *
bool CTexture::Load() {
m_texObj = 0; // init to zero
std::auto_ptr<glimg::ImageSet> pImgSet;
try {
pImgSet.reset( glimg::loaders::stb::LoadFromFile(m_filename) );
m_texObj = glimg::CreateTexture( &(*pImgSet), 0); // generates a texture and returns the related texture id
//glimg::SingleImage image = pImgSet->GetImage(0, 0, 0);
//glimg::Dimensions dims = image.GetDimensions();
//GLuint targetTexType = glimg::GetTextureType( &(*pImgSet), 0); // not using this yet - but potentially might need to base this objects targetType on this interpreted value.
//glimg::OpenGLPixelTransferParams params = GetUploadFormatType(image.GetFormat(), 0);
//glPixelStorei(GL_UNPACK_ALIGNMENT, image.GetFormat().LineAlign());
//glGenTextures(1, &m_texObj);
//glActiveTexture(GL_TEXTURE0);
//glBindTexture(GL_TEXTURE_2D, m_texObj);
//glTexImage2D(m_targetType, 0, glimg::GetInternalFormat(image.GetFormat(), 0), dims.width, dims.height, 0, params.format, params.type, image.GetImageData());
//glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, dims.width, dims.height, 0, GL_RGB, GL_UNSIGNED_BYTE, image.GetImageData() );
/*glTexParameterf(m_targetType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(m_targetType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(m_targetType, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(m_targetType, GL_TEXTURE_WRAP_T, GL_REPEAT);*/
}
catch(glimg::loaders::stb::StbLoaderException &e) {
std::cout << "Warning : " << e.what() << " || .Image file loading failed for file : '" << m_filename << std::endl;
return false;
}
glBindTexture(m_targetType, 0); // Bind to default texture
return true;
}
* Mesh Loading *
#include "MeshModel.h"
// ----------------------------------------------------------------------------------------
#include "Texture.h"
#include "GLSystem.h"
#include "Game.h"
// ----------------------------------------------------------------------------------------
#include <assert.h>
// ----------------------------------------------------------------------------------------
MeshItem::MeshItem() {
}
MeshItem::MeshItem(MeshModel& p_meshModel) {
m_pmeshModel = &p_meshModel;
p_delete_object_data = true;
VBO = INVALID_OGL_VALUE;
IBO = INVALID_OGL_VALUE;
NBO = INVALID_OGL_VALUE;
TBO = INVALID_OGL_VALUE;
NumVertices = 0;
NumFaces = 0;
NumIndices = 0;
MaterialIndex = INVALID_MATERIAL;
};
MeshItem::~MeshItem() {
if (VBO != INVALID_OGL_VALUE) {
glDeleteBuffers(1, &VBO);
}
if (IBO != INVALID_OGL_VALUE) {
glDeleteBuffers(1, &IBO);
}
if (NBO != INVALID_OGL_VALUE) {
glDeleteBuffers(1, &NBO);
}
if (TBO != INVALID_OGL_VALUE) {
glDeleteBuffers(1, &TBO);
}
}
void MeshItem::BuildVBO() {
glGenVertexArrays(1, &VAO); /* Generate a vertex array object - container for all vertex attribute arrays */
glBindVertexArray(VAO); /* Bind this VAO as the current Vertex Attribute Array container [ Holds the state for all attributes i.e. not the Vertex and Index data ] */
// Positions
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * NumVertices * 3, &Positions[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // Positions
// Indices
glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * NumFaces * 3, &Indices[0], GL_STATIC_DRAW);
// Normals
glGenBuffers(1, &NBO);
glBindBuffer(GL_ARRAY_BUFFER, NBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * NumVertices * 3, &Normals[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0); // Normals
// TexCoords
glGenBuffers(1, &TBO);
glBindBuffer(GL_ARRAY_BUFFER, TBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * NumVertices * 2, &TexCoords[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0); // TexCoords
glBindVertexArray(0); // Unbind the VAO
glBindBuffer(GL_ARRAY_BUFFER,0); // Unbind the vertices array buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // Unbind the indices array buffer
// Our copy of the data is no longer necessary, it is safe in the graphics card memory
if(p_delete_object_data) {
Positions.erase( Positions.begin(), Positions.end() );
Indices.erase( Indices.begin(), Indices.end() );
Normals.erase( Normals.begin(), Normals.end() );
TexCoords.erase( TexCoords.begin(), TexCoords.end() );
}
}
// ********************* MESHMODEL *********************
MeshModel::MeshModel(GLSystem& p_gls)
: m_pgls(&p_gls)
{
m_texUnit = 0;
m_samplerObj = 0;
}
MeshModel::~MeshModel() {
Clear();
}
GLSystem& MeshModel::getGLSystem() {
return *m_pgls;
}
void MeshModel::Clear() {
//for (unsigned int i = 0 ; i < m_textures.size() ; i++) {
// m_textures[i]);
//}
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
bool MeshModel::LoadMesh(const std::string& p_filename) {
Clear(); // Release the previously loaded mesh (if it exists)
bool Ret = false;
Assimp::Importer Importer;
const aiScene* pScene = Importer.ReadFile(p_filename.c_str(), aiProcess_Triangulate | aiProcess_GenSmoothNormals /* | aiProcess_FlipWindingOrder*/ /* | aiProcess_FlipUVs*/ | aiProcess_ValidateDataStructure);
//const aiScene* pScene = aiImportFile(p_filename.c_str(), aiProcessPreset_TargetRealtime_MaxQuality);
if (pScene) {
printf("3D Object File '%s' loaded successfully.\n", p_filename.c_str() );
Ret = InitFromScene(pScene, p_filename);
}
else {
printf("Error parsing '%s': '%s'.\n", p_filename.c_str(), Importer.GetErrorString());
}
return Ret;
}
bool MeshModel::InitFromScene(const aiScene* pScene, const std::string& p_filename) {
//m_meshItems.resize(pScene->mNumMeshes);
m_textures.resize(pScene->mNumMaterials);
InitMaterials(pScene, p_filename); // load materials/textures etc
// Initialize the meshes in the scene one by one
for (unsigned int i = 0 ; i < pScene->mNumMeshes ; i++) {
const aiMesh* paiMesh = pScene->mMeshes[i];
MeshItem mItem(*this);
InitMesh(mItem, paiMesh);
mItem.BuildVBO();
m_meshItems.push_back(mItem);
}
return true;
}
void MeshModel::InitMesh(MeshItem& p_meshItem, const aiMesh* p_paiMesh) {
p_meshItem.MaterialIndex = p_paiMesh->mMaterialIndex;
// Indices
p_meshItem.NumFaces = p_paiMesh->mNumFaces;
p_meshItem.NumIndices = p_meshItem.NumFaces * 3;
p_meshItem.Indices.resize(p_meshItem.NumIndices);
for (unsigned int i = 0 ; i < p_paiMesh->mNumFaces ; ++i) {
const aiFace& face = p_paiMesh->mFaces[i];
assert(face.mNumIndices == 3);
p_meshItem.Indices[i*3+0] = face.mIndices[0];
p_meshItem.Indices[i*3+1] = face.mIndices[1];
p_meshItem.Indices[i*3+2] = face.mIndices[2];
}
p_meshItem.NumVertices = p_paiMesh->mNumVertices;
p_meshItem.Positions.resize(p_meshItem.NumVertices * 3);
p_meshItem.Normals.resize(p_meshItem.NumVertices * 3);
p_meshItem.TexCoords.resize(p_meshItem.NumVertices * 2);
for (unsigned int i = 0 ; i < p_paiMesh->mNumVertices ; ++i) {
// Positions
if( p_paiMesh->HasPositions() ) {
p_meshItem.Positions[i*3+0] = p_paiMesh->mVertices[i].x;
p_meshItem.Positions[i*3+1] = p_paiMesh->mVertices[i].y;
p_meshItem.Positions[i*3+2] = p_paiMesh->mVertices[i].z;
}
// Normals
if( p_paiMesh->HasNormals() ) {
p_meshItem.Normals[i*3+0] = p_paiMesh->mNormals[i].x;
p_meshItem.Normals[i*3+1] = p_paiMesh->mNormals[i].y;
p_meshItem.Normals[i*3+2] = p_paiMesh->mNormals[i].z;
}
// TexCoords
if( p_paiMesh->HasTextureCoords(0) ) {
p_meshItem.TexCoords[i*2+0] = p_paiMesh->mTextureCoords[0][i].x;
p_meshItem.TexCoords[i*2+1] = p_paiMesh->mTextureCoords[0][i].y;
}
}
}
bool MeshModel::InitMaterials(const aiScene* pScene, const std::string& p_filename) {
// Extract the directory part from the file name
std::string::size_type SlashIndex = p_filename.find_last_of("/");
std::string Dir;
if (SlashIndex == std::string::npos) {
Dir = ".";
}
else if (SlashIndex == 0) {
Dir = "/";
}
else {
Dir = p_filename.substr(0, SlashIndex);
}
bool Ret = true;
// Initialize the materials
for (unsigned int i = 0 ; i < pScene->mNumMaterials ; i++) {
const aiMaterial* pMaterial = pScene->mMaterials[i];
m_textures[i] = NULL;
std::string FullPath = "";
if (pMaterial->GetTextureCount(aiTextureType_DIFFUSE) > 0) {
aiString Path;
if (pMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &Path, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) {
FullPath = Dir + "/" + Path.data;
m_textures[i] = std::make_shared<CTexture>( GL_TEXTURE_2D, FullPath.c_str() );
if ( !m_textures[i]->Load() ) {
printf("Error loading texture '%s'.\n", FullPath.c_str());
m_textures[i].reset();
m_textures[i] = NULL;
Ret = false;
}
else {
printf("Texture File '%s' loaded successfully\n", FullPath.c_str());
}
}
}
// Load a white texture in case the model does not include its own texture
if (!m_textures[i]) {
m_textures[i] = std::make_shared<CTexture>( GL_TEXTURE_2D, "..//Data/Textures/white.png");
printf("A default Texture File was loaded for '%s'.\n", FullPath.c_str());
Ret = m_textures[i]->Load();
}
}
// Genertate a Sampler object
glGenSamplers(1, &m_samplerObj);
glSamplerParameteri(m_samplerObj, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(m_samplerObj, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(m_samplerObj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(m_samplerObj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return Ret;
}
void MeshModel::DrawMesh() {
for (unsigned int i = 0 ; i < m_meshItems.size() ; i++) {
glUseProgram( getGLSystem().getProgram() ); // Bind to our selected shader program
glBindVertexArray(m_meshItems[i].VAO);
const unsigned int MaterialIndex = m_meshItems[i].MaterialIndex;
// If textures exist then bind them to samplers etc
if (MaterialIndex < m_textures.size() && m_textures[MaterialIndex]) {
glUniform1i(m_pgls->m_samplerUnif, 0);
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, m_textures[MaterialIndex]->m_texObj);
glBindSampler(0, m_samplerObj);
} else {
printf("MeshItem has no material!");
}
// RTS
glutil::MatrixStack currMatrix;
currMatrix.Translate(glm::vec3(0.0f, -3.0f, -10.0f));
currMatrix.Scale(0.1f, 0.1f, 0.1f);
currMatrix.RotateX(-90);
float a = Game::m_tick.asSeconds() /10;
float fAngRad = m_pgls->ComputeAngleRad(a, 2.0);
float fCos = cosf(fAngRad);
float fSin = sinf(fAngRad);
glm::mat3 theMat(1.0f);
theMat[0].x = fCos; theMat[1].x = -fSin;
theMat[0].y = fSin; theMat[1].y = fCos;
currMatrix.ApplyMatrix(glm::mat4(theMat));
glUniformMatrix4fv(m_pgls->modelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(currMatrix.Top()));
glDrawElements(GL_TRIANGLES, m_meshItems[i].NumIndices, GL_UNSIGNED_INT, 0);
glBindVertexArray(0); // Unbind the VAO
glUseProgram(0); // Close the link to the bound shader programs
}
}
I notice your vertex shader declares:
out vec2 oTexCoord;
but your fragment shader declares:
in vec2 TexCoord;
This might leave your texture coordinates undefined.
I think you need to enable textures with glEnable(GL_TEXTURES_2D) in your init section. I get the same look by commenting out that line from my project. Here's the code, if that helps:
EnableGraphics::EnableGraphics()
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glOrtho(-1.0, 1.0, -1.0, 1.0, 0.0, 1.0);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // typical alpha transparency
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
EDIT:
In case your link dies, I should add that your screenshot shows a 3D model with no textures or shading, although it has colors.
My render method currently looks like this:
void Renderer::render() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
checkGlError("glClear");
EntityCamera* camera = (EntityCamera*) resourceManager_->getResource(GHOST_CAMERA);
mat4 proj;
Matrix::projection3D(proj, 45.0f,
(float) nScreenWidth_ / nScreenHeight_, GHOST_NEAR_DISTANCE, GHOST_FAR_DISTANCE);
mat4 view;
Matrix::multiply(proj, camera_->getMatrix(), view);
camera->extractPlanes(view);
for (vector<Node*>::const_iterator it = renderArray_.begin(); it != renderArray_.end();
it++) {
Node* node = *it;
if (!node->isRenderable()) {
continue;
}
if (node->hasBV() && node->getBV()->isInFrustum(camera, node) == BoundingVolume::OUTSIDE) {
LOGI("Node %s is outside :O", node->getName().c_str());
continue;
}
EntityModel* entity =
static_cast<EntityModel*>(resourceManager_->getResource(
(*it)->getEntity()));
if (entity == 0 || entity->getVertices() == 0 || entity->getVertices()->size() == 0) {
LOGI("Empty entity %s.", node->getName().c_str());
continue;
}
Resource* resource = resourceManager_->getResource(node->getShader());
Shader* shader = static_cast<Shader*>(resource);
Resource* resource2 = resourceManager_->getResource(entity->getTexture());
Image* image = static_cast<Image*>(resource2);
mat4 res;
Matrix::multiply(view, node->getMatrix(), res);
// Select shader program to use.
glUseProgram(shader->getId());
checkGlError("glUseProgram");
int matrix = glGetUniformLocation(shader->getId(), "uWVP");
int texture = glGetUniformLocation(shader->getId(), "texture_0");
checkGlError("glGetUniformLocation");
int textureCoords = glGetAttribLocation(shader->getId(), "attrTexCoords");
int vertices = glGetAttribLocation(shader->getId(), "attrPos");
checkGlError("glGetAttribLocation");
// Specify WVP matrix.
glUniformMatrix4fv(matrix, 1, false, res);
checkGlError("glUniformMatrix4fv");
// Load vertex positions.
if (!entity->isCompiled()) {
//LOGI("Entity %s, not compiled.", entity->getName().c_str());
continue;
}
glEnableVertexAttribArray(vertices);
checkGlError("glEnableVertexAttribArray");
//glVertexAttribPointer(vertices, 3, GL_FLOAT, GL_FALSE, 0,
// &(*entity->getVertices())[0]);
//LOGI("%s vbo id: %d", node->getName().c_str(), entity->getVBO());
glBindBuffer(GL_ARRAY_BUFFER, entity->getVBO());
checkGlError("glBindBuffer");
glVertexAttribPointer(vertices, 3, GL_FLOAT, GL_FALSE, 0, 0);
checkGlError("glVertexAttribPointer");
// Load UV coordinates.
glEnableVertexAttribArray(textureCoords);
checkGlError("glEnableVertexAttribArray");
glVertexAttribPointer(textureCoords, 2, GL_FLOAT, GL_FALSE, 0,
&(*entity->getTextureCoords())[0]);
checkGlError("glVertexAttribPointer");
// Bind the texture.
glActiveTexture(GL_TEXTURE0);
checkGlError("glActiveTexture");
glBindTexture(GL_TEXTURE_2D, image->getId());
checkGlError("glBindTexture");
glUniform1i(texture, 0);
checkGlError("glUniform1i");
if (entity->hasIndices()) {
vector<vector<GLushort>*>* indices = entity->getIndices();
for (unsigned int i = 0; i < indices->size(); i++) {
if (entity->hasBoundingVolumes()) {
BoundingVolume* volume = (*entity->getBoundingVolumes())[i];
if (volume->isInFrustum(camera, node) == BoundingVolume::OUTSIDE) {
continue;
}
}
vector<GLushort>* ind = (*indices)[i];
glDrawElements(GL_TRIANGLES, ind->size(), GL_UNSIGNED_SHORT, &(*ind)[0]);
checkGlError("glDrawElements");
}
}
else {
glDrawArrays(GL_TRIANGLES, 0, entity->getVertices()->size() / 3);
checkGlError("glDrawArrays");
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
checkGlError("glBindBuffer");
}
}
I just recently tried to use VBO, before I was sending vertex data directly and everything worked fine, textures were mapped correctly. Now I changed vertex array with VBO and even though it works, no textures are applied, I could only see black object.
What might be wrong with my textures?
Why when I change glVertexAttribPointer(vertices, 3, GL_FLOAT, GL_FALSE, 0, 0); line order with glBindBuffer(GL_ARRAY_BUFFER, entity->getVBO()); I get disfigured objects? Is this the right call order that I'm using?
You're sending your UV coordinates from plain memory, while you seem to send your vertex coordinates from a VBO. This may not be so efficient, you should have both data sets in VBO to profit of the VBO advantages.
That being said, I think your problem is that you don't unbind your VBO before sending your UV coordinates. Your code should be :
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(textureCoords, 2, GL_FLOAT, GL_FALSE, 0,
&(*entity->getTextureCoords())[0]);
as I suppose your getTextureCoords() does not return an offset in your VBO.