Im trying to add Phong lighting to my scene in which I have loaded mesh with textures. This is my mesh loadin class:
#define INVALID_OGL_VALUE 0xFFFFFFFF
#define INVALID_MATERIAL 0xFFFFFFFF
#define SAFE_DELETE(p) if (p) { delete p; p = NULL; }
Mesh::MeshEntry::MeshEntry()
{
VB = INVALID_OGL_VALUE;
IB = INVALID_OGL_VALUE;
NumIndices = 0;
MaterialIndex = INVALID_MATERIAL;
};
Mesh::MeshEntry::~MeshEntry()
{
if (VB != INVALID_OGL_VALUE)
{
glDeleteBuffers(1, &VB);
}
if (IB != INVALID_OGL_VALUE)
{
glDeleteBuffers(1, &IB);
}
}
void Mesh::MeshEntry::Init(const std::vector<Vertex>& Vertices,
const std::vector<unsigned int>& Indices)
{
NumIndices = Indices.size();
glGenBuffers(1, &VB);
glBindBuffer(GL_ARRAY_BUFFER, VB);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * Vertices.size(), &Vertices[0], GL_STATIC_DRAW);
glGenBuffers(1, &IB);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IB);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * NumIndices, &Indices[0], GL_STATIC_DRAW);
}
Mesh::Mesh()
{
}
Mesh::~Mesh()
{
Clear();
}
void Mesh::Clear()
{
for (unsigned int i = 0 ; i < textures_cnt.size() ; i++) {
SAFE_DELETE(textures_cnt[i]);
}
}
/*Loads mesh
*#Filename - name of the .obj file
**********************************************************/
bool Mesh::LoadMesh(const std::string& Filename){
Assimp::Importer Importer;
bool rc = false;
//clear previous loaded mesh
Clear();
//read file content
const aiScene* oScene = Importer.ReadFile(Filename.c_str(), aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs );
if(oScene){
printf (" %i animations\n", oScene->mNumAnimations);
printf (" %i cameras\n", oScene->mNumCameras);
printf (" %i lights\n", oScene->mNumLights);
printf (" %i materials\n", oScene->mNumMaterials);
printf (" %i meshes\n", oScene->mNumMeshes);
printf (" %i textures\n", oScene->mNumTextures);
submeshes_cnt.resize(oScene->mNumMeshes);
textures_cnt.resize(oScene->mNumMaterials);
/*.........Initialize the meshes in the scene one by one...........*/
for (unsigned int i = 0 ; i < submeshes_cnt.size() ; i++) {
const aiMesh* paiMesh = oScene->mMeshes[i];
InitMesh(i, paiMesh);
}
// Extract the directory part from the file name
std::string::size_type SlashIndex = Filename.find_last_of("/");
std::string Dir;
if (SlashIndex == std::string::npos) {
Dir = ".";
}
else if (SlashIndex == 0) {
Dir = "/";
}
else {
Dir = Filename.substr(0, SlashIndex);
}
/*.................Initialization of meshes end....................*/
/*.............Initialize the materials.............................*/
for(unsigned int i = 0 ; i < oScene->mNumMaterials ; i++) {
const aiMaterial* pMaterial = oScene->mMaterials[i];
textures_cnt[i] = NULL;
if (pMaterial->GetTextureCount(aiTextureType_DIFFUSE) > 0) {
aiString Path;
if (pMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &Path, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) {
std::string FullPath = Dir + "/" + Path.data;
textures_cnt[i] = new Texture(GL_TEXTURE_2D, FullPath.c_str());
if (!textures_cnt[i]->Load()) {
printf("Error loading texture '%s'\n", FullPath.c_str());
delete textures_cnt[i];
textures_cnt[i] = NULL;
rc = false;
}
else {
printf("Loaded texture '%s'\n", FullPath.c_str());
}
}
}
}
/*.................Initialization of materials end....................*/
}
else {
printf("Error parsing '%s': '%s'\n", Filename.c_str(), Importer.GetErrorString());
}
return rc;
}
void Mesh::InitMesh(unsigned int Index, const aiMesh* paiMesh)
{
submeshes_cnt[Index].MaterialIndex = paiMesh->mMaterialIndex;
std::vector<Vertex> Vertices;
std::vector<unsigned int> Indices;
const aiVector3D Zero3D(0.0f, 0.0f, 0.0f);
for (unsigned int i = 0 ; i < paiMesh->mNumVertices ; i++) {
const aiVector3D* pPos = &(paiMesh->mVertices[i]);
const aiVector3D* pNormal = &(paiMesh->mNormals[i]);
const aiVector3D* pTexCoord = paiMesh->HasTextureCoords(0) ? &(paiMesh->mTextureCoords[0][i]) : &Zero3D;
Vertex v(Vector3f(pPos->x, pPos->y, pPos->z),
Vector2f(pTexCoord->x, pTexCoord->y),
Vector3f(pNormal->x, pNormal->y, pNormal->z));
Vertices.push_back(v);
}
for (unsigned int i = 0 ; i < paiMesh->mNumFaces ; i++) {
const aiFace& Face = paiMesh->mFaces[i];
assert(Face.mNumIndices == 3);
Indices.push_back(Face.mIndices[0]);
Indices.push_back(Face.mIndices[1]);
Indices.push_back(Face.mIndices[2]);
}
submeshes_cnt[Index].Init(Vertices, Indices);
}
void Mesh::Render(){
//enable VAOs for vertices, normals, textures
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
for(unsigned int i = 0 ; i < submeshes_cnt.size() ; i++){
glBindBuffer(GL_ARRAY_BUFFER, submeshes_cnt[i].VB);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)12);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)20);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, submeshes_cnt[i].IB);
const unsigned int MaterialIndex = submeshes_cnt[i].MaterialIndex;
if (MaterialIndex < textures_cnt.size() && textures_cnt[MaterialIndex]) {
textures_cnt[MaterialIndex]->Bind(GL_TEXTURE0);
}
glDrawElements(GL_TRIANGLES, submeshes_cnt[i].NumIndices, GL_UNSIGNED_INT, 0);
}
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
vertex shader:
#version 420
uniform mat4 camera;
uniform mat4 model;
in vec3 vert;
in vec2 vertTexCoord;
out vec2 fragTexCoord;
void main() {
// Pass the tex coord straight through to the fragment shader
fragTexCoord = vertTexCoord;
// Apply all matrix transformations to vert
gl_Position = camera * model * vec4(vert, 1);
}
fragment shader:
#version 420
uniform sampler2D tex;
in vec2 fragTexCoord;
out vec4 finalColor;
void main() {
//note: the texture function was called texture2D in older versions of GLSL
finalColor = texture(tex, fragTexCoord);
}
this is what i get:
before
but when i try to add phong lighting, when I add in attribute for normals to my vertex shader this is what I get:
after
Where is the problem?
Thank you for help.
//edit
shader loading and link
shprogram::shprogram(const std::vector<shader>& shaders) :
_object(0)
{
if(shaders.size() <= 0)
throw std::runtime_error("No shaders were provided to create the program");
//create the program object
_object = glCreateProgram();
if(_object == 0)
throw std::runtime_error("glCreateProgram failed");
//attach all the shaders
for(unsigned i = 0; i < shaders.size(); ++i)
glAttachShader(_object, shaders[i].getObject());
//link the shaders together
glLinkProgram(_object);
//detach all the shaders
for(unsigned i = 0; i < shaders.size(); ++i)
glDetachShader(_object, shaders[i].getObject());
//throw exception if linking failed
GLint status;
glGetProgramiv(_object, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
std::string msg("Program linking failure: ");
GLint infoLogLength;
glGetProgramiv(_object, GL_INFO_LOG_LENGTH, &infoLogLength);
char* strInfoLog = new char[infoLogLength + 1];
glGetProgramInfoLog(_object, infoLogLength, NULL, strInfoLog);
msg += strInfoLog;
delete[] strInfoLog;
glDeleteProgram(_object); _object = 0;
throw std::runtime_error(msg);
}
}
shprogram::~shprogram() {
//might be 0 if ctor fails by throwing exception
if(_object != 0) glDeleteProgram(_object);
}
GLuint shprogram::object() const {
return _object;
}
void shprogram::use() const {
glUseProgram(_object);
}
bool shprogram::isInUse() const {
GLint currentProgram = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram);
return (currentProgram == (GLint)_object);
}
void shprogram::stopUsing() const {
assert(isInUse());
glUseProgram(0);
}
GLint shprogram::attrib(const GLchar* attribName) const {
if(!attribName)
throw std::runtime_error("attribName was NULL");
GLint attrib = glGetAttribLocation(_object, attribName);
if(attrib == -1)
throw std::runtime_error(std::string("Program attribute not found: ") + attribName);
return attrib;
}
GLint shprogram::uniform(const GLchar* uniformName) const {
if(!uniformName)
throw std::runtime_error("uniformName was NULL");
GLint uniform = glGetUniformLocation(_object, uniformName);
if(uniform == -1)
throw std::runtime_error(std::string("Program uniform not found: ") + uniformName);
return uniform;
}
void shprogram::setUniformMatrix2(const GLchar* name, const GLfloat* v, GLsizei count, GLboolean transpose) {
assert(isInUse());
glUniformMatrix2fv(uniform(name), count, transpose, v);
}
void shprogram::setUniformMatrix3(const GLchar* name, const GLfloat* v, GLsizei count, GLboolean transpose) {
assert(isInUse());
glUniformMatrix3fv(uniform(name), count, transpose, v);
}
void shprogram::setUniformMatrix4(const GLchar* name, const GLfloat* v, GLsizei count, GLboolean transpose) {
assert(isInUse());
glUniformMatrix4fv(uniform(name), count, transpose, v);
}
void shprogram::setUniform(const GLchar* name, const glm::mat2& m, GLboolean transpose) {
assert(isInUse());
glUniformMatrix2fv(uniform(name), 1, transpose, glm::value_ptr(m));
}
void shprogram::setUniform(const GLchar* name, const glm::mat3& m, GLboolean transpose) {
assert(isInUse());
glUniformMatrix3fv(uniform(name), 1, transpose, glm::value_ptr(m));
}
void shprogram::setUniform(const GLchar* name, const glm::mat4& m, GLboolean transpose) {
assert(isInUse());
glUniformMatrix4fv(uniform(name), 1, transpose, glm::value_ptr(m));
}
void shprogram::setUniform(const GLchar* uniformName, const glm::vec3& v) {
setUniform3v(uniformName, glm::value_ptr(v));
}
void shprogram::setUniform(const GLchar* uniformName, const glm::vec4& v) {
setUniform4v(uniformName, glm::value_ptr(v));
}
setting up uniforms
//bind shader program
gProgram->use();
//set the "camera" uniform
gProgram->setUniform("camera", gCamera.matrix());
//set the "model" uniform in the vertex shader, based on the gDegreesRotated global
gProgram->setUniform("model", glm::rotate(glm::mat4(), 150.0f ,glm::vec3(0,1,0)));
gProgram->setUniform("light.position", gLight.position);
gProgram->setUniform("light.intensities", gLight.intensities);
You neither explicitely set the attribute locations in the shader source, nor bind them before linking the shader, nor query them. The GL is free to use any valid attribute index for any active input attribute.
You just assume that 0 is position, 1 is texcoord and 2 is normal. It might work by accident for the position+texcoord case, but when you add the normal, the indices might get all mixed up. Either use layout(location=...) (see GL_ARB_explicit_attrib_location for details) in the GLSL code, or use glBindAttribLocation() to specify the mapping you want.
Related
I've already looked all over the internet for the past couple days and I cannot find a fix to this problem. I believe I've got the basic debugging stuff setup yet I get no errors telling me what I've done wrong. This is my first attempt at a renderer, and I'm also trying to use a OOP/DOD (Data oriented design) structure. I think the error is just something small that I am blind to, or I'm completely doing this wrong... please help if you can.
Also I'm using shaders, EBOs, VBOs, linking to a VAO (I think)
Additional information: I'm using SDL + GLAD(opengl 4.6, core)
If you need me to reply with anything else that could help you help me, let me know.
Here is my console output:
screenshot
All I see on the window: screenshot
Here are my files (that might have the error):
vertex shader
#version 460 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
out vec3 ourColor;
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
}
fragment shader
#version 460 core
out vec4 FragColor;
in vec3 ourColor;
void main()
{
FragColor = vec4(ourColor, 1.0f);
}
main.cpp (the window)
#include <loader.h>
using namespace testProgram;
int close(SDL_Window* window, SDL_GLContext glContext) {
IMG_Quit();
SDL_DestroyWindow(window);
SDL_GL_DeleteContext(glContext);
SDL_Quit();
return 0;
}
// Simple debugger callback, will most likely modify in the future.
static void APIENTRY openglCallbackFunction(
GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar* message,
const void* userParam
){
(void)source; (void)type; (void)id;
(void)severity; (void)length; (void)userParam;
fprintf(stderr, "%s\n", message);
if (severity==GL_DEBUG_SEVERITY_HIGH) {
fprintf(stderr, "Aborting...\n");
abort();
}
}
std::vector<GLfloat> vertices {
// positions // colors
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // bottom left
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // top
};
std::vector<GLuint> indices {
0, 1, 2
};
std::vector<int> vertexAttribSizes {
3
};
int main(int argc, char* argv[]) {
// Variables
bool quit = false;
// Initializations
SDL_Init(SDL_INIT_EVERYTHING);
IMG_Init(IMG_INIT_PNG);
setGLAttributes();
// Creating the window
SDL_Window* window = SDL_CreateWindow("test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, winW, winH, flags);
SDL_GLContext glContext = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, glContext);
SDL_Surface* icon = IMG_Load("images/icon.png");
setIcon(window, icon);
// Setting up OpenGL
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
printf("Failed to initialize GLAD\n");
return -1;
}
glViewport(0, 0, winW, winH);
// Enable the debug callback
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(openglCallbackFunction, nullptr);
glDebugMessageControl
(
GL_DONT_CARE,
GL_DONT_CARE,
GL_DONT_CARE,
0, NULL, true
);
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
Renderer::Queue queue;
Renderer::Shader shader;
Renderer::Object object;
shader.Update("CUT OUT THE DIRECTORY FOR THIS POST (because it doesn't matter)", "CUT OUT THE DIRECTORY FOR THIS POST (because it doesn't matter)");
object.Update(vertices, indices, shader.id, "triangle", vertexAttribSizes);
object.Attach(queue);
while(!quit) {
SDL_Event event;
mouseX = event.motion.x;
mouseY = event.motion.y;
while (SDL_PollEvent(&event) != 0) {
inputCollection(event, quit);
}
SDL_SetWindowHitTest(window, hitCallback, 0);
glClearColor(0.085f, 0.085f, 0.085f, 0.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
queue.Render();
SDL_GL_SwapWindow(window);
}
// Closes all processes
object.Detach(queue);
close(window, glContext);
return 0;
}
renderer.h (it is included in loader.h, which is included in main.cpp. loader.h I believe doesn't contain the issue, so I dont want to pollute this post with even more code haha)
// Includes
#include <glad/glad.h>
#include <stdio.h>
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <ctype.h>
#include <fstream>
#include <cstring>
#include <sstream>
// Renderer
namespace Renderer {
class Shader {
private:
// Read the shader's source code.
std::string readFile(const char* _path);
// Check the shader.
bool checkShader(GLuint &_shader);
// Check the program.
bool checkProgram(GLuint &_program);
public:
const char* vertexPath;
const char* fragmentPath;
GLuint id;
void Update(const char* _vertexPath, const char* _fragmentPath); // Update the shader's parameters.
void Use() { glUseProgram(id); } // Use the shader.
};
class Queue {
public:
GLuint VAO;
void listObjects(); // List all objects in the queue (BY NAME, INDEX IS THE ID FOR ALL VALUES/PARAMETERS)
GLuint getObject(std::string objName); // Grab an object from the queue (BY NAME, INDEX IS THE ID FOR ALL VALUES/PARAMETERS) [Returns the id of the object]
bool findObject(std::string objName); // Returns true or false if the object is found.
void Render(); // Render all objects in the queue. (Sets VAOs)
std::vector<std::vector<GLfloat>> vertices;
std::vector<std::vector<int>> indices;
std::vector<std::vector<int>> vertexAttribs;
std::vector<int> shaderProgramIDs;
std::vector<std::string> names;
};
class Object {
public:
GLuint VBO, EBO;
std::vector<GLfloat> vertices;
std::vector<int> indices;
std::vector<int> vertexAttribSizes; // This is the sizes of the vertex attributes, not the values.
GLuint shaderProgramID;
std::string name;
// Set the object's parameters.
void Update(
std::vector<GLfloat> _vertices,
std::vector<GLuint> _indices,
int _shaderProgramID,
std::string _name,
std::vector<int> _vertexAttribSizes
);
void Attach(Queue &queue);
void Detach(Queue &queue);
};
};
renderer.cpp
#include <renderer.h>
using namespace Renderer;
// List all objects in the queue.
void Queue::listObjects() {
for (auto name : names) {
std::cout << name << std::endl;
}
}
// Returns true or false if the object is found.
bool Queue::findObject(std::string objName) {
auto index = std::find(names.begin(), names.end(), objName);
if (index != names.end()) {
return true;
} else {
return false;
}
}
// Returns the object id.
GLuint Queue::getObject(std::string objName) {
auto index = std::find(names.begin(), names.end(), objName);
if (findObject(objName)) {
return index - names.begin();
} else {
return -1;
}
}
// Renders all objects linked to the current queue.
void Queue::Render() {
for (int i = 0; i < names.size(); i++) {
glUseProgram(shaderProgramIDs[i]);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, indices[i].size(), GL_UNSIGNED_INT, 0);
}
}
std::string Shader::readFile(const char* _path) {
std::string content;
std::ifstream fileStream(_path, std::ios::in);
if (!fileStream.is_open()) {
std::cerr << "Could not read file " << _path << ". File does not exist." << std::endl;
return "";
}
std::string line = "";
while (!fileStream.eof()) {
std::getline(fileStream, line);
content.append(line + "\n");
}
fileStream.close();
return content;
}
bool Shader::checkShader(GLuint &_shader) {
GLint success;
glGetShaderiv(_shader, GL_COMPILE_STATUS, &success);
if(!success) {
GLint maxLength = 0;
glGetShaderiv(_shader, GL_INFO_LOG_LENGTH, &maxLength);
std::vector<GLchar> errorLog(maxLength);
glGetShaderInfoLog(_shader, maxLength, &maxLength, &errorLog[0]);
std::cout << "Shader compilation failed: " << std::endl;
std::cout << &errorLog[0] << std::endl;
return false;
}
return true;
}
bool Shader::checkProgram(GLuint &_program) {
GLint success;
glGetProgramiv(_program, GL_LINK_STATUS, &success);
if(!success) {
GLint maxLength = 0;
glGetProgramiv(_program, GL_INFO_LOG_LENGTH, &maxLength);
std::vector<GLchar> errorLog(maxLength);
glGetProgramInfoLog(_program, maxLength, &maxLength, &errorLog[0]);
std::cout << "Program linking failed: " << std::endl;
std::cout << &errorLog[0] << std::endl;
return false;
}
return true;
}
void Shader::Update(
const char* _vertexPath,
const char* _fragmentPath
){
GLuint vertex, fragment;
vertex = glCreateShader(GL_VERTEX_SHADER);
fragment = glCreateShader(GL_FRAGMENT_SHADER);
vertexPath = _vertexPath;
fragmentPath = _fragmentPath;
std::string vertexString = readFile(_vertexPath);
std::string fragmentString = readFile(_fragmentPath);
const char *vertexSource = vertexString.c_str();
const char *fragmentSource = fragmentString.c_str();
glShaderSource(vertex, 1, &vertexSource, NULL);
glCompileShader(vertex);
checkShader(vertex);
glShaderSource(fragment, 1, &fragmentSource, NULL);
glCompileShader(fragment);
checkShader(fragment);
this->id = glCreateProgram();
glAttachShader(this->id, vertex);
glAttachShader(this->id, fragment);
glLinkProgram(this->id);
checkProgram(this->id);
glDeleteShader(vertex);
glDeleteShader(fragment);
}
void Object::Attach(Queue &queue) {
queue.names.push_back(this->name);
GLuint id = queue.getObject(this->name);
queue.shaderProgramIDs.insert(queue.shaderProgramIDs.begin()+id, this->shaderProgramID);
queue.vertices.insert(queue.vertices.begin()+id, this->vertices);
queue.indices.insert(queue.indices.begin()+id, this->indices);
queue.vertexAttribs.insert(queue.vertexAttribs.begin()+id, this->vertexAttribSizes);
glGenBuffers(1, &this->VBO);
glGenBuffers(1, &this->EBO);
glGenVertexArrays(1, &queue.VAO);
glBindVertexArray(queue.VAO);
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(this->vertices), &this->vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(this->indices), &this->indices, GL_STATIC_DRAW);
int stride = 3;
for (int i = 0; i < this->vertexAttribSizes.size(); i++) {
stride+=this->vertexAttribSizes[i];
}
// Positions
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// Other attributes
int totalSize = 3;
for (int i = 0; i < this->vertexAttribSizes.size(); i++) {
glVertexAttribPointer(i+1, this->vertexAttribSizes[i], GL_FLOAT, GL_FALSE, stride * sizeof(float), (void*)(totalSize * sizeof(float)));
glEnableVertexAttribArray(i+1);
totalSize+=this->vertexAttribSizes[i];
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void Object::Detach(Queue &queue) {
GLuint id = queue.getObject(this->name);
queue.names.erase(std::remove(queue.names.begin(), queue.names.end(), this->name), queue.names.end());
queue.vertices.erase(queue.vertices.begin()+id, queue.vertices.begin()+id+this->vertices.size());
queue.indices.erase(queue.indices.begin()+id, queue.indices.begin()+id+this->indices.size());
queue.vertexAttribs.erase(queue.vertexAttribs.begin()+id, queue.vertexAttribs.begin()+id+this->vertexAttribSizes.size());
glDeleteBuffers(1, &this->VBO);
glDeleteBuffers(1, &this->EBO);
}
void Object::Update(
std::vector<GLfloat> _vertices,
std::vector<GLuint> _indices,
int _shaderProgramID,
std::string _name,
std::vector<int> _vertexAttribSizes
){
// TODO: Clear all vectors, also detach and reattach automatically.
for (int i = 0; i < _vertices.size(); i++) {
vertices.push_back(_vertices[i]);
}
for (int i = 0; i < _indices.size(); i++) {
indices.push_back(_indices[i]);
}
for (int i = 0; i < _vertexAttribSizes.size(); i++) {
vertexAttribSizes.push_back(_vertexAttribSizes[i]);
}
shaderProgramID = _shaderProgramID;
name = _name;
}
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(this->vertices), &this->vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(this->indices), &this->indices, GL_STATIC_DRAW);
this->vertices is a std::vector. You are uploading raw pointer values where there should be actual data in the buffer.
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * this->vertices.size(), this->vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * this->indices.size(), this->indices.data(), GL_STATIC_DRAW);
I have encountered an strange issue with ASSIMP mesh load.
Here is the thing, I have a complex model obj file to show a character, this model has 4 child Meshes, and in my code, I encapsulate the Mesh class to get the relevant data(vertices, indices, normals...), When I draw the 4 meshes together(in a for circle), I always get the wrong result:
But if I only load one mesh a time(hard code my assimp load class to do that), and only draw the one mesh, then I can draw the mesh correctly(but this issue only occured when I load the complex model obj file, not with the cube or plane, I can load multiple cube and plane obj files and draw them together):
Here is my ASSIMP load class code, I don't know where my issues are, please help me:
#include "AssetImport.h"
#include "Mesh.h"
#include "Model.h"
#include "ModelMgr.h"
#include "TextureMgr.h"
#include <string>
using namespace Assimp;
AssetImport::AssetImport()
{
}
AssetImport::~AssetImport()
{
}
int AssetImport::LoadModel(const char *path)
{
Importer import;
const aiScene *scene = import.ReadFile(path, aiProcess_Triangulate
| aiProcess_FlipUVs
| aiProcess_CalcTangentSpace
| aiProcess_GenNormals);
if (!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
{
return -1;
}
m_prePath = path;
auto index = m_prePath.lastIndexOf("/");
if (-1 != index) {
m_prePath = m_prePath.left(index + 1);
}
HandleChildNode(scene, scene->mRootNode);
return 0;
}
int AssetImport::HandleChildNode(const aiScene *scene, aiNode *node)
{
unsigned int numChild = node->mNumChildren;
for (unsigned int a = 0; a < 1; ++a)
{
**auto child = node->mChildren[3];// here is the hard code**
if (child->mNumChildren > 0) {
HandleChildNode(scene, child);
}
if (0) {
continue;
}
// create a new model for a child
Model *mod = ModelMgr::Instance().CreateNewModel();
mod->SetModelName(child->mName.C_Str());
auto numMeshes = child->mNumMeshes;
for (unsigned int i = 0; i < numMeshes; ++i)
{
// create new sub mesh, then you can add it to one model
Mesh *m = new Mesh;
auto mesh = scene->mMeshes[child->mMeshes[i]];
//handle the mesh's materials
auto mat = scene->mMaterials[mesh->mMaterialIndex];
HandleMeshMaterial(mat, m);
// add vertex info
for (unsigned int j = 0; j < mesh->mNumVertices; ++j)
{
auto vert = mesh->mVertices[j];
Mesh::VertInfo info;
info.pos = QVector3D(mesh->mVertices[j].x, mesh->mVertices[j].y, mesh->mVertices[j].z);
info.color = QVector4D(0.8f, 0.8f, 0.8f, 1);
info.normal = QVector3D(mesh->mNormals[j].x, mesh->mNormals[j].y, mesh->mNormals[j].z);
if (nullptr != mesh->mTangents)
{
info.tangent = QVector4D(mesh->mTangents[j].x, mesh->mTangents[j].y, mesh->mTangents[j].z, 0);
}
if (nullptr != mesh->mBitangents)
{
info.bitangent = QVector4D(mesh->mBitangents[j].x, mesh->mBitangents[j].y, mesh->mBitangents[j].z, 0);
}
if (nullptr != mesh->mTextureCoords[0])
{
info.uv1 = QVector2D(mesh->mTextureCoords[0][j].x, mesh->mTextureCoords[0][j].y);
}
m->AddVertInfo(info);
}
// add index info
for (unsigned int j = 0; j < mesh->mNumFaces; ++j)
{
auto face = mesh->mFaces[j];
for (unsigned int k = 0; k < face.mNumIndices; ++k)
{
m->AddIndex(face.mIndices[k]);
}
}
m->BindBuffer();
mod->AddMesh(m);
}
}
return 0;
}
int AssetImport::HandleMeshMaterial(aiMaterial *mat, Mesh *mesh)
{
auto diffuseTexCount = mat->GetTextureCount(aiTextureType_DIFFUSE);
auto specularTexCount = mat->GetTextureCount(aiTextureType_SPECULAR);
for (unsigned int i = 0; i < diffuseTexCount; ++i)
{
aiString path;
mat->GetTexture(aiTextureType_DIFFUSE, i, &path);
QString strPath = path.C_Str();
strPath.replace("/", "\\");
auto index = strPath.lastIndexOf("\\");
if (-1 != index) {
strPath = strPath.right(strPath.length() - index - 1);
strPath.prepend(m_prePath);
}
auto texID = TextureMgr::Instance().LoadTexture(strPath.toLocal8Bit());
mesh->SetDiffuseTexID(texID);
}
for (unsigned int i = 0; i < specularTexCount; ++i)
{
aiString strPath;
mat->GetTexture(aiTextureType_SPECULAR, i, &strPath);
}
return 0;
}
Here is my Mesh Class(with Draw Function):
#include "Mesh.h"
#include "TextureMgr.h"
#include "ShaderHelper.h"
#include "PreDef.h"
#include "QMatrix4x4"
Mesh::Mesh()
:m_vao(0), m_vbo(0), m_vaeo(0), m_instanceBufferId(0), m_tbo1(0)
, m_drawType(Triangle)
, m_skyboxTexID(0), m_projTexID(0), m_normalmapTexID(0)
, m_diffuseTex1ID(0)
{
initializeOpenGLFunctions();
m_normalmapTexID = TextureMgr::Instance().LoadTexture("./models/brickwall_normal.jpg");
}
Mesh::~Mesh()
{
}
void Mesh::AddVertex(QVector3D vert)
{
m_vertices.push_back(vert.x());
m_vertices.push_back(vert.y());
m_vertices.push_back(vert.z());
}
const float* Mesh::GetVertices() const
{
return m_vertices.data();
}
int Mesh::GetVerticesMemSize() const
{
return m_vertices.size() * sizeof(float);
}
int Mesh::GetVerticesNum() const
{
return m_vertices.size() / 3;
}
void Mesh::AddColor(QVector4D color)
{
m_colors.push_back(color.x());
m_colors.push_back(color.y());
m_colors.push_back(color.z());
m_colors.push_back(color.w());
}
const float* Mesh::GetColors() const
{
return m_colors.data();
}
int Mesh::GetColorsMemSize() const
{
return m_colors.size() * sizeof(float);
}
void Mesh::AddNormal(QVector3D normal)
{
m_normals.append(normal.x());
m_normals.append(normal.y());
m_normals.append(normal.z());
}
const float* Mesh::GetNormals() const
{
return m_normals.data();
}
int Mesh::GetNormalsMemSize() const
{
return m_normals.size() * sizeof(float);
}
int Mesh::GetNormalsNum() const
{
return m_normals.size() / 3;
}
void Mesh::AddTangent(QVector4D tangent)
{
m_tangents.append(tangent.x());
m_tangents.append(tangent.y());
m_tangents.append(tangent.z());
m_tangents.append(tangent.w());
}
const float* Mesh::GetTangents() const
{
return m_tangents.data();
}
int Mesh::GetTangentsMemSize() const
{
return m_tangents.size() * sizeof(float);
}
int Mesh::GetTangentsNum() const
{
return m_tangents.size() / 4;
}
void Mesh::AddBitangent(QVector4D binormal)
{
m_binormals.append(binormal.x());
m_binormals.append(binormal.y());
m_binormals.append(binormal.z());
m_binormals.append(binormal.w());
}
const float* Mesh::GetBitangents() const
{
return m_binormals.data();
}
int Mesh::GetBitangentsMemSize() const
{
return m_binormals.size() * sizeof(float);
}
int Mesh::GetBitangentsNum() const
{
return m_binormals.size() / 4;
}
void Mesh::AddUv1(QVector2D uv)
{
m_uvs1.append(uv.x());
m_uvs1.append(uv.y());
}
const float* Mesh::GetUvs1() const
{
return m_uvs1.data();
}
int Mesh::GetUvs1MemSize() const
{
return m_uvs1.size() * sizeof(float);
}
int Mesh::GetUvs1Num() const
{
return m_uvs1.size() / 2;
}
void Mesh::AddVertInfo(const VertInfo &info)
{
m_vertInfoVec.push_back(info);
AddVertex(info.pos);
AddColor(info.color);
AddNormal(info.normal);
AddTangent(info.tangent);
AddBitangent(info.bitangent);
AddUv1(info.uv1);
}
void Mesh::BindBuffer()
{
// init the shaders first
ShaderHelper::Instance();
BindVertexRelevantBuffer();
}
void Mesh::BindVertexRelevantBuffer()
{
const GLuint *vertex_indices = GetIndices();
const GLfloat *vertex_positions = GetVertices();
const GLfloat *vertex_uvs = GetUvs1();
const GLfloat *vertex_tangents = GetTangents();
const GLfloat *vertex_bitangents = GetBitangents();
const GLfloat *vertex_normals = GetNormals();
// set element array(index array) buffer
glGenBuffers(1, &m_vaeo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vaeo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, GetIndicesMemSize(), vertex_indices, GL_STATIC_DRAW);
// set vertex array object
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
glVertexArrayElementBuffer(m_vao, m_vaeo);
glGenBuffers(1, &m_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, GetVerticesMemSize() + GetUvs1MemSize()
+ GetTangentsMemSize() + GetBitangentsMemSize() + GetNormalsMemSize() /*+ 16 * sizeof(GLfloat) * 10*/, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, GetVerticesMemSize(), vertex_positions);
glBufferSubData(GL_ARRAY_BUFFER, GetVerticesMemSize(), GetUvs1MemSize(), vertex_uvs);
glBufferSubData(GL_ARRAY_BUFFER, GetVerticesMemSize() + GetUvs1MemSize(), GetTangentsMemSize(), vertex_tangents);
glBufferSubData(GL_ARRAY_BUFFER, GetVerticesMemSize() + GetUvs1MemSize() + GetTangentsMemSize(),
GetBitangentsMemSize(), vertex_bitangents);
glBufferSubData(GL_ARRAY_BUFFER, GetVerticesMemSize() + GetUvs1MemSize()
+ GetTangentsMemSize() + GetBitangentsMemSize(),
GetNormalsMemSize(), vertex_normals);
GLint positionLoc = 0;
GLint uvLoc = 1;
GLint tangentLoc = 2;
GLint bitangentLoc = 3;
GLint normalLoc = 4;
GLint modelMatLoc = -1;
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, (0));
glEnableVertexAttribArray(positionLoc);
glVertexAttribPointer(uvLoc, 2, GL_FLOAT, GL_FALSE, 0, (void*)(GetVerticesMemSize()));
glEnableVertexAttribArray(uvLoc);
glVertexAttribPointer(tangentLoc, 3, GL_FLOAT, GL_FALSE, 0, (void*)(GetVerticesMemSize() + GetUvs1MemSize()));
glEnableVertexAttribArray(tangentLoc);
glVertexAttribPointer(bitangentLoc, 3, GL_FLOAT, GL_FALSE, 0,
(void*)(GetVerticesMemSize() + GetUvs1MemSize() + GetTangentsMemSize()));
glEnableVertexAttribArray(bitangentLoc);
glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, 0,
(void*)(GetVerticesMemSize() + GetUvs1MemSize() + GetTangentsMemSize() + GetBitangentsMemSize()));
glEnableVertexAttribArray(normalLoc);
// multi instances model matrix
if (-1 != modelMatLoc) {
// generate the new buffer for instances
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &m_instanceBufferId);
glBindBuffer(GL_ARRAY_BUFFER, m_instanceBufferId);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16 * 10, nullptr, GL_STATIC_DRAW);
// m_instanceBufferId = m_vbo;
// mat4 type take space of 4 vec4, so we should circle 4 times
for (int i = 0; i < 4; ++i)
{
glVertexAttribPointer(modelMatLoc + i, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 16,
(void*)(/*GetVerticesMemSize() + GetUvs1MemSize() + GetTangentsMemSize() + GetBitangentsMemSize() + GetNormalsMemSize() +*/ sizeof(GLfloat) * 4 * i));
glEnableVertexAttribArray(modelMatLoc + i);
// implement the multi instances
glVertexAttribDivisor(modelMatLoc + i, 1);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// sampler buffer bind
glGenBuffers(1, &m_tbo1);
glBindBuffer(GL_TEXTURE_BUFFER, m_tbo1);
glBufferData(GL_TEXTURE_BUFFER, sizeof(GLfloat) * 16 * 10, nullptr, GL_STATIC_DRAW);
GLuint tex1;
glCreateTextures(GL_TEXTURE_BUFFER, 1, &tex1);
glTextureBuffer(tex1, GL_RGBA32F, m_tbo1);
glBindTextureUnit(0, m_tbo1);
glBindBuffer(GL_TEXTURE_BUFFER, 0);
}
GLuint Mesh::GetVao()
{
return m_vao;
}
void Mesh::AddIndex(unsigned int index)
{
m_indices.push_back(index);
}
void Mesh::AddIndexVec(QVector<unsigned int> &indexVec)
{
m_indices = indexVec;
}
const unsigned int* Mesh::GetIndices() const
{
return m_indices.data();
}
int Mesh::GetIndicesMemSize() const
{
return m_indices.size() * sizeof(unsigned int);
}
int Mesh::GetIndicesNum() const
{
return m_indices.size();
}
GLuint Mesh::GetMultiInstanceModelMatrixOffset() const
{
return (GetVerticesMemSize() + GetUvs1MemSize() + GetTangentsMemSize()
+ GetBitangentsMemSize() + GetNormalsMemSize());
}
GLuint Mesh::GetInstancesBufferId() const
{
return m_instanceBufferId;
}
void Mesh::SetDiffuseTexID(GLuint id)
{
m_diffuseTex1ID = id;
}
void Mesh::AddSpecularTexture(GLuint id)
{
m_specularTex1ID = id;
}
GLuint Mesh::GetTextureBuffer1() const
{
return m_tbo1;
}
void Mesh::InitSkybox()
{
m_skyboxTexID = TextureMgr::Instance().LoadTexture("skybox");
}
void Mesh::InitProjTex()
{
m_projTexID = TextureMgr::Instance().LoadTexture("./textures/proj.jpg");
}
void Mesh::SetDrawType(eDrawType type)
{
m_drawType = type;
}
void Mesh::Draw(QMatrix4x4 matVP, QMatrix4x4 matModel, QVector3D camPos, QMatrix4x4 matProj, QMatrix4x4 matView,
QMatrix4x4 matOrtho)
{
if (0 != m_diffuseTex1ID)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_diffuseTex1ID);
}
if (0 != m_normalmapTexID)
{
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_normalmapTexID);
}
if (0 != m_skyboxTexID) {
glBindTexture(GL_TEXTURE_CUBE_MAP, m_skyboxTexID);
matModel.translate(camPos);
matModel.scale(10000);
glCullFace(GL_FRONT);
glDepthMask(0);
ShaderHelper::Instance().SetShaderType(ShaderHelper::Skybox);
}
else if (0 != m_projTexID) {
glActiveTexture(GL_TEXTURE10);
glBindTexture(GL_TEXTURE_2D, m_projTexID);
ShaderHelper::Instance().SetShaderType(ShaderHelper::Decal);
}
glBindVertexArray(GetVao());
matVP = matVP * matModel;
ShaderHelper::Instance().SetMVPMatrix(matVP);
ShaderHelper::Instance().SetWorldMatrix(matModel);
ShaderHelper::Instance().SetCamWorldPos(camPos);
ShaderHelper::Instance().SetProjMat(matProj);
ShaderHelper::Instance().SetViewMat(matView);
ShaderHelper::Instance().SetOrthoMat(matOrtho);
// Draw element(with indices)
if (Triangle == m_drawType)
{
glDrawElements(GL_TRIANGLES, GetIndicesNum(), GL_UNSIGNED_INT, 0);
}
else if (Point == m_drawType)
{
glDrawArrays(GL_POINTS, 0, GetVerticesNum());
}
if (0 != m_skyboxTexID) {
glCullFace(GL_BACK);
glDepthMask(1);
}
glBindVertexArray(0);
}
Here is the main draw function:
void OpenWidget::paintGL()
{
static int i = 0;
if (0 == i){
++i;
CreateOffScreenFrameBufferTexture();
}
glBindFramebuffer(GL_FRAMEBUFFER, m_fb);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
return;
}
paintClearAndReset();
QMatrix4x4 matVP = m_cam->GetVPMatrix();
QMatrix4x4 matProj = m_cam->GetProjectionMatrix();
QMatrix4x4 matOrtho = m_cam->GetOrthographicMatrix();
QMatrix4x4 matView = m_cam->GetViewMatrix();
QVector3D camPos = m_cam->GetCamPos().toVector3D();
auto modelNum = ModelMgr::Instance().GetModelNum();
for (unsigned int i = 0; i < modelNum; ++i)
{
Model *mod = ModelMgr::Instance().GetModel(i);
SwitchShader(ShaderHelper::Default);
QMatrix4x4 matModel = mod->GetWorldMat();
mod->Draw(matVP, matModel, camPos, matProj, matView, matOrtho);
if (modelNum - 1 == i) {
Model *pBox = ModelMgr::Instance().FindModelByName("Box001");
// SwitchShader(ShaderHelper::PlaneClip);
// glFrontFace(GL_CW);
// glEnable(GL_CLIP_PLANE0);
// glStencilMask(0xff);
// glStencilFunc(GL_ALWAYS, 1, 0xff);
// glColorMask(0, 0, 0, 0);
//
// pBox->Draw(matVP, pBox->GetWorldMat(), camPos);
// glFrontFace(GL_CCW);
// glDisable(GL_CLIP_PLANE0);
// glColorMask(1, 1, 1, 1);
//
// glStencilMask(0);
// glStencilFunc(GL_EQUAL, 1, 0xff);
// SwitchShader(ShaderHelper::PureColor);
// pBox->Draw(matVP, pBox->GetWorldMat(), camPos);
}
}
//-----------------------------------------------
// test the frame buffer
DrawOffScreenTexture();
}
Finally, I found where the problem is, I give up on this issue for a while, but today, I encountered another problem with my skybox(cubemap), I can only draw my skybox correctly at the last of my draw queue, otherwise the cubemap only show one face, so, no shortcut for me anymore, I decided to resolve this issue, after many hours debug, I found this issue caused by the create sequence of VAO and ElementBuffer, here is the wrong code:
// set element array(index array) buffer
glGenBuffers(1, &m_vaeo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vaeo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, GetIndicesMemSize(), vertex_indices, GL_STATIC_DRAW);
// set vertex array object
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
glVertexArrayElementBuffer(m_vao, m_vaeo);
you should always create and bind VAO first:
// set vertex array object
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
// set element array(index array) buffer
glGenBuffers(1, &m_vaeo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vaeo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, GetIndicesMemSize(), vertex_indices, GL_STATIC_DRAW);
glVertexArrayElementBuffer(m_vao, m_vaeo);
But, after all, I think it's so implicit, because of you can bind the element to vao after you create the element buffer, it seems like when you gen the element buffer it's nothing to do with the vao, what I can do, only be careful next time.
I used opengl in qt5 on ubuntu 18.4, i wanted to load a simple cube(store in an .obj file) and render it. I followed the tutorial here http://www.opengl-tutorial.org/beginners-tutorials/tutorial-7-model-loading/, but i got a black window at last. Here is my code, thank you very much:
myopengl.hpp
class MyOpenGL : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
MyOpenGL(const QString& obj_file);
protected:
virtual void initializeGL() Q_DECL_OVERRIDE;
virtual void paintGL() Q_DECL_OVERRIDE;
virtual void resizeGL(int width, int height) Q_DECL_OVERRIDE;
void CheckGLerror();
private:
std::vector< glm::vec3 > vertices;
std::vector< glm::vec2 > uvs;
std::vector< glm::vec3 > normals;
/* Render data */
GLuint VAO, VBO, EBO;
};
myopengl.cpp
MyOpenGL ::MyOpenGL (const QString& obj_file) :
VAO(0),
VBO(0),
EBO(0)
{
std::vector< unsigned int > vertex_indices, uv_indices, normal_indices;
std::vector< glm::vec3 > temp_vertices;
std::vector< glm::vec2 > temp_uvs;
std::vector< glm::vec3 > temp_normals;
//parse obj file
QFile file(obj_file);
file.open(QFile::ReadOnly | QFile::Text);
QTextStream in(&file);
while(!in.atEnd()) {
QString line = in.readLine();
QStringList list = line.replace(",","").split(' ', QString::SkipEmptyParts);
if (list.size() >= 3) {
if (list.at(0) == "v") { //veertex
bool ok1, ok2, ok3;
float v1 = list.at(1).toFloat(&ok1);
float v2 = list.at(2).toFloat(&ok2);
float v3 = list.at(3).toFloat(&ok3);
if (ok1 && ok2 && ok3) {
glm::vec3 vertex;
vertex.x = v1;
vertex.y = v2;
vertex.z = v3;
temp_vertices.push_back(vertex);
}
} else if (list.at(0) == "vn") {
bool ok1, ok2, ok3;
float v1 = list.at(1).toFloat(&ok1);
float v2 = list.at(2).toFloat(&ok2);
float v3 = list.at(3).toFloat(&ok3);
if (ok1 && ok2 && ok3) {
glm::vec3 normal;
normal.x = v1;
normal.y = v2;
normal.z = v3;
temp_normals.push_back(normal);
}
} else if (list.at(0) == "vt") {
bool ok1, ok2;
float v1 = list.at(1).toFloat(&ok1);
float v2 = list.at(2).toFloat(&ok2);
if (ok1 && ok2) {
glm::vec2 uv;
uv.x = v1;
uv.y = v2;
temp_uvs.push_back(uv);
}
} else if (list.at(0) == "f") {
bool f_ok1, f_ok2, f_ok3;
bool t_ok1, t_ok2, t_ok3;
bool n_ok1, n_ok2, n_ok3;
unsigned int v_index1, v_index2, v_index3;
unsigned int t_index1, t_index2, t_index3;
unsigned int n_index1, n_index2, n_index3;
QStringList f_list = list.at(1).split('/');
if (f_list.size() >= 3) {
v_index1 = f_list.at(0).toUInt(&f_ok1);
if (f_ok1) {
v_index1 -= 1;
}
t_index1 = f_list.at(1).toUInt(&t_ok1);
if (t_ok1) {
t_index1 -= 1;
}
n_index1 = f_list.at(2).toUInt(&n_ok1);
if (n_ok1) {
n_index1 -= 1;
}
}
f_list = list.at(2).split('/');
if (f_list.size() >= 3) {
v_index2 = f_list.at(0).toUInt(&f_ok2);
if (f_ok2) {
v_index2 -= 1;
}
t_index2 = f_list.at(1).toUInt(&t_ok2);
if (t_ok2) {
t_index2 -= 1;
}
n_index2 = f_list.at(2).toUInt(&n_ok2);
if (n_ok2) {
n_index2 -= 1;
}
}
f_list = list.at(3).split('/');
if (f_list.size() >= 3) {
v_index3 = f_list.at(0).toUInt(&f_ok3);
if (f_ok3) {
v_index3 -= 1;
}
t_index3 = f_list.at(1).toUInt(&t_ok3);
if (t_ok3) {
t_index3 -= 1;
}
n_index3 = f_list.at(2).toUInt(&n_ok3);
if (n_ok3) {
n_index3 -= 1;
}
}
if (f_ok1 && f_ok2 && f_ok3 && n_ok1 && n_ok2 && n_ok3
&& t_ok1 && t_ok2 && t_ok3) {
vertex_indices.push_back(v_index1);
vertex_indices.push_back(v_index2);
vertex_indices.push_back(v_index3);
uv_indices.push_back(t_index1);
uv_indices.push_back(t_index2);
uv_indices.push_back(t_index3);
normal_indices.push_back(n_index1);
normal_indices.push_back(n_index2);
normal_indices.push_back(n_index3);
}
}
}
}
file.close();
for (unsigned int i = 0; i < vertex_indices.size(); ++i) {
glm::vec3 vertex = temp_vertices.at(vertex_indices.at(i));
vertices.push_back(vertex);
}
for (unsigned int i = 0; i < uv_indices.size(); ++i) {
glm::vec2 uv = temp_uvs.at(uv_indices.at(i));
uvs.push_back(uv);
}
for (unsigned int i = 0; i < normal_indices.size(); ++i) {
glm::vec3 normal = temp_normals.at(normal_indices.at(i));
normals.push_back(normal);
}
}
void MyOpenGL::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0.0,0.0,0.0,1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glEnable(GL_LIGHT0);
glewExperimental = GL_TRUE;
GLenum status = glewInit();
if (status != GLEW_OK)
{
glfwTerminate();
std::system("pause");
return;
}
glGenVertexArrays(1, &this->VAO);
glBindVertexArray(this->VAO);
glGenBuffers(1, &this->VBO);
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(glm::vec3), &this->vertices[0], GL_STATIC_DRAW);
glBindVertexArray(0);
}
void MyOpenGL::resizeGL(int width, int height)
{
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-width/2,width/2,-height/2,height/2,-1,1);
glMatrixMode(GL_MODELVIEW);
}
void MyOpenGL::paintGL()
{
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
glDisableVertexAttribArray(0);
}
You have to define an array of generic vertex attribute data by glVertexAttribPointer.
Note, the vertex buffer object is only a data store for the vertex data. But you have to specify how to "use" them. The specification and states of the vertex attributes are stored in the vertex array object. It is sufficient to bind the vertex array object when you want to draw the mesh:
specification:
glBindVertexArray(this->VAO);
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
draw:
glBindVertexArray(this->VAO);
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
glBindVertexArray(0);
Extension:
Is it essential to use vertex shader and fragment shader? I didn't use them for current.
If you don't have a shader, then you have to use the Fixed Function Pipeline and to define the fixed function attributes, by glVertexPointer respectively glEnableClientState( GL_VERTEX_ARRAY ).
But since you only use one attribute, the vertex coordiante with attribute index 0, you can still define the attribute by glVertexAttribPointer and glEnableVertexAttribArray.
See What are the Attribute locations for fixed function pipeline in OpenGL 4.0++ core profile?
Note, in this case you have to use a compatibility OpenGL Context.
I have been working to create a circle based on a shader that changes color periodically. My code prints out a blank window. I decided to set-up some methods to check for errors using GL_LINK_STATUS and it seems that my code is encountering an issue linking the program and the shader.
I did some research into similar issues that people have had but they don't seem to have similar causes.
I have posted my shader code, the driver program(named Priomary) and the Shader Program below.
Vertex Shader
#version 330 core
layout (location = 0) in vec3 pos;
uniform vec2 posOffset;
void main()
{
gl_Position = vec4(pos.x + posOffset.x, pos.y + posOffset.y, pos.z, 1.0);
}
Fragment Shader
#version 330 core
uniform vec4 vertColor;
out vec4 frag_color;
void main()
{
frag_color = vertColor;
}
Shader Program
#include "ShaderProgram.h"
#include <fstream>
#include <iostream>
#include <sstream>
ShaderProgram::ShaderProgram()
: mProgram(0){
}
ShaderProgram::~ShaderProgram()
{
glDeleteProgram(mProgram);
}
bool ShaderProgram::assignShaders(const char* vertFileName, const char* fragFileName)
{
//Shaders output objects called programs that define their relationship and lead to .exe functionality
//assigning pointer to the shader
string vsString = readFile(vertFileName);
string fsString = readFile(fragFileName);
// c_str returns a const char* that points to a null-terminated string (i.e. a C-style string). It is useful when you want to pass the "contents"ยน of an std::string to a function that expects to work with a C-style string.
const GLchar* vsSourcePtr = vsString.c_str();
const GLchar* fsSourcePtr = fsString.c_str();
//creating vertex shader(vs) shader object
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
//assigning shader source using address. Replaces the source code in a shader object //#arg (shader, count Strings, pointer to const File ,size)
glShaderSource(vs, 1, &vsSourcePtr, NULL);
glShaderSource(fs, 1, &fsSourcePtr, NULL);
glCompileShader(vs);
testShaderCompile(vs);
glCompileShader(fs);
testShaderCompile(fs);
//createProgram returns GLUint which is basically an unsigned int... we will use This Handler to create a program object
mProgram = glCreateProgram();
if (mProgram == 0)
{
std::cerr << "Shader cannot be created" << std::endl;
return false;
}
//assign the program object(mProgram) to the Shader
glAttachShader(mProgram, vs);
glAttachShader(mProgram, fs);
//this method accepts a GLuint "program" . If its an object of type GL_VERTEX_SHADER,
//itll create a .exe that runs on the programmable vertex processor. same goes for geometric and fragment shaders if they were included
//it will also bind all user defined uniform variables and attributes to the program
//The program can then be made part of a defined state by calling useProgram
glLinkProgram(mProgram);
testProgramCompile();
//cleaning up the elements we already used
glDeleteShader(vs);
glDeleteShader(fs);
//clear the identifier lookup map(in this case, there's only one)
mUniformIdentifiers.clear();
return true;
}//end main
//Read the shaderFile. strngstream for reading multiple lines
string ShaderProgram:: readFile(const string& filename) {
std::stringstream strgstream;
std::ifstream file;
try
{
file.open(filename, std::ios::in);
if (!file.fail())
{
strgstream << file.rdbuf();
}
file.close();
}
catch (std::exception ex)
{
std::cerr << "Error: File or File Name Issues" << std::endl;
}
return strgstream.str();
}
//use the Program Object we created in this current state(color)
void ShaderProgram::use()
{
//check if it is not null
if (mProgram > 0)
glUseProgram(mProgram);
}
void ShaderProgram::dumpShaderLog(bool is_shader, GLuint obj)
{
int maxlen = 0;
if (is_shader) glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &maxlen);
else glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &maxlen);
if (maxlen > 0)
{
char *log = new char[maxlen];
int len;
if (is_shader) glGetShaderInfoLog(obj, maxlen, &len, log);
else glGetProgramInfoLog(obj, maxlen, &len, log);
if (len > 0 && log[0] != '\0')
fprintf(stderr, "%s\n", log);
delete[] log;
}
}
void ShaderProgram::testProgramCompile() {
int status = 0;
GLuint program = mProgram;
// ///CHECKING GL_LINK_STATUS to see if Program Link was successul. Link Status will return GL_TRUE if it was
glGetProgramiv( mProgram, GL_LINK_STATUS, &status); //requesting the status
if (status == GL_FALSE)
{
GLint length = 0;
glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &length);
string errorLog(length, ' '); // Resize and fill with space character
glGetProgramInfoLog(mProgram, length, &length, &errorLog[0]);
dumpShaderLog(false, mProgram);
std::cerr << "Linking Error with Program and Shader" << std::endl;
}
}
void ShaderProgram :: testShaderCompile(GLuint shader) {
int status = 0;
// ///CHECKING GL_LINK_STATUS to see if Program Link was successul. Link Status will return GL_TRUE if it was
glGetShaderiv (shader, GL_COMPILE_STATUS, &status);//requesting the status
if (status == GL_FALSE)
{
GLint length = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
string errorLog(length, ' '); // Resize and fill with space character
glGetShaderInfoLog(shader, length, &length, &errorLog[0]);
std::cerr << "SHADER COMPILE TEST ERROR " << std::endl;
}
}
////GETTERS AND SETTERS
GLuint ShaderProgram::getProgram() const
{
return mProgram;
}
void ShaderProgram::setUniform(const GLchar* name, const glm::vec2& v)
{
GLint address = getUniformIdentifier(name);
glUniform2f(address, v.x, v.y);
}
void ShaderProgram::setUniform(const GLchar* name, const glm::vec3& v)
{
GLint address = getUniformIdentifier(name);
glUniform3f(address, v.x, v.y, v.z);
}
void ShaderProgram:: setUniform(const GLchar* name, const glm::vec4& v) {
GLint address = getUniformIdentifier(name);
glUniform4f(address, v.x, v.y, v.z, v.w);
}
//Maybe need to switch places with setUniform
GLint ShaderProgram :: getUniformIdentifier(const GLchar* name) {
std::map<std::string, GLint>::iterator it;
it = mUniformIdentifiers.find(name);
//std::map<std::string, GLint>
// Only need to query the shader program IF it doesn't already exist.
if (it == mUniformIdentifiers.end())
{
// Find it and add it to the map
mUniformIdentifiers[name] = glGetUniformLocation(mProgram, name);
}
// Return it
return mUniformIdentifiers[name];
}
Primary Program
#include <iostream>
#include <vector>
#include <sstream>
#define GLEW_STATIC
//always GLEW before GLFW
#include "GL/glew.h"
#include "GLFW/glfw3.h"
#include "glm/glm.hpp"
#include "ShaderProgram.h"
#ifndef M_PI
# define M_PI 3.141592653
#endif
/////gLOBAL
GLFWwindow* w = NULL;
const int wWidth = 800;
const int wHeight = 600;
std::vector<glm::vec3> vertices;
std::vector<unsigned int> indices;
GLfloat Radius = 6;
GLfloat Stacks = 5;
GLfloat Slices = 5;
void key_callback(GLFWwindow *w, int key, int scancode, int action, int mode);
//update colors based on average framerate
void averageFPS(GLFWwindow* window);
//screen resizing
void glfw_onFramebufferSize(GLFWwindow* window, int width, int height);
bool initOpenGL();
static void error(int error, const char *desc)
{
fputs(desc, stderr);
}
//setting up values for keys
int main() {
//pointing to GLFW window
//GLFWwindow *w; //may want to initialize as null outside before main
if (!initOpenGL()) ///5IMPR
{
// An error occured
std::cerr << "GLFW not initialized" << std::endl;
return -1;
}
glfwSetErrorCallback(error);
///TEMP CIRCLE VERTICES
// Calc The Vertices
for (int i = 0; i <= Stacks; ++i) {
float V = i / (float)Stacks;
float phi = V * M_PI; //change to glm:: pi
// Loop Through Slices
for (int j = 0; j <= Slices; ++j) {
float U = j / (float)Slices;
float theta = U * (M_PI * 2);
// Calc The Vertex Positions
float x = cosf(theta) * sinf(phi);
float y = cosf(phi);
float z = sinf(theta) * sinf(phi);
// Push Back Vertex Data //push_back is a standard vector function which adds a parameter to the end of the vector
//std::vector<glm::vec3> vertices; //moved to global variables at top
vertices.push_back(glm::vec3(x, y, z) * Radius);
}
}
// Calc The Index Positions
for (int i = 0; i < Slices * Stacks + Slices; ++i) {
indices.push_back(i);
indices.push_back(i + Slices + 1);
indices.push_back(i + Slices);
indices.push_back(i + Slices + 1);
indices.push_back(i);
indices.push_back(i + 1);
}
////TEMP CIRCLE VERTICES END
// 2. Set up buffers on the GPU
GLuint vbo, ibo, vao; ///5IMPROVEdown
glGenBuffers(1, &vbo); // Generate an empty vertex buffer on the GPU
glBindBuffer(GL_ARRAY_BUFFER, vbo); // "bind" or set as the current buffer we are working with
//3rd argument of glBufferData needs a pointer to the std:vector data...A pointer to the data can be obtained by vertices.data() accrding to std: ector docs...
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), vertices.data(), GL_STATIC_DRAW);// copy the data from CPU to GPU
glGenVertexArrays(1, &vao); // Tell OpenGL to create new Vertex Array Object
glBindVertexArray(vao); // Make it the current one
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL); // Define a layout for the first vertex buffer "0"
glEnableVertexAttribArray(0); // Enable the first attribute or attribute "0"
// Set up index buffer
glGenBuffers(1, &ibo); // Create buffer space on the GPU for the index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
// glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); - REPLACED
glBindVertexArray(0); // unbind to make sure other code doesn't change it
ShaderProgram shaderProgram; ///5ADD
shaderProgram.assignShaders("shaders\\ColorShader.vert", "shaders\\ColorShader.frag"); ///5ADD
////////SETUP RENDERING
while (!glfwWindowShouldClose(w))
{
averageFPS(w);
//process events
glfwPollEvents();
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT);
shaderProgram.use(); ///5ADD
GLfloat time = (GLfloat)glfwGetTime(); ///5ADD
GLfloat blueSetting = (sin(time) / 2) + 0.5f; ///5ADD
glm::vec2 pos;
pos.x = sin(time) / 2;
pos.y = cos(time) / 2;
shaderProgram.setUniform("vertColor", glm::vec4(0.0f, 0.0f, blueSetting, 1.0f)); ///5ADD
shaderProgram.setUniform("posOffset", pos);
glBindVertexArray(vao);
//og for quad
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//glDrawElements(GL_LINE_LOOP, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
// Swap buffers and look for events
glfwSwapBuffers(w);
}
//clean up
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ibo);
//glfwDestroyWindow(w);
glfwTerminate();
return 0;
}
///////START Initializing glfw glew etc
bool initOpenGL(){
//this method will exit on these conditions
GLuint error = glfwInit();
if (!error)
return false;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
w = glfwCreateWindow(wWidth, wHeight, "Exercise", NULL, NULL);
if (w== NULL)
{
std::cerr << "glfw window not created" << std::endl;
glfwTerminate();
return false;
}
//update context
glfwMakeContextCurrent(w);
// Initialize GLEWunifor
glewExperimental = GL_TRUE;
GLuint err = glewInit();
if (err != GLEW_OK)
{
std::cerr << "initialize GLEW Failed" << std::endl;
return false;
}
//setup key callbacks
glfwSetKeyCallback(w, key_callback);
glfwSetFramebufferSizeCallback(w, glfw_onFramebufferSize);
glClearColor(0.23f, 0.38f, 0.47f, 1.0f); ///5ADD
// Define the viewport dimensions
glViewport(0, 0, wWidth, wHeight); //necessary?
return true;
}
void key_callback(GLFWwindow *w, int key, int scancode, int action, int mode)
{
// See http://www.glfw.org/docs/latest/group__keys.html
if ((key == GLFW_KEY_ESCAPE || key == GLFW_KEY_Q) && action == GLFW_PRESS)
glfwSetWindowShouldClose(w, GL_TRUE);
if (key == GLFW_KEY_W && action == GLFW_PRESS)
{
bool showWires = false;
if (showWires)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
else
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
//whever window resizes, do this
void glfw_onFramebufferSize(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
void averageFPS(GLFWwindow* window) ///5ADDdown
{
static double previousSeconds = 0.0;
static int frameCount = 0;
double passedSeconds;
double currentSeconds = glfwGetTime(); //seconds since GLFW started
passedSeconds = currentSeconds - previousSeconds;
// Limit time updates to 4 times per second
if (passedSeconds > 0.25)
{
previousSeconds = currentSeconds;
double fps = (double)frameCount / passedSeconds;
// double frameInMilSecs = 1000.0 / fps;
frameCount = 0;}
frameCount++;
}
While glGetShaderiv returns a parameter from a shader object, glGetProgramiv returns a parameter from a program object.
This means you have to change your code like this:
// glGetProgramiv(shader, GL_COMPILE_STATUS, &status); <--- delete
glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
Ensure that the shader files are correctly loaded and trace them for debug reasons:
glShaderSource(vs, 1, &vsSourcePtr, NULL);
glShaderSource(fs, 1, &fsSourcePtr, NULL);
std::cout << "VERTEX SHADER:" << std::endl << vsSourcePtr << std::endl << std::endl;
glCompileShader(vs);
testShaderCompile(vs);
std::cout << "FRAGMENT SHADER:" << std::endl << fsSourcePtr << std::endl << std::endl;
glCompileShader(fs);
testShaderCompile(fs);
Since you don't set any matrices, you have to set up all the vertex coordinates in normalized device space, which is in [-1.0, 1.0].
To make the mesh proper "visible" on the viewport set the radius of the sphere to 0.5:
GLfloat Radius = 0.5f;
and draw all the primitives:
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
Ok, I think I'm going crazy.
I've been developing a 3D game in OpenGL for a while now - all working well.
Then I decided to redesign it from scratch since it was turning to custard. So I set up a basic framework for rendering with OpenGL, and I can't for the life of me get the thing to render a triangle.
I have gone over every line of code about 60 times, gone through every checklist I can find on the internet, checked it against my old project which worked, and still can't see anything wrong with it.
So here is where it all starts (the main method):
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* cmdLine, int cmdShow)
{
try
{
GLContext gl(WINDOW_TITLE, 800, 600);
TestRenderable test;
while(true)
{
glClearColor(0.0f, 0.15f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
test.draw();
gl.swapBuffers();
}
}
catch(const std::string& err)
{
std::string msg = "Error: ";
msg += err;
MessageBox(NULL, msg.c_str(), WINDOW_TITLE, MB_OK | MB_ICONERROR);
}
catch(...)
{
MessageBox(NULL, "Unknown Error", WINDOW_TITLE, MB_OK | MB_ICONERROR);
}
}
And all class methods are below:
GLContext::GLContext(const std::string& title, int width, int height)
{
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
m_pWindow = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);
m_glContext = SDL_GL_CreateContext(m_pWindow);
if(glewInit() != GLEW_OK)
throw(std::string("Failed to initialize GLEW"));
}
GLContext::~GLContext()
{
SDL_GL_DeleteContext(m_glContext);
SDL_DestroyWindow(m_pWindow);
}
void GLContext::swapBuffers()
{
SDL_GL_SwapWindow(m_pWindow);
}
TestRenderable::TestRenderable() : m_pShader(nullptr), m_pMesh(nullptr)
{
std::vector<std::string> attributes;
attributes.push_back("position");
attributes.push_back("color");
m_pShader = new Shader("shaders\\shader", attributes);
m_pShader->addUniform("transform");
glm::vec3 positions[] =
{
{ 10.0f, 10.0f, 0.0f },
{ 200.0f, 10.0f, 0.0f },
{ 10.0f, 100.0f, 0.0f }
};
glm::vec4 colors[] =
{
{ 0.0f, 1.0f, 0.0f, 1.0f },
{ 1.0f, 0.0f, 0.0f, 1.0f },
{ 0.0f, 0.0f, 1.0f, 1.0f }
};
m_pMesh = new Mesh_PC(positions, colors, 3);
m_projection = glm::ortho<float>(0.0f, 800.0f, 600.0f, 0.0f, 0.0f, 1.0f);
}
TestRenderable::~TestRenderable()
{
if(m_pShader != nullptr)
delete m_pShader;
if(m_pMesh != nullptr)
delete m_pMesh;
}
void TestRenderable::draw()
{
m_pShader->bind();
m_pShader->setUniformMatrix("transform", m_projection);
m_pMesh->draw();
}
Mesh_PC::Mesh_PC(glm::vec3 positions[], glm::vec4 colors[], unsigned numVertices) : m_vertexCount(numVertices)
{
glGenVertexArrays(1, m_vertexArray);
glBindVertexArray(m_vertexArray[0]);
glGenBuffers(NUM_BUFFERS, m_buffers);
glBindBuffer(GL_ARRAY_BUFFER, m_buffers[POSITIONS_BUFFER]);
glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(positions[0]), positions, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_buffers[COLORS_BUFFER]);
glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(colors[0]), colors, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, 0);
glBindVertexArray(0);
}
Mesh_PC::~Mesh_PC()
{
glDeleteBuffers(NUM_BUFFERS, m_buffers);
glDeleteVertexArrays(1, m_vertexArray);
}
void Mesh_PC::draw()
{
glBindVertexArray(m_vertexArray[0]);
glDrawArrays(GL_TRIANGLES, 0, m_vertexCount);
glBindVertexArray(0);
}
Shader::Shader(const std::string& filename, const std::vector<std::string>& attributes)
{
m_program = glCreateProgram();
m_shaders[0] = createShader(loadShader(filename + ".vs"), GL_VERTEX_SHADER);
m_shaders[1] = createShader(loadShader(filename + ".fs"), GL_FRAGMENT_SHADER);
for(unsigned int i = 0; i < NUM_SHADERS; ++i)
glAttachShader(m_program, m_shaders[i]);
for(unsigned int i = 0; i < attributes.size(); ++i)
glBindAttribLocation(m_program, i, attributes[i].c_str());
glLinkProgram(m_program);
checkShaderError(m_program, GL_LINK_STATUS, true, "Program linking failed");
glValidateProgram(m_program);
checkShaderError(m_program, GL_VALIDATE_STATUS, true, "Program is invalid");
}
Shader::~Shader()
{
for(unsigned int i = 0; i < NUM_SHADERS; ++i)
{
glDetachShader(m_program, m_shaders[i]);
glDeleteShader(m_shaders[i]);
}
glDeleteProgram(m_program);
}
void Shader::addUniform(const std::string& name)
{
m_uniforms.insert(std::make_pair(name, glGetUniformLocation(m_program, name.c_str())));
}
void Shader::bind()
{
glUseProgram(m_program);
}
void Shader::setUniformMatrix(const std::string& name, const glm::mat4& value)
{
bind();
auto it = m_uniforms.find(name);
if(it != m_uniforms.end())
{
glUniformMatrix4fv(it->second, 1, GL_FALSE, &value[0][0]);
}
}
void Shader::setUniformVec4(const std::string& name, const glm::vec4& value)
{
bind();
auto it = m_uniforms.find(name);
if(it != m_uniforms.end())
{
glUniform4fv(it->second, 1, &value[0]);
}
}
std::string Shader::loadShader(const std::string& filename)
{
std::ifstream file;
file.open(filename.c_str());
std::string output;
std::string line;
if(file.is_open())
{
while(file.good())
{
getline(file, line);
output.append(line + "\n");
}
file.close();
}
else
{
std::string err("Unable to load shader: ");
err.append(filename);
throw(err);
}
return output;
}
void Shader::checkShaderError(GLuint shader, GLuint flag, bool isProgram, const char* errorMessage)
{
GLint success = 0;
GLchar error[1024] = { 0 };
if(isProgram)
glGetProgramiv(shader, flag, &success);
else
glGetShaderiv(shader, flag, &success);
if(success == GL_FALSE)
{
if(isProgram)
glGetProgramInfoLog(shader, sizeof(error), NULL, error);
else
glGetShaderInfoLog(shader, sizeof(error), NULL, error);
std::string err(errorMessage);
err.append(": '");
err.append(error);
err.append("'");
throw(err);
}
}
GLuint Shader::createShader(const std::string& text, GLenum shaderType)
{
GLuint shader = glCreateShader(shaderType);
if(shader == 0)
throw("Shader creation failed");
const GLchar *shaderSourceStrings[1];
GLint shaderSourceStringLengths[1];
shaderSourceStrings[0] = text.c_str();
shaderSourceStringLengths[0] = static_cast<GLint>(text.length());
glShaderSource(shader, 1, shaderSourceStrings, shaderSourceStringLengths);
glCompileShader(shader);
checkShaderError(shader, GL_COMPILE_STATUS, false, "Shader compilation failed");
return shader;
}
And by the way I am initalizing SDL - I have an object of the below type in the GLContext class:
class SDL
{
public:
SDL() { SDL_Init(SDL_INIT_VIDEO); }
~SDL() { SDL_Quit(); }
};
And my shaders:
Vertex Shader:
#version 120
attribute vec3 position;
attribute vec4 color;
varying vec4 color0;
uniform mat4 transform;
void main(void)
{
gl_Position = transform * vec4(position, 1.0);
color0 = color;
}
Fragment Shader:
#version 120
varying vec4 color0;
void main(void)
{
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
I know there is unused stuff in the shaders etc but this is just for testing purposes.
Can anyone help me here?
You need to call glBindBuffer before calling glVertexAttribPointer. At the moment, both attributes will be using the last buffer bound (the one with the color values).