Welcome to collectivesolver - Programming & Software Q&A with code examples. A website with trusted programming answers. All programs are tested and work.

Contact: aviboots(AT)netvision.net.il

Buy a domain name - Register cheap domain names from $0.99 - Namecheap

Scalable Hosting That Grows With You

Secure & Reliable Web Hosting, Free Domain, Free SSL, 1-Click WordPress Install, Expert 24/7 Support

Semrush - keyword research tool

Boost your online presence with premium web hosting and servers

Disclosure: My content contains affiliate links.

39,855 questions

51,776 answers

573 users

How to draw a rotated 3D square using OpenGL, FreeGLUT, GLEW, vertex shader & perspective projection in C++

1 Answer

0 votes
#include <iostream> // For std::cerr, std::cout
#include <fstream>  // For std::ifstream
#include <string>   // For std::string
#include <cstring>  // For strlen
#include <sstream>  // For std::stringstream (used in ReadFile)
#include <GL/glew.h> 
#include <freeglut.h> 

const double PI = 3.14159265358979323846;

#define ToRadian(x) (float)(((x) * PI / 180.0f))
#define ToDegree(x) (float)(((x) * 180.0f / PI))

struct Vec3 {
    float x, y, z;

    Vec3() {}

    Vec3(float _x, float _y, float _z) {
        x = _x;
        y = _y;
        z = _z;
    }
};

struct Mat4
{
    float m[4][4] = { 0.0f };

    Mat4() {}

    Mat4(float v00, float v01, float v02, float v03,
         float v10, float v11, float v12, float v13,
         float v20, float v21, float v22, float v23,
         float v30, float v31, float v32, float v33)
    {
        m[0][0] = v00; m[0][1] = v01; m[0][2] = v02; m[0][3] = v03;
        m[1][0] = v10; m[1][1] = v11; m[1][2] = v12; m[1][3] = v13;
        m[2][0] = v20; m[2][1] = v21; m[2][2] = v22; m[2][3] = v23;
        m[3][0] = v30; m[3][1] = v31; m[3][2] = v32; m[3][3] = v33;
    }

    Mat4 operator *(const Mat4& Right) const {
        Mat4 Result;

        // Matrix Multiplication
        for (unsigned int i = 0; i < 4; i++) {
            for (unsigned int j = 0; j < 4; j++) {
                Result.m[i][j] = m[i][0] * Right.m[0][j] +
                    m[i][1] * Right.m[1][j] +
                    m[i][2] * Right.m[2][j] +
                    m[i][3] * Right.m[3][j];
            }
        }

        return Result;
    }
};
 
static float RandomFloat() {
    return static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
 }

struct Vertex {
    Vec3 pos;
    Vec3 color;

    Vertex() {}

    Vertex(float x, float y, float z) {
        pos = Vec3(x, y, z);

        float red = RandomFloat();
        float green = RandomFloat();
        float blue = RandomFloat();
        color = Vec3(red, green, blue);
    }
};

GLuint VBO;
GLuint IBO;
GLuint gLocation;

static void RenderScene() {
    glClear(GL_COLOR_BUFFER_BIT);

    // Scaling();
    static float Scale = 0.0f;
    Scale += 0.01f; // Rotation speed

    Mat4 Rotation(cosf(Scale), 0.0f, -sinf(Scale), 0.0f,
                  0.0f, 1.0f, 0.0f, 0.0f,
                  sinf(Scale), 0.0f, cosf(Scale), 0.0f,
                  0.0f, 0.0f, 0.0f, 1.0f);

    Mat4 Translation(1.0f, 0.0f, 0.0f, 0.0f,
                     0.0f, 1.0f, 0.0f, 0.0f,
                     0.0f, 0.0f, 1.0f, 2.0f, // 2.0 = Distance
                     0.0f, 0.0f, 0.0f, 1.0f);

    /*
      FOV = in OpenGL code defines the field of view angle, which is the 
      visible angle of the scene from the camera's perspective. 
      A FOV of 90.0f degrees means the camera will capture a wide view, 
      effectively encompassing a 90-degree angle horizontally 
      (or vertically, depending on the implementation)
    */
    float FOV = 90.0f; // Field Of View

    /*
      In OpenGL, the tangent of half the field of view (FOV) angle is crucial 
      for perspective projection, especially when calculating the projection matrix. 
      Specifically, tan(fov/2) (where fov is the half-angle in radians) 
      is used to determine the scaling factor for x and y coordinates 
      to achieve the desired perspective. 
      This scaling is essential for creating the illusion of depth in 3D scenes. 
    */

    float tangentHalfFOV = tanf(ToRadian(FOV / 2.0f));

    /*
      In OpenGL, when setting up a perspective projection, the reciprocal of 
      the tangent of half the field of view (FOV) is used. 
      This value is crucial for calculating the aspect ratio and near/far plane clipping. 
      It's not the FOV itself, but rather a value derived from it 
      that directly influences the projection matrix's scaling and distortion effects.
    */

    float d = 1 / tangentHalfFOV; 

    Mat4 Projection(d, 0.0f, 0.0f, 0.0f,
                    0.0f, d, 0.0f, 0.0f,
                    0.0f, 0.0f, 1.0f, 0.0f,
                    0.0f, 0.0f, 1.0f, 0.0f);

    Mat4 FinalMatrix = Projection * Translation * Rotation;

    glUniformMatrix4fv(gLocation, 1, GL_TRUE, &FinalMatrix.m[0][0]);

    glBindBuffer(GL_ARRAY_BUFFER, VBO); // glew
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); // glew

    // position
    glEnableVertexAttribArray(0); // glew
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), 0);

    // color
    glEnableVertexAttribArray(1); // glew
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); // glew

    // glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices);
    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0); // glew

    // glDisableVertexAttribArray GLEW_GET_FUN(__glewDisableVertexAttribArray)
    glDisableVertexAttribArray(0); // glew
    glDisableVertexAttribArray(1); // glew

    glutPostRedisplay(); // freeglut

    glutSwapBuffers(); // freeglut
}

static void AddShader(GLuint ShaderProgram, const char* pShaderText, GLenum ShaderType) {
    GLuint ShaderObj = glCreateShader(ShaderType); // glew

    if (ShaderObj == 0) {
        fprintf(stderr, "Error creating shader type %d\n", ShaderType);
        exit(0);
    }

    const GLchar* p[1]; // glew
    p[0] = pShaderText;

    GLint len[1];
    len[0] = (GLint)strlen(pShaderText);

    glShaderSource(ShaderObj, 1, p, len); // glew

    glCompileShader(ShaderObj); // glew

    GLint success;
    glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success); // glew

    if (!success) {
        GLchar InfoLog[1024]; // glew
        glGetShaderInfoLog(ShaderObj, 1024, NULL, InfoLog); // glew
        fprintf(stderr, "Error compiling shader type %d: '%s'\n", ShaderType, InfoLog);
        exit(1);
    }

    glAttachShader(ShaderProgram, ShaderObj); // glew
}

const char* pVSFileName = "shader.vs";
const char* pFSFileName = "shader.fs";

static bool ReadFile(const char* pFileName, std::string& outFile)
{
    std::ifstream f(pFileName); // Open the file

    bool rv = false;
    if (f.is_open()) {
        std::stringstream ss;
        ss << f.rdbuf(); // Read entire file buffer into stringstream
        outFile = ss.str(); // Convert stringstream to std::string
        rv = true;
    }
    else {
        std::cerr << "Error opening file: " << pFileName << std::endl;
    }

    return rv;
}

static void CompileShaders()
{
    GLuint ShaderProgram = glCreateProgram(); // glew

    if (ShaderProgram == 0) {
        fprintf(stderr, "Error creating shader program - glCreateProgram\n");
        exit(1);
    }

    std::string vs, fs;

    if (!ReadFile(pVSFileName, vs)) {
        exit(1);
    };

    AddShader(ShaderProgram, vs.c_str(), GL_VERTEX_SHADER); // glew

    if (!ReadFile(pFSFileName, fs)) {
        exit(1);
    };

    AddShader(ShaderProgram, fs.c_str(), GL_FRAGMENT_SHADER); // glew

    GLint Success = 0;
    GLchar ErrorLog[1024] = { 0 }; // glew

    glLinkProgram(ShaderProgram); // glew

    glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &Success); // glew
    if (Success == 0) {
        glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog); // glew
        fprintf(stderr, "Error linking shader program: '%s'\n", ErrorLog);
        exit(1);
    }

    gLocation = glGetUniformLocation(ShaderProgram, "guWorld");
    if (gLocation == -1) {
        printf("Error getting uniform location of 'guWorld'\n");
        exit(1);
    }

    glValidateProgram(ShaderProgram); // glew
    glGetProgramiv(ShaderProgram, GL_VALIDATE_STATUS, &Success); // glew
    if (!Success) {
        glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog); // glew
        fprintf(stderr, "Invalid shader program: '%s'\n", ErrorLog);
        exit(1);
    }

    glUseProgram(ShaderProgram); // glew
}

static void CreateVertexBuffer() {
    Vertex Vertices[8];
    
    // Square
    Vertices[0] = Vertex(0.5f, 0.5f, 0.5f);
    Vertices[1] = Vertex(-0.5f, 0.5f, -0.5f);
    Vertices[2] = Vertex(-0.5f, 0.5f, 0.5f);
    Vertices[3] = Vertex(0.5f, -0.5f, -0.5f);
    Vertices[4] = Vertex(-0.5f, -0.5f, -0.5f);
    Vertices[5] = Vertex(0.5f, 0.5f, -0.5f);
    Vertices[6] = Vertex(0.5f, -0.5f, 0.5f);
    Vertices[7] = Vertex(-0.5f, -0.5f, 0.5f);

    glGenBuffers(1, &VBO); // glew
    glBindBuffer(GL_ARRAY_BUFFER, VBO); // glew
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW); // glew
}

static void CreateIndexBuffer()
{
    unsigned int Indices[] = {
                              0, 1, 2,
                              1, 3, 4,
                              5, 6, 3,
                              7, 3, 6,
                              2, 4, 7,
                              0, 7, 6,
                              0, 5, 1,
                              1, 5, 3,
                              5, 0, 6,
                              7, 4, 3,
                              2, 1, 4,
                              0, 2, 7
    };

    glGenBuffers(1, &IBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}


int main(int argc, char** argv)
{
    srand((unsigned)time(NULL));

    glutInit(&argc, argv); // freeglut
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); // freeglut

    int width = 800;
    int height = 600;
    glutInitWindowSize(width, height); // freeglut

    // freeglut
    int win = glutCreateWindow("freeglut - glutCreateWindow() - Draw 3D Square");

    GLenum res = glewInit(); // glew
    if (res != GLEW_OK) { // glew
        std::cout << glewGetErrorString(res); // glew
        return 1;
    }

    GLclampf Red = 0.0f, Green = 0.0f, Blue = 0.0f, Alpha = 0.0f;
    glClearColor(Red, Green, Blue, Alpha);

    glEnable(GL_CULL_FACE);
    glFrontFace(GL_CW);
    glCullFace(GL_BACK);

    CreateVertexBuffer();
    CreateIndexBuffer();

    CompileShaders();

    glutDisplayFunc(RenderScene); // freeglut

    glutMainLoop(); // freeglut

    return 0;
}



/*
run



*/

 

// fragment shader 

#version 460 core

in vec4 Color;

out vec4 FragColor;

void main()
{
    FragColor = Color;
}


// vertex shader 

#version 460 core

layout (location = 0) in vec3 Position;
layout (location = 1) in vec3 inColor;

uniform mat4 guWorld;

out vec4 Color;

void main()
{
    gl_Position = guWorld * vec4(Position, 1.0);
    Color = vec4(inColor, 1.0);
}

 



answered Jul 16, 2025 by avibootz
...