Example #1
0
void Shader::init(const char *_vsFile, const char *_fsFile)
{
	shader_vp = glCreateShader(GL_VERTEX_SHADER);
	shader_fp = glCreateShader(GL_FRAGMENT_SHADER);

	const char *vsText = textFileRead(_vsFile);
	const char *fsText = textFileRead(_fsFile);

	if (vsText == nullptr || fsText == nullptr)
	{
		std::cout << "ERROR: Either vertex or fragment shader file could not be found" << std::endl;
		return;
	}

	glShaderSource(shader_vp, 1, &vsText, 0);
	glShaderSource(shader_fp, 1, &fsText, 0);

	glCompileShader(shader_vp);
	validateShader(shader_vp, _vsFile);
	glCompileShader(shader_fp);
	validateShader(shader_fp, _fsFile);

	shader_id = glCreateProgram();
	glAttachShader(shader_id, shader_fp);
	glAttachShader(shader_id, shader_vp);
	glLinkProgram(shader_id);
	validateProgram(shader_id);

	delete vsText;
	delete fsText;
}
Example #2
0
void Shader::compile()
{
    // compile the shader and attempt to vlidate it
    glCompileShader(m_shaderHandle);
    if (!validateShader(m_shaderHandle, m_shaderFile.c_str()))
    {
        return;
    }
    m_compiled = true;
}
Example #3
0
unsigned Shader::loadShader(const char *fileName, GLenum shaderType){

	unsigned shader_id = 0;
	FILE* file;
	long file_size = -1;
	const char* glsl_source;

	glsl_source = textFileRead(fileName);

	shader_id = glCreateShader(shaderType);

	glShaderSource(shader_id, 1, &glsl_source, 0);
	glCompileShader(shader_id);
	validateShader(shader_id);

	return shader_id;
}
Example #4
0
SpriteBatcher::SpriteBatcher()
    : camera(NULL), vao(0), program(0), lastTexture(0), drawn(0)
{
    // Create shader program
    GLuint vertex = createShader(VERTEX_SRC, GL_VERTEX_SHADER);
    GLuint fragment = createShader(FRAGMENT_SRC, GL_FRAGMENT_SHADER);
    program = createShaderProgram(vertex, fragment);
    linkShader(program);
    validateShader(program);

    glDetachShader(program, vertex);
    glDeleteShader(vertex);
    glDetachShader(program, fragment);
    glDeleteShader(fragment);

    // Create vertex array and prepare buffers for being updated >= once per frame
    glGenVertexArrays(1, &vao);
    glGenBuffers(2, buffers);

    glBindVertexArray(vao);

    glBindBuffer(GL_ARRAY_BUFFER, buffers[VCTBO]);
    glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_DYNAMIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[EBO]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 0, NULL, GL_DYNAMIC_DRAW);
    
    // Attributes
    glBindBuffer(GL_ARRAY_BUFFER, buffers[VCTBO]);
    glEnableVertexAttribArray(0); // position
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(1); // color
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(2 * sizeof(float)));
    glEnableVertexAttribArray(2); // texture coordinates
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));

    glBindVertexArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
Example #5
0
int main(void)
{
    GLFWwindow* window;

    window = init("Transparency", 640, 480);
    if(!window)
    {
        return -1;
    }

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)640/(float)480, 0.1f, 1000.0f);
    glm::mat4 model1, model2, model3; // Cube 1 and 2 will be transparent, cube 3 not
    model1 = glm::translate(model1, glm::vec3(0.5f, 0.0f, -1.0f));
    model2 = glm::translate(model2, glm::vec3(0.0f, 0.5f, -2.0f));
    model3 = glm::translate(model3, glm::vec3(0.0f, 0.0f, -3.0f));
    glm::mat4 view;
    view = glm::translate(view, glm::vec3(0.0f, 0.0f, -4.0f));

    GLuint vertex = createShader(VERTEX_SRC, GL_VERTEX_SHADER);
    if(!vertex)
    {
        return -1;
    }
    GLuint fragment = createShader(FRAGMENT_SRC, GL_FRAGMENT_SHADER);
    if(!fragment)
    {
        return -1;
    }
    GLuint program = createShaderProgram(vertex, fragment);
    if(!program)
    {
        return -1;
    }
    bool result = linkShader(program);
    if(!result)
    {
        return -1;
    }
    result = validateShader(program);
    if(!result)
    {
        return -1;
    }
    glDetachShader(program, vertex);
    glDeleteShader(vertex);
    glDetachShader(program, fragment);
    glDeleteShader(fragment);

    glUseProgram(program);

    GLuint vao;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    GLuint vbo;
    glGenBuffers(1, &vbo);

    float vertices[] =
    {
        // x   y      z     r     g     b     u     v
        -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
         0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
         0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
         0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
    
        -0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
         0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
         0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
         0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
    
        -0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
    
         0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
         0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
         0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
         0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
         0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
         0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
    
        -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
         0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
         0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
         0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
    
        -0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
         0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
         0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
         0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f
    };

    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0); // position
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 0);
    glEnableVertexAttribArray(1); // color
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2); // texture coordinates
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(6 * sizeof(GLfloat)));

    int w, h;
    GLuint textureTrans = loadImage("transparent.png", &w, &h, 0, true); // GL_TEXTURE0
    if(!textureTrans)
    {
        return -1;
    }
    GLuint textureOpaq = loadImage("opaque.png", &w, &h, 0, true); // GL_TEXTURE0
    if(!textureOpaq)
    {
        return -1;
    }
    glUniform1i(glGetUniformLocation(program, "tex"), 0); // GL_TEXTURE0

    GLint viewUL = glGetUniformLocation(program, "view");
    glUniformMatrix4fv(viewUL, 1, GL_FALSE, glm::value_ptr(view));
    GLint projUL = glGetUniformLocation(program, "projection");
    glUniformMatrix4fv(projUL, 1, GL_FALSE, glm::value_ptr(proj));
    GLint modelUL = glGetUniformLocation(program, "model");
    
    glClearColor(0.75f, 0.75f, 0.75f, 1.0f);

    while(!glfwWindowShouldClose(window))
    {
        // Clear (note the addition of GL_DEPTH_BUFFER_BIT)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // We have to sort the objects, rendering the backmost objects first
        // in this example, model1 is in front of model2, which is in front of model3
        // and model3 is the only one using the opaque texture
        // (This way we can avoid the sorting, making the example a bit easier to follow)
        glBindTexture(GL_TEXTURE_2D, textureOpaq);
        glUniformMatrix4fv(modelUL, 1, GL_FALSE, glm::value_ptr(model3));
        glDrawArrays(GL_TRIANGLES, 0, 36);

        glBindTexture(GL_TEXTURE_2D, textureTrans);
        glUniformMatrix4fv(modelUL, 1, GL_FALSE, glm::value_ptr(model2));
        glDrawArrays(GL_TRIANGLES, 0, 36);
        glUniformMatrix4fv(modelUL, 1, GL_FALSE, glm::value_ptr(model1));
        glDrawArrays(GL_TRIANGLES, 0, 36);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // Clean up
    glDeleteBuffers(1, &vbo);
    glDeleteVertexArrays(1, &vao);
    glDeleteTextures(1, &textureTrans);
    glDeleteTextures(1, &textureOpaq);

    glfwTerminate();
    return 0;
}
Example #6
0
int main(void)
{
    GLFWwindow* window;
    window = init("Deferred Shading", WIDTH / 2, HEIGHT / 2);
    if(!window)
    {
        return -1;
    }

    glEnable(GL_DEPTH_TEST);

    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    Camera camera(CAMERA_PERSPECTIVE, 45.0f, 0.1f, 1000.0f, (float)WIDTH, (float)HEIGHT);
    setCamera(&camera);

    GLuint geomProgram, lightProgram;
    {
        // Geometry pass program
        GLuint vertex = createShader(VERTEX_GEOM_SRC, GL_VERTEX_SHADER);
        GLuint fragment = createShader(FRAGMENT_GEOM_SRC, GL_FRAGMENT_SHADER);
        geomProgram = createShaderProgram(vertex, fragment);
        linkShader(geomProgram);
        validateShader(geomProgram);
        glDetachShader(geomProgram, vertex);
        glDeleteShader(vertex);
        glDetachShader(geomProgram, fragment);
        glDeleteShader(fragment);
    }
    {
        // Light pass program
        GLuint vertex = createShader(VERTEX_LIGHT_SRC, GL_VERTEX_SHADER);
        GLuint fragment = createShader(FRAGMENT_LIGHT_SRC, GL_FRAGMENT_SHADER);
        lightProgram = createShaderProgram(vertex, fragment);
        linkShader(lightProgram);
        validateShader(lightProgram);
        glDetachShader(lightProgram, vertex);
        glDeleteShader(vertex);
        glDetachShader(lightProgram, fragment);
        glDeleteShader(fragment);
    }

    // Create quad VAO
    GLuint vao;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    GLuint vbo;
    glGenBuffers(1, &vbo);

    float vertices[] =
    {
        // x   y       u     v
        -1.0f,  1.0f,  0.0f, 0.0f,
         1.0f,  1.0f,  1.0f, 0.0f,
         1.0f, -1.0f,  1.0f, 1.0f,
        -1.0f, -1.0f,  0.0f, 1.0f
    };

    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    GLuint ebo;
    glGenBuffers(1, &ebo);

    GLuint indices[] =
    {
        0, 1, 2,
        2, 3, 0
    };

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));

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

    // Load the diffuse and specular texture
    // TODO: seperate specular texture
    glUseProgram(geomProgram);
    int w, h;
    GLuint textureDiff = loadImage("asteroid.png", &w, &h, 0, false); // GL_TEXTURE0
    if(!textureDiff)
    {
        return -1;
    }
    glUniform1i(glGetUniformLocation(geomProgram, "diffuse"), 0); // GL_TEXTURE0
    GLuint textureSpec = loadImage("asteroid.png", &w, &h, 1, false); // GL_TEXTURE1
    if(!textureSpec)
    {
        return -1;
    }
    glUniform1i(glGetUniformLocation(geomProgram, "specular"), 1); // GL_TEXTURE1

    Mesh mesh;
    if(!mesh.load("asteroid.obj"))
    {
        std::cerr << "Could not load mesh" << std::endl;
        return -1;
    }

    // Set random positions for the asteroids
    std::vector<glm::mat4> models;
    models.resize(NUM_ASTEROIDS);
    srand(SEED);
    for(int i = 0; i < NUM_ASTEROIDS; ++i)
    {
        glm::mat4 model;

        // Translate
        model = glm::translate(model, glm::vec3(rand() % 100 - 50.0f, rand() % 100 - 50.0f, rand() % 100));

        // Scale
        float scale = (rand() % 200) / 100.0f + 0.1f;
        model = glm::scale(model, glm::vec3(scale, scale, scale));

        // Rotate
        model = glm::rotate(model, glm::radians((float)(rand() % 100)), glm::vec3(1.0f, 1.0f, 1.0f));

        models[i] = model;
    }

    mesh.setInstances(NUM_ASTEROIDS, models);

    // Set positions and more for the lights
    PointLight lights[NUM_POINT_LIGHTS];
    for(int i = 0; i < NUM_POINT_LIGHTS; ++i)
    {
        float x, y, z;
        x = rand() % 100 - 50.0f;
        y = rand() % 100 - 50.0f;
        z = rand() % 100;

        float r, g, b; // For simplicity's sake, ambient, diffuse and specular colours are the same
        r = ((rand() % 10) / 20.0f) + 0.5;
        g = ((rand() % 10) / 20.0f) + 0.5;
        b = ((rand() % 10) / 20.0f) + 0.5;

        float con, lin, qua;
        con = 1.0f;
        lin = 0.09f;
        qua = 0.032f;

        lights[i].set(glm::vec3(x, y, z), glm::vec3(con, lin, qua), glm::vec3(r, g, b),
                glm::vec3(r, g, b), glm::vec3(r, g, b));
    }

    GBuffer gBuffer;

    glUseProgram(lightProgram);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, gBuffer.position);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, gBuffer.normal);
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, gBuffer.color);
    glUniform1i(glGetUniformLocation(lightProgram, "g_position"), 0);
    glUniform1i(glGetUniformLocation(lightProgram, "g_normal_spec_pow"), 1);
    glUniform1i(glGetUniformLocation(lightProgram, "g_albedo_spec"), 2);

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    while(!glfwWindowShouldClose(window))
    {
        if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        {
            break;
        }

        updateCamera(WIDTH, HEIGHT, window);

        // GEOMETRY PASS
        glBindFramebuffer(GL_FRAMEBUFFER, gBuffer.buffer);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glm::mat4 proj = camera.getProjection();
        glm::mat4 view = camera.getView();
        glUseProgram(geomProgram);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, textureDiff);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, textureSpec);
        glUniformMatrix4fv(glGetUniformLocation(geomProgram, "projection"), 1, GL_FALSE, glm::value_ptr(proj));
        glUniformMatrix4fv(glGetUniformLocation(geomProgram, "view"), 1, GL_FALSE, glm::value_ptr(view));
        glUniform1f(glGetUniformLocation(geomProgram, "specularPower"), 16.0f);
        mesh.render();
        
        // LIGHT PASS
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glUseProgram(lightProgram); 
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, gBuffer.position);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, gBuffer.normal);
        glActiveTexture(GL_TEXTURE2);
        glBindTexture(GL_TEXTURE_2D, gBuffer.color);
        glUniform3fv(glGetUniformLocation(lightProgram, "eye"), 1, glm::value_ptr(camera.getPosition()));
        for(int i = 0; i < NUM_POINT_LIGHTS; ++i)
        {
            lights[i].setUniforms(i, lightProgram);
        }
        glBindVertexArray(vao);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // Clean up
    glDeleteTextures(1, &textureDiff);
    glDeleteTextures(1, &textureSpec);
    glDeleteProgram(geomProgram);
    glDeleteProgram(lightProgram);
    glDeleteVertexArrays(1, &vao);
    glDeleteBuffers(1, &vbo);
    glDeleteBuffers(1, &ebo);

    glfwTerminate();
    return 0;
}
Example #7
0
int main(void)
{
    GLFWwindow* window;

    // The OpenGL context creation code is in
    // ../common/util.cpp
    window = init("Hello Heightmap", 640, 480);
    if(!window)
    {
        return -1;
    }

    // Hide the cursor (escape will exit the application)
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // We will need to enable depth testing, so that OpenGL draws further
    // vertices first
    glEnable(GL_DEPTH_TEST);

    // Draw wireframe
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    // Enable backface culling
    glEnable(GL_CULL_FACE); 
    glCullFace(GL_BACK);
    glFrontFace(GL_CCW); // front facing vertices are defined counter clock wise

    // Create the model matrix
    model = glm::mat4();
    // Rotate just a bit (the vector indicates the axes on which to rotate)
    model = glm::rotate(model, -glm::radians(35.0f), glm::vec3(0.0f, 1.0f, 1.0f));

    // Set the camera
    Camera camera(CAMERA_PERSPECTIVE, 45.0f, 0.1f, 1000.0f, 640.0f, 480.0f);
    camera.setPosition(0.0f, 0.0f, -3.0f);
    setCamera(&camera); // The camera updating is handled in ../common/util.cpp

    // Load the heightmap
    HeightMap map(20.0f);
    if(!map.load("heightmap.bmp"))
    {
        return -1;
    }
    const std::vector<float>& data = map.getData();
    int w = map.getWidth(), h = map.getHeight();
    std::vector<float> vertices;
    for(int i = 0; i < h; ++i)
    {
        for(int j = 0; j < w; ++j)
        {
            float x = (float)i;
            float z = (float)j;
            float height = data[i * w + j];

            vertices.push_back(x * SIZE);
            vertices.push_back(height);
            vertices.push_back(z * SIZE);
            // This is where you could add extra information,
            // like colour or texture coordinates.
            // You would have to change the vertex attribute pointer
            // code as well!
        }
    }
    std::vector<GLuint> indices;
    for(int i = 0; i < (h - 1); ++i)
    {
        for(int j = 0; j < (w - 1); ++j)
        {
            // We create six indices for each tile
            indices.push_back(i * w + j);
            indices.push_back((i + 1) * w + j);
            indices.push_back(i * w + j + 1);
            indices.push_back(i * w + j + 1);
            indices.push_back((i + 1) * w + j);
            indices.push_back((i + 1) * w + j + 1);
        }
    }

    // We start by creating a vertex and fragment shader
    // from the above strings
    GLuint vertex = createShader(VERTEX_SRC, GL_VERTEX_SHADER);
    if(!vertex)
    {
        return -1;
    }
    GLuint fragment = createShader(FRAGMENT_SRC, GL_FRAGMENT_SHADER);
    if(!fragment)
    {
        return -1;
    }
    // Now we must make a shader program: this program
    // contains both the vertex and the fragment shader
    GLuint program = createShaderProgram(vertex, fragment);
    if(!program)
    {
        return -1;
    }
    // We link the program, just like your C compiler links
    // .o files
    bool result = linkShader(program);
    if(!result)
    {
        return -1;
    }
    // We make sure the shader is validated
    result = validateShader(program);
    if(!result)
    {
        return -1;
    }
    // Detach and delete the shaders, because we no longer need them
    glDetachShader(program, vertex);
    glDeleteShader(vertex);
    glDetachShader(program, fragment);
    glDeleteShader(fragment);

    glUseProgram(program); // Set this as the current shader program

    // We now create the data to send to the GPU
    GLuint vao;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    GLuint vbo;
    glGenBuffers(1, &vbo);

    // Upload the vertices to the buffer
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), &vertices[0], GL_STATIC_DRAW);

    GLuint ebo;
    glGenBuffers(1, &ebo);

    // Upload the indices to the buffer
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices.size(), &indices[0], GL_STATIC_DRAW);

    // Enable the vertex attributes and upload their data (see: layout(location=x))
    glEnableVertexAttribArray(0); // position
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0);

    // We have now successfully created a drawable Vertex Array Object

    // Set the clear color to a light grey
    glClearColor(0.75f, 0.75f, 0.75f, 1.0f);

    while(!glfwWindowShouldClose(window))
    {
        if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        {
            break;
        }

        updateCamera(640, 480, window);

        // Clear (note the addition of GL_DEPTH_BUFFER_BIT)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Upload the MVP matrices
        GLint modelUL = glGetUniformLocation(program, "model");
        glUniformMatrix4fv(modelUL, 1, GL_FALSE, glm::value_ptr(model));
        GLint viewUL = glGetUniformLocation(program, "view");
        glUniformMatrix4fv(viewUL, 1, GL_FALSE, glm::value_ptr(camera.getView()));
        // This can be moved out of the loop because it rarely changes
        GLint projUL = glGetUniformLocation(program, "projection");
        glUniformMatrix4fv(projUL, 1, GL_FALSE, glm::value_ptr(camera.getProjection()));

        // The VAO is still bound so just draw the vertices
        glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);

        // Tip: if nothing is drawn, check the return value of glGetError and google it

        // Swap buffers to show current image on screen (for more information google 'backbuffer')
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // Clean up
    glDeleteBuffers(1, &vbo);
    glDeleteBuffers(1, &ebo);
    glDeleteVertexArrays(1, &vao);

    glfwTerminate();
    return 0;
}
Example #8
0
bool
Shader::initInContext(unsigned int contextId)
{
  // handler for the single shaders
  GLuint vs = 0;
  GLuint fs = 0;
  GLuint gs = 0;

  // return values for the compile process
  GLint vsCompiled = 0;
  GLint fsCompiled = 0;
  GLint gsCompiled = 0;

  // iterator for program parameter
  std::map<GLenum,int>::iterator params;



#ifndef GLOOST_SYSTEM_DISABLE_OUTPUT_MESSAGES
  std::cerr << std::endl;
  std::cerr << std::endl << "Message from Message from Shader::initInContext() on SharedResource " << getSharedResourceId() << ":";
  std::cerr << std::endl << "             Building for context " << contextId << ".";
  std::cerr << std::endl;
#endif
  // remove this resource if it allready exists
  removeFromContext(contextId);


  /// create a vertex, a fragment and a geometry shader handle
  vs  = glCreateShader( GL_VERTEX_SHADER );
  fs  = glCreateShader( GL_FRAGMENT_SHADER );

  if (_geometryShaderSource)
  {
    gs  = glCreateShader( GL_GEOMETRY_SHADER );
  }


  /// send shader source to the driver
  glShaderSource(vs, 1, &_vertexShaderSource, NULL);
  glShaderSource(fs, 1, &_fragmentShaderSource, NULL);

  if (_geometryShaderSource)
  {
    glShaderSource(gs, 1, &_geometryShaderSource, NULL);
  }


  // vertex shader
  glCompileShader(vs);
  glGetShaderiv(vs, GL_COMPILE_STATUS, &vsCompiled);

  if (vsCompiled == GL_FALSE)
  {

#ifndef GLOOST_SYSTEM_DISABLE_OUTPUT_ERRORS
    std::cerr << std::endl;
    std::cerr << std::endl << "ERROR in shader::create() on SharedResource " << getSharedResourceId() << ":";
    std::cerr << std::endl << "         Can't compile vertexshader \"" << _vertexShaderFileName << "\"";
    std::cerr << std::endl;
#endif

  }

  // validate the vertex shader and print compiler log
  validateShader(vs, _vertexShaderFileName);



  // fragment shader
  glCompileShader( fs );
  glGetShaderiv(fs, GL_COMPILE_STATUS, &fsCompiled);


  if (fsCompiled == GL_FALSE)
  {

#ifndef GLOOST_SYSTEM_DISABLE_OUTPUT_ERRORS
    std::cerr << std::endl;
    std::cerr << std::endl << "ERROR in shader::create() on SharedResource " << getSharedResourceId() << ":";
    std::cerr << std::endl << "         Cant't compile fragmentshader \"" << _fragmentShaderFileName << "\"";
    std::cerr << std::endl;
#endif

  }

  // validate the fragment shader and print compiler log
  validateShader(fs, _fragmentShaderFileName);


  // geometry shader
  if (gs)
  {
    glCompileShader( gs );
    glGetShaderiv(gs, GL_COMPILE_STATUS, &gsCompiled);

    if (gsCompiled == GL_FALSE)
    {

#ifndef GLOOST_SYSTEM_DISABLE_OUTPUT_ERRORS
      std::cerr << std::endl;
      std::cerr << std::endl << "ERROR in shader::create() on SharedResource " << getSharedResourceId() << ":";
      std::cerr << std::endl << "         Cant't compile geometryshader \"" << _geometryShaderFileName << "\"";
      std::cerr << std::endl;
#endif

    }

    // validate the fragment shader and print compiler log
    validateShader(gs, _geometryShaderFileName);
  }
  else
  {
    // shader not used - set compile flag anyway
    gsCompiled = GL_TRUE;
  }// if (geometry shader exist)



  /// create a shader program and attache the compiled vertex, fragment and geometry binaries
  unsigned int shaderHandle = glCreateProgram();


  /// make a association between the context and the GL handle of this shader
  setResourceHandleForContext(shaderHandle, contextId);


  /// attache programs to shaderh handle
  glAttachShader( shaderHandle, fs );
  glAttachShader( shaderHandle, vs );

  if (gs)
  {
    glAttachShader( shaderHandle, gs );
    CheckErrorsGL(("Shader::initInContext() after attaching geometry shader for Shared Resource " + gloost::toString(getSharedResourceId())).c_str());

    for ( params =_programParameter.begin(); params != _programParameter.end(); params++ )
    {
        glProgramParameteriEXT(shaderHandle, (*params).first, (*params).second);
        CheckErrorsGL(("Shader::initInContext() after setting for Shared Resource " + gloost::toString(getSharedResourceId())).c_str());
    }
  }


  /// link programs
  glLinkProgram(shaderHandle);

//  		int temp;
//		glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT,&temp);
//		std::cerr << std::endl << "GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT: " << temp;


#ifndef GLOOST_SYSTEM_DISABLE_OUTPUT_MESSAGES
  if (vsCompiled == GL_TRUE && fsCompiled == GL_TRUE && gsCompiled == GL_TRUE)
  {
    std::cerr << std::endl;
    std::cerr << std::endl << "Message from Shader::compileToProgram() on SharedResource " << getSharedResourceId() << ":";
    std::cerr << std::endl << "             Successfully compiled and linked.";
    std::cerr << std::endl << "               \"" << _vertexShaderFileName << "\", ";
    std::cerr << std::endl << "               \"" << _fragmentShaderFileName << "\"";
    if (gs)
    {
      std::cerr << ", "<< std::endl << "               \"" << _geometryShaderFileName << "\"";
    }
    std::cerr << std::endl;
    return true;
  }
#endif

  return false;
}
Example #9
0
int main(void)
{
    GLFWwindow* window = init("Morph Target Animation", 640, 480);
    if(!window)
    {
        return -1;
    }

    glEnable(GL_DEPTH_TEST);

    glm::mat4 proj = glm::perspective(glm::radians(45.0f), 640.0f / 480.0f, 0.1f, 1000.0f);
    glm::mat4 view = glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, -4.0f));
    glm::mat4 model = glm::rotate(glm::mat4(), glm::radians(45.0f), glm::vec3(1.0f, 0.0f, 1.0f));

    // Load the shader
    GLuint program;
    {
        GLuint vertex = createShader(VERTEX_SRC, GL_VERTEX_SHADER);
        GLuint fragment = createShader(FRAGMENT_SRC, GL_FRAGMENT_SHADER);
        program = createShaderProgram(vertex, fragment);
        linkShader(program);
        validateShader(program);

        glDetachShader(program, vertex);
        glDeleteShader(vertex);
        glDetachShader(program, fragment);
        glDeleteShader(fragment);

        if(!program)
        {
            return -1;
        }
    }
    glUseProgram(program);

    float vertices1[] =
    {
        // x   y      z
        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
    
        -0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,
    
        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f, -0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
    
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
    
        -0.5f, -0.5f, -0.5f,
         0.5f, -0.5f, -0.5f,
         0.5f, -0.5f,  0.5f,
         0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f,  0.5f,
        -0.5f, -0.5f, -0.5f,
    
        -0.5f,  0.5f, -0.5f,
         0.5f,  0.5f, -0.5f,
         0.5f,  0.5f,  0.5f,
         0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f,  0.5f,
        -0.5f,  0.5f, -0.5f,
    };
    float vertices2[] =
    {
        // x   y      z     
        -1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f,  1.0f, -1.0f,
         1.0f,  1.0f, -1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f, -1.0f, -1.0f,
    
        -1.0f, -1.0f,  1.0f,
         1.0f, -1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,
    
        -1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f, -1.0f,
        -1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f, -1.0f,
        -1.0f, -1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
    
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
    
        -1.0f, -1.0f, -1.0f,
         1.0f, -1.0f, -1.0f,
         1.0f, -1.0f,  1.0f,
         1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f,  1.0f,
        -1.0f, -1.0f, -1.0f,
    
        -1.0f,  1.0f, -1.0f,
         1.0f,  1.0f, -1.0f,
         1.0f,  1.0f,  1.0f,
         1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f,  1.0f,
        -1.0f,  1.0f, -1.0f,
    };

    // Create the vao and buffer objects
    GLuint vao;
    GLuint buffers[2];
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glGenBuffers(2, buffers);

    glBindBuffer(GL_ARRAY_BUFFER, buffers[CUR_POS_VBO]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices1), vertices1, GL_DYNAMIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

    glBindBuffer(GL_ARRAY_BUFFER, buffers[NEXT_POS_VBO]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_DYNAMIC_DRAW);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, GL_FALSE, glm::value_ptr(proj));
    glUniformMatrix4fv(glGetUniformLocation(program, "view"), 1, GL_FALSE, glm::value_ptr(view));
    glUniformMatrix4fv(glGetUniformLocation(program, "model"), 1, GL_FALSE, glm::value_ptr(model));

    glClearColor(0.75f, 0.75f, 0.75f, 1.0f);

    float timeSinceFrame = 0.0f;
    int currentFrame;
    glBindVertexArray(vao);
    while(!glfwWindowShouldClose(window))
    {
        // If the time for a frame has passed, we have to switch frames
        timeSinceFrame += glfwGetTime();
        if(timeSinceFrame >= FRAME_TIME)
        {
            if(currentFrame == 0)
            {
                currentFrame = 1;
            }
            else
            {
                currentFrame = 0;
            }
            timeSinceFrame = 0.0f;

            // Update the buffers
            glBindBuffer(GL_ARRAY_BUFFER, buffers[CUR_POS_VBO]);
            glBufferData(GL_ARRAY_BUFFER, sizeof(vertices1), 
                    currentFrame == 0 ? vertices1 : vertices2, GL_DYNAMIC_DRAW);
            glBindBuffer(GL_ARRAY_BUFFER, buffers[NEXT_POS_VBO]);
            glBufferData(GL_ARRAY_BUFFER, sizeof(vertices1),
                    currentFrame == 0 ? vertices2 : vertices1, GL_DYNAMIC_DRAW);

            glBindBuffer(GL_ARRAY_BUFFER, 0);
        }

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glUniform1f(glGetUniformLocation(program, "tween"), timeSinceFrame / FRAME_TIME);

        glDrawArrays(GL_TRIANGLES, 0, 36);

        glfwSetTime(0.0);
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glDeleteBuffers(2, buffers);
    glDeleteVertexArrays(1, &vao);
    glDeleteProgram(program);

    glfwTerminate();
    return 0;
}
Example #10
0
int main(void)
{
    GLFWwindow* window;

    window = init("Billboards", 640, 480);
    if(!window)
    {
        return -1;
    }

    glEnable(GL_DEPTH_TEST);

    Camera camera(CAMERA_PERSPECTIVE, 45.0f, 0.1f, 1000.0f, 640.0f, 480.0f);
    camera.setPosition(0.0f, 0.0f, -3.0f);
    setCamera(&camera); // The camera updating is handled in ../common/util.cpp

    glm::mat4 model;

    GLuint program;
    {
        GLuint vertex = createShader(VERTEX_SRC, GL_VERTEX_SHADER);
        GLuint fragment = createShader(FRAGMENT_SRC, GL_FRAGMENT_SHADER);
        program = createShaderProgram(vertex, fragment);
        linkShader(program);
        validateShader(program);
        glDetachShader(program, vertex);
        glDeleteShader(vertex);
        glDetachShader(program, fragment);
        glDeleteShader(fragment);
    }
    GLuint billboardProgram;
    {
        GLuint vertex = createShader(VERTEX_BB_SRC, GL_VERTEX_SHADER);
        GLuint fragment = createShader(FRAGMENT_BB_SRC, GL_FRAGMENT_SHADER);
        billboardProgram = createShaderProgram(vertex, fragment);
        linkShader(billboardProgram);
        validateShader(billboardProgram);
        glDetachShader(program, vertex);
        glDeleteShader(vertex);
        glDetachShader(program, fragment);
        glDeleteShader(fragment);
    }

    glUseProgram(program);

    GLuint vao;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    GLuint vbo;
    glGenBuffers(1, &vbo);

    float vertices[] =
    {
        // x   y      z     r     g     b     u     v
        -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
         0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
         0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
         0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
    
        -0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
         0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
         0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
         0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
    
        -0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
    
         0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
         0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
         0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
         0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
         0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
         0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
    
        -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
         0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
         0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
         0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
    
        -0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
         0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
         0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
         0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f
    };

    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0); // position
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 0);
    glEnableVertexAttribArray(1); // color
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2); // texture coordinates
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(6 * sizeof(GLfloat)));

    float verts_bb[] =
    {
        -0.5f, -0.5f,
        0.5f,  -0.5f,
        -0.5f, 0.5f,
        0.5f, 0.5f
    };
    GLuint vbo_bb;
    glGenBuffers(1, &vbo_bb);
    GLuint vao_bb;
    glGenVertexArrays(1, &vao_bb);
    glBindVertexArray(vao_bb);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_bb);
    glBufferData(GL_ARRAY_BUFFER, sizeof(verts_bb), verts_bb, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glUseProgram(billboardProgram);
    glUniform3f(glGetUniformLocation(billboardProgram, "center"), 0.0f, 2.0f, 0.0f);
    glUniform2f(glGetUniformLocation(billboardProgram, "size"), 0.5f, 0.5f);

    int w, h;
    GLuint texture = loadImage("image.png", &w, &h, 0, false);
    glUniform1i(glGetUniformLocation(billboardProgram, "tex"), 0);
    glUseProgram(program);
    glUniform1i(glGetUniformLocation(program, "tex"), 0);
    
    glClearColor(0.75f, 0.75f, 0.75f, 1.0f);

    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    while(!glfwWindowShouldClose(window))
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        updateCamera(640, 480, window);

        // Draw the cube
        glUseProgram(program);
        glBindVertexArray(vao);

        GLint modelUL = glGetUniformLocation(program, "model");
        glUniformMatrix4fv(modelUL, 1, GL_FALSE, glm::value_ptr(model));
        GLint viewUL = glGetUniformLocation(program, "view");
        glUniformMatrix4fv(viewUL, 1, GL_FALSE, glm::value_ptr(camera.getView()));
        GLint projUL = glGetUniformLocation(program, "projection");
        glUniformMatrix4fv(projUL, 1, GL_FALSE, glm::value_ptr(camera.getProjection()));

        glDrawArrays(GL_TRIANGLES, 0, 36);

        // Draw the billboard
        glBindVertexArray(vao_bb);
        glUseProgram(billboardProgram);

        viewUL = glGetUniformLocation(billboardProgram, "view");
        glUniformMatrix4fv(viewUL, 1, GL_FALSE, glm::value_ptr(camera.getView()));
        projUL = glGetUniformLocation(billboardProgram, "proj");
        glUniformMatrix4fv(projUL, 1, GL_FALSE, glm::value_ptr(camera.getProjection()));
        glUniform1i(glGetUniformLocation(billboardProgram, "fixedSize"), 0);
        glUniform3f(glGetUniformLocation(billboardProgram, "camRight"), camera.getRightVector().x, camera.getRightVector().y, camera.getRightVector().z);
        glUniform3f(glGetUniformLocation(billboardProgram, "camUp"), camera.getUpVector().x, camera.getUpVector().y, camera.getUpVector().z);

        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // Clean up
    glDeleteBuffers(1, &vbo);
    glDeleteVertexArrays(1, &vao);
    glDeleteBuffers(1, &vbo_bb);
    glDeleteVertexArrays(1, &vao_bb);
    glDeleteProgram(program);
    glDeleteProgram(billboardProgram);
    glDeleteTextures(1, &texture);

    glfwTerminate();
    return 0;
}