2
\$\begingroup\$

I have been studying on glsl lately, starting from very simple vertex and fragment shaders creation for Blender3d, I was quickly drawn to writing the OpenGL context in C++. I find it difficult to understand the process of drawing on screen from matrices data, especially the way I am supposed to work with uniform variables: here in this code I created an uniform mat4 in my vertex shader, which index is retrieved with glGetUniformLocation() in DoShader() and filled with data with glUniformMatrix4fv() in the IdleFunc This way I can use my projection matrix in the shader program, shouldn't it be enough for displaying a perspective ? I am obliviously missing something because my square is not appearing at all. Thanks for your help.

#include <GL/glew.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#include <string.h>
#include "math_3d.h"

static GLuint vbo;
GLint gWorldLocation;

class Pipeline
{
    public:
        Pipeline(){};
        void InitPersProjTrans(Matrix4f& m);
        Matrix4f* GetTrans();
        void setPerspectiveProj(float fov, float width, float height, float znear, float zfar);
    private:
        Matrix4f m_transformation;
        struct {
    float FOV;
    float Width;
    float Height;
    float zNear;
    float zFar;
        } m_persProj;
};

void Pipeline::setPerspectiveProj(float fov, float width, float height, float znear, float zfar)
{
    m_persProj.FOV = fov;
    m_persProj.Width = width;
    m_persProj.Height = height;
    m_persProj.zNear = znear;
    m_persProj.zFar = zfar;
} 

void Pipeline::InitPersProjTrans(Matrix4f& m) 
{
    const float ar = m_persProj.Width / m_persProj.Height;
    const float zNear = m_persProj.zNear;
    const float zFar = m_persProj.zFar;
    float zRange = zNear - zFar; 
    const float tanHalfFOV = tanf(ToRadian(m_persProj.FOV / 2.0));
    m.m[0][0] = 1.0f / (tanHalfFOV * ar);
    m.m[0][1] = 0.0f;
    m.m[0][2] = 0.0f;
    m.m[0][3] = 0.0f;

    m.m[1][0] = 0.0f;
    m.m[1][1] = 1.0f / tanHalfFOV;
    m.m[1][2] = 0.0f;
    m.m[1][3] = 0.0f;

    m.m[2][0] = 0.0f;
    m.m[2][1] = 0.0f;
    m.m[2][2] = (zNear - zFar) / zRange;
    m.m[2][3] = 2.0f * zFar * zNear / zRange;

    m.m[3][0] = 0.0f;
    m.m[3][1] = 0.0f;
    m.m[3][2] = 1.0f;
    m.m[3][3] = 0.0f;
}

Matrix4f* Pipeline::GetTrans()
{
    Matrix4f PersProjTrans;

    InitPersProjTrans(PersProjTrans);
    m_transformation = PersProjTrans;
    return &m_transformation;
}

static void createShader(GLuint program, const char *shader, GLenum type){
    GLuint obj = glCreateShader(type);
    if (!obj){
        fprintf(stderr, "fail during shader creation: %d", type);
        exit(1); 
    }
    const GLchar *p[1];
    p[0] = shader;
    GLint len[1];
    len[0] = strlen(shader);
    glShaderSource(obj, 1, &shader, NULL);
    glCompileShader(obj);

    GLint test;
    glGetShaderiv(obj, GL_COMPILE_STATUS, &test);
    if (!test){
        GLchar log[1024];
        glGetShaderInfoLog(obj, 1024, NULL, log);
        fprintf(stderr, "fail during shader compilation:\n%s", log);
        exit(1);
    }
    glAttachShader(program, obj);
}

#define RAW(X) "#version 330\n" #X
static const char *vertex_shader = RAW(
    layout (location = 0) in vec3 Position;

    uniform mat4 World;

    void main(void)
    {
        gl_Position = World * vec4(Position.x, Position.y, Position.z, 1.0);
    }
);

static const char *fragment_shader = RAW(
    out vec4 fragColor;
    void main(void)
    {
        fragColor = vec4(0.5, 0.15, 1.0, 1.0);
    }
);

static void DoShaders(){
    GLuint prog = glCreateProgram();
    if (!prog){
        fprintf(stderr, "fail while creating shader program\n");
        exit(1);
    }

    createShader(prog, vertex_shader, GL_VERTEX_SHADER);
    createShader(prog, fragment_shader, GL_FRAGMENT_SHADER);

    glLinkProgram(prog);

    gWorldLocation = glGetUniformLocation(prog, "World");
    GLint test = 0;
    GLchar log[1024] = { 0 };
    glGetProgramiv(prog, GL_LINK_STATUS, &test);
    if (!test){
        glGetProgramInfoLog(prog, sizeof(log), NULL, log);
        fprintf(stderr,"error link%s\n", log);
        exit(1);
    }
    glValidateProgram(prog);
    glGetProgramiv(prog, GL_VALIDATE_STATUS, &test);
    if (!test){
        glGetProgramInfoLog(prog, sizeof(log), NULL, log);
        fprintf(stderr,"error validate%s\n", log);
        exit(1);
    }
    glUseProgram(prog);
}

static void ObjectRenderInfo(){

    Vector3f verts[4];
    verts[0] = Vector3f(-1.0f, -0.1f, -1.0f);
    verts[1] = Vector3f(-1.0f, -0.1f, 1.0f);
    verts[2] = Vector3f(1.0f, -0.1f, -1.0f);
    verts[3] = Vector3f(1.0f, -0.1f, 1.0f);

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
}

static void IdleFunc(){ 
    glClear(GL_COLOR_BUFFER_BIT);
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

    Pipeline p;

    p.setPerspectiveProj(50, 1024, 768, 1, 1000);

    glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, (const GLfloat*)p.GetTrans());
    glDrawArrays(GL_QUADS, 0, 4);
    glDisableVertexAttribArray(0);
    glutSwapBuffers();
}

int main (int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowSize(1024, 768);
    glutInitWindowPosition(100, 100);

    glutCreateWindow("MyProject");

    GLenum test = glewInit();
    if (test != GLEW_OK){
        fprintf(stderr, "failed to start glew%s\n", glewGetErrorString(test));
        return 1;
    }

    glutIdleFunc(&IdleFunc);

    ObjectRenderInfo();

    DoShaders();

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

    glutMainLoop();
}

get the header file

\$\endgroup\$
1
\$\begingroup\$

I believe your problem can be solved by simply creating a Vertex Array Object for OpenGL to utilize before you create your Vertex Buffer Object.

You're required to use VAOs past OpenGL 3.1 core.

// In ObjectRenderInfo create your VAO
GLuint vertexArrayId;
glGenVertexArrays(1, &vertexArrayId);

glGenBuffers(1, &vbo);
...

It may help you avoid issues like this in the future, when getting started with OpenGL, frequently test to make sure your assumptions about what is functioning are correct by testing frequently. If you can't get a single white point or triangle to display on screen first, there's no point in writing all the code to roll your own version of a perspective projection (especially when you could use a simple one-liner like glm::perspective).

\$\endgroup\$
  • \$\begingroup\$ No it wont work either, I've had the program working properly at one point by using build-in function like that, but I wanted to input the perspective my self that was the point, anyway I got messed up. I will try to go a little backward. \$\endgroup\$ – Yvain Apr 6 '16 at 11:08
  • \$\begingroup\$ Have you tried breaking your program and peaking the result of p.GetTrans() and manually multiplying it against the positions in C++ to verify that the math will actually generate coordinates within the clip space? \$\endgroup\$ – user5665 Apr 6 '16 at 11:31
1
\$\begingroup\$

These are what I can think of and cause the object to not appear on screen:

1.) Your near variable is set to too low and it can't render

2.) Your object is at (0;0;0) and thus gets clipped in the depth test phase.

3.) Your projection matrix is in column major order and not in row major, as OpenGL requires it. Try changing every m.m[x][y] to m.m[y][x]

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.