#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);
}