https://en.wikipedia.org/wiki/Black-body_radiation
Coordinate System:
X in µm: 0 < x < 3
Y in kW / (sr * m² * nm): 0 < y < 15
Graph_CoordinateSystem coordinatesystem{
/* xmin, xmax, xstep */
-0.1, +3.0f, 0.01f,
/* ymin, ymax, ystep */
-1.0, +15.0f, 0.01f
};
Function Object: std::function< float (float) >
float Y = f (float X) where f calculates the result in the correct dimensions, then casts it to float:
auto function_3K = [](float x) {
/* first: transform x-coord into wavelength / µm */
long double wavelength = long double(x) * Micro();
/* then: set temperature / Kelvin */
long double temperature = 3 * Kilo();
/* with the correct dimensions, compute spectral radiance */
long double spectralradiance = Physics::SpectralRadiance(wavelength, temperature);
/* result is measured in Watt / ( sr * m² * m ) */
/* y-coord is measured in KW / ( sr * m² * nm )) */
/* so: eliminate Kilo (in numerator) and Nano (in denominator), return float */
return float(spectralradiance / Kilo() * Nano());
};
Create a "Graph" which is a function object + a color:
Graph_Y_FX graph_3K{ function_3K, vec4(1, 0, 0, 1)}; /* red, T = 3000K */
Tell the "Graphics" what to draw:
graphics.SetCoordinateSystem(coordinatesystem);
graphics.AddGraph(graph_3K);
Use the keys 2, 4, 6 or 8 on the num block to move the coordinate system.
Use the keys + or - on the num block to scale the coordinate system.
Complete Source Code:
Main.cpp
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <iostream>
#include <vector>
#include <string>
#include "Physics.h"
#include "Graphics.h"
using namespace std;
using namespace glm;
using namespace Physics::Dimension;
/* callbacks */
/******************************************************************************************************/
void error_callback(int error, const char* description);
void drop_callback(GLFWwindow* window, int pathcount, const char** paths);
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
void cursor_position_callback(GLFWwindow* window, double xpos, double ypos);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
/* debug callback: gets called by OpenGL automatically */
void APIENTRY OpenGLDebugCallback(
GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userparam);
/******************************************************************************************************/
/* global variables */
/******************************************************************************************************/
struct DebugUserParams {
bool ShowNotifications = false;
} debuguserparams;
Graphics graphics;
Graph_CoordinateSystem coordinatesystem{
/* xmin, xmax, xstep */
-0.1, +3.0f, 0.01f,
/* ymin, ymax, ystep */
-1.0, +15.0f, 0.01f
};
/******************************************************************************************************/
/******************************************************************************************************/
int main(int argc, char* argv[])
{
auto function_3K = [](float x) {
/* first: transform x-coord into wavelength / µm */
long double wavelength = long double(x) * Micro();
/* then: set temperature / Kelvin */
long double temperature = 3 * Kilo();
/* with the correct dimensions, compute spectral radiance */
long double spectralradiance = Physics::SpectralRadiance(wavelength, temperature);
/* result is measured in Watt / ( sr * m² * m ) */
/* y-coord is measured in KW / ( sr * m² * nm )) */
/* so: eliminate Kilo (in numerator) and Nano (in denominator), return float */
return float(spectralradiance / Kilo() * Nano());
};
auto function_4K = [](float x) {
long double wavelength = long double(x) * Micro();
long double temperature = 4 * Kilo();
long double spectralradiance = Physics::SpectralRadiance(wavelength, temperature);
return float(spectralradiance / Kilo() * Nano());
};
auto function_5K = [](float x) {
long double wavelength = long double(x) * Micro();
long double temperature = 5 * Kilo();
long double spectralradiance = Physics::SpectralRadiance(wavelength, temperature);
return float(spectralradiance / Kilo() * Nano());
};
auto x_min_visible = [](float y) { return 0.380 /* µm */; };
auto x_max_visible = [](float y) { return 0.780 /* µm */; };
Graph_Y_FX graph_3K{ function_3K, vec4(1, 0, 0, 1)}; /* red, T = 3000K */
Graph_Y_FX graph_4K{ function_4K, vec4(0, 1, 0, 1)}; /* green, T = 4000K */
Graph_Y_FX graph_5K{ function_5K, vec4(0, 0, 1, 1)}; /* blue, T = 5000K */
Graph_X_FY graph_x_min_visible{ x_min_visible, vec4(1, 1, 1, 1) }; /* x = f(y) */
Graph_X_FY graph_x_max_visible{ x_max_visible, vec4(1, 1, 1, 1) }; /* x = f(y) */
graphics.SetCoordinateSystem(coordinatesystem);
graphics.AddGraph(graph_3K);
graphics.AddGraph(graph_4K);
graphics.AddGraph(graph_5K);
graphics.AddGraph(graph_x_min_visible);
graphics.AddGraph(graph_x_max_visible);
bool fullscreen = false;
bool debugcontext = true;
bool compatibilityprofilecontext = false;
bool vsync = true;
/* Initialize GLFW */
if (!glfwInit())
return 1;
/* Create a window and its OpenGL context */
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, debugcontext ? GLFW_TRUE : GLFW_FALSE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, compatibilityprofilecontext ? GLFW_TRUE : GLFW_FALSE);
glfwWindowHint(GLFW_OPENGL_PROFILE, compatibilityprofilecontext ? GLFW_OPENGL_COMPAT_PROFILE : GLFW_OPENGL_CORE_PROFILE);
GLFWmonitor* monitor = fullscreen ? glfwGetPrimaryMonitor() : NULL;
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL", monitor, NULL);
if (!window)
{
glfwTerminate();
return 1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
/* Set V-sync */
glfwSwapInterval(vsync ? 1 : 0);
/* Set callback functions */
glfwSetErrorCallback(error_callback);
glfwSetDropCallback(window, drop_callback);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetKeyCallback(window, key_callback);
glfwSetCursorPosCallback(window, cursor_position_callback);
glfwSetMouseButtonCallback(window, mouse_button_callback);
glfwSetScrollCallback(window, scroll_callback);
/* Initialize GLEW */
if (glewInit() != GLEW_OK)
{
glfwTerminate();
return 2;
}
/* Register debug callback if debug context is available */
GLint contextflags = 0;
glGetIntegerv(GL_CONTEXT_FLAGS, &contextflags);
if (contextflags & GL_CONTEXT_FLAG_DEBUG_BIT)
{
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(OpenGLDebugCallback, &debuguserparams);
}
/* Initialize graphics */
int width = 0, height = 0;
glfwGetFramebufferSize(window, &width, &height);
if (!graphics.Initialize(width, height, coordinatesystem))
{
glfwTerminate();
return 3;
}
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
graphics.Render();
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
/* Clean up graphics */
graphics.CleanUp();
glfwTerminate();
return 0;
}
/******************************************************************************************************/
/* input */
/******************************************************************************************************/
void error_callback(int error, const char* description)
{
cout << "GLFW Error: " << description << endl;
cin.get();
}
void drop_callback(GLFWwindow* window, int pathcount, const char** paths)
{
for (unsigned int i = 0; i < pathcount; i++)
cout << "dropped file: " << paths[i] << endl;
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
graphics.DefaultFramebufferResized(width, height);
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (action == GLFW_PRESS || action == GLFW_REPEAT)
{
/* close application */
if (key == GLFW_KEY_ESCAPE)
glfwSetWindowShouldClose(window, GLFW_TRUE);
/* move coordinate system */
else if (key == GLFW_KEY_KP_4)
{
float delta = coordinatesystem.XMax - coordinatesystem.XMin;
coordinatesystem.XMin -= delta / 5.0f;
coordinatesystem.XMax -= delta / 5.0f;
graphics.SetCoordinateSystem(coordinatesystem);
}
else if (key == GLFW_KEY_KP_6)
{
float delta = coordinatesystem.XMax - coordinatesystem.XMin;
coordinatesystem.XMin += delta / 5.0f;
coordinatesystem.XMax += delta / 5.0f;
graphics.SetCoordinateSystem(coordinatesystem);
}
else if (key == GLFW_KEY_KP_8)
{
float delta = coordinatesystem.YMax - coordinatesystem.YMin;
coordinatesystem.YMin += delta / 5.0f;
coordinatesystem.YMax += delta / 5.0f;
graphics.SetCoordinateSystem(coordinatesystem);
}
else if (key == GLFW_KEY_KP_2)
{
float delta = coordinatesystem.YMax - coordinatesystem.YMin;
coordinatesystem.YMin -= delta / 5.0f;
coordinatesystem.YMax -= delta / 5.0f;
graphics.SetCoordinateSystem(coordinatesystem);
}
/* scale coordinate system */
else if (key == GLFW_KEY_KP_ADD)
{
float scale = 0.9f;
float center_x = (coordinatesystem.XMin + coordinatesystem.XMax) / 2.0f;
float center_y = (coordinatesystem.YMin + coordinatesystem.YMax) / 2.0f;
float delta_x = (coordinatesystem.XMax - coordinatesystem.XMin) / 2.0f;
float delta_y = (coordinatesystem.YMax - coordinatesystem.YMin) / 2.0f;
coordinatesystem.XMin = center_x - delta_x * scale;
coordinatesystem.XMax = center_x + delta_x * scale;
coordinatesystem.XStep = coordinatesystem.XStep * scale;
coordinatesystem.YMin = center_y - delta_y * scale;
coordinatesystem.YMax = center_y + delta_y * scale;
coordinatesystem.YStep = coordinatesystem.YStep * scale;
graphics.SetCoordinateSystem(coordinatesystem);
}
else if (key == GLFW_KEY_KP_SUBTRACT)
{
float scale = 1.1f;
float center_x = (coordinatesystem.XMin + coordinatesystem.XMax) / 2.0f;
float center_y = (coordinatesystem.YMin + coordinatesystem.YMax) / 2.0f;
float delta_x = (coordinatesystem.XMax - coordinatesystem.XMin) / 2.0f;
float delta_y = (coordinatesystem.YMax - coordinatesystem.YMin) / 2.0f;
coordinatesystem.XMin = center_x - delta_x * scale;
coordinatesystem.XMax = center_x + delta_x * scale;
coordinatesystem.XStep *= scale;
coordinatesystem.YMin = center_y - delta_y * scale;
coordinatesystem.YMax = center_y + delta_y * scale;
coordinatesystem.YStep *= scale;
graphics.SetCoordinateSystem(coordinatesystem);
}
}
}
void cursor_position_callback(GLFWwindow* window, double xpos, double ypos)
{}
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
if (action == GLFW_PRESS)
{
if (button == GLFW_MOUSE_BUTTON_LEFT)
{}
}
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{}
/******************************************************************************************************/
/* debug */
/******************************************************************************************************/
void APIENTRY OpenGLDebugCallback(
GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userparam)
{
/* debug user params */
DebugUserParams* params = (DebugUserParams*)userparam;
/* filter out unnecessary warnings */
if (!params->ShowNotifications)
if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
return;
/* source */
string str_source;
if (source == GL_DEBUG_SOURCE_API) str_source = "API";
if (source == GL_DEBUG_SOURCE_WINDOW_SYSTEM) str_source = "Window System";
if (source == GL_DEBUG_SOURCE_SHADER_COMPILER) str_source = "Shader Compiler";
if (source == GL_DEBUG_SOURCE_THIRD_PARTY) str_source = "Third Party";
if (source == GL_DEBUG_SOURCE_APPLICATION) str_source = "Application";
if (source == GL_DEBUG_SOURCE_OTHER) str_source = "Other";
/* type */
string str_type;
if (type == GL_DEBUG_TYPE_ERROR) str_type = "Error";
if (type == GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR) str_type = "Deprecated Behavior";
if (type == GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR) str_type = "Undefined Behavior";
if (type == GL_DEBUG_TYPE_PORTABILITY) str_type = "Portability";
if (type == GL_DEBUG_TYPE_PERFORMANCE) str_type = "Performance";
if (type == GL_DEBUG_TYPE_MARKER) str_type = "Marker";
if (type == GL_DEBUG_TYPE_PUSH_GROUP) str_type = "Push Group";
if (type == GL_DEBUG_TYPE_POP_GROUP) str_type = "Pop Group";
if (type == GL_DEBUG_TYPE_OTHER) str_type = "Other";
/* severity */
string str_severity;
if (severity == GL_DEBUG_SEVERITY_HIGH) str_severity = "High";
if (severity == GL_DEBUG_SEVERITY_MEDIUM) str_severity = "Medium";
if (severity == GL_DEBUG_SEVERITY_LOW) str_severity = "Low";
if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) str_severity = "Notification";
/* print message */
cout << "OpenGL Debug Message:" << endl;
cout << "----------------------------------" << endl;
cout << "ID: \t\t" << id << endl;
cout << "Source: \t" << str_source << endl;
cout << "Type: \t\t" << str_type << endl;
cout << "Severity: \t" << str_severity << endl;
cout << "Message: \t" << message << endl;
cout << "----------------------------------" << endl << endl;
}
/******************************************************************************************************/
Graph.h
#pragma once
#include <glm/glm.hpp>
#include <functional>
/* X range, Y range */
struct Graph_CoordinateSystem
{
float XMin = 0.0f;
float XMax = 1.0f;
float XStep = 0.1f;
float YMin = 0.0f;
float YMax = 1.0f;
float YStep = 0.1f;
};
/* graph y = f(x) */
struct Graph_Y_FX
{
std::function<float(float)> Function = /* lambda expression */
[] (float x)
{
return 0;
};
glm::vec4 Color = glm::vec4(0, 1, 0, 1); /* green by default */
};
/* graph x = f(y), is essentially the same as f(x) */
struct Graph_X_FY
{
std::function<float(float)> Function = /* lambda expression */
[] (float y)
{
return 0;
};
glm::vec4 Color = glm::vec4(0, 1, 0, 1); /* green by default */
};
Physics.h
#pragma once
namespace Physics
{
/* constants */
namespace Constant
{
long double SpeedOfLight();
long double StephanBoltzman();
long double WiensDisplacement();
long double Planck();
long double Boltzman();
}
/* dimensions */
namespace Dimension
{
long double Tera();
long double Giga();
long double Mega();
long double Kilo();
long double Milli();
long double Micro();
long double Nano();
long double Pico();
}
/* physical laws */
long double Steradian(long double alpha_ranging_from_0_to_2PI);
long double Luminosity(long double area_in_m2, long double temperature_in_K);
long double LuminosityOfSphere(long double radius_in_m, long double temperature_in_K);
long double WaveLength(long double frequency_in_Hz);
long double Frequency(long double wavelength_in_m);
long double PeakWaveLength(long double temperature_in_K);
long double SpectralRadiance(long double wavelength_in_m, long double temperature_in_K);
}
Physics.cpp
#include "Physics.h"
#include <cmath>
using namespace std;
#define PI 3.14159265358979323846264338327950288419716939937510L
#define SPEED_OF_LIGHT 299792458.0L
#define STEPHAN_BOLTZMAN 5.6470367e-8L
#define WIENS_DISPLACEMENT 2.897772917e-3L
#define PLANCK 6.62607004081e-34L
#define BOLTZMAN 1.3806485279e-23L
long double Physics::Constant::SpeedOfLight()
{
return SPEED_OF_LIGHT;
}
long double Physics::Constant::StephanBoltzman()
{
return STEPHAN_BOLTZMAN;
}
long double Physics::Constant::WiensDisplacement()
{
return WIENS_DISPLACEMENT;
}
long double Physics::Constant::Planck()
{
return PLANCK;
}
long double Physics::Constant::Boltzman()
{
return BOLTZMAN;
}
long double Physics::Dimension::Tera()
{
return 1e12L;
}
long double Physics::Dimension::Giga()
{
return 1e9L;
}
long double Physics::Dimension::Mega()
{
return 1e6L;
}
long double Physics::Dimension::Kilo()
{
return 1e3L;
}
long double Physics::Dimension::Milli()
{
return 1e-3L;
}
long double Physics::Dimension::Micro()
{
return 1e-6L;
}
long double Physics::Dimension::Nano()
{
return 1e-9L;
}
long double Physics::Dimension::Pico()
{
return 1e-12L;
}
long double Physics::Steradian(long double alpha_ranging_from_0_to_2PI)
{
alpha_ranging_from_0_to_2PI = abs(alpha_ranging_from_0_to_2PI);
if (alpha_ranging_from_0_to_2PI > 2 * PI)
{
long double k = alpha_ranging_from_0_to_2PI / (2 * PI);
alpha_ranging_from_0_to_2PI -= long long(k) * (2 * PI);
}
return 2.0 * PI * (1.0 - cos(alpha_ranging_from_0_to_2PI / 2.0));
}
long double Physics::Luminosity(long double area_in_m2, long double temperature_in_K)
{
return area_in_m2 * STEPHAN_BOLTZMAN * pow(temperature_in_K, 4.0);
}
long double Physics::LuminosityOfSphere(long double radius_in_m, long double temperature_in_K)
{
return Luminosity(4.0 * PI * pow(radius_in_m, 2.0), temperature_in_K);
}
long double Physics::WaveLength(long double frequency_in_Hz)
{
return SPEED_OF_LIGHT / frequency_in_Hz;
}
long double Physics::Frequency(long double wavelength_in_m)
{
return SPEED_OF_LIGHT / wavelength_in_m;
}
long double Physics::PeakWaveLength(long double temperature_in_K)
{
return WIENS_DISPLACEMENT / temperature_in_K;
}
long double Physics::SpectralRadiance(long double wavelength_in_m, long double temperature_in_K)
{
return
2.0 * PLANCK * pow(SPEED_OF_LIGHT, 2) /
(pow(wavelength_in_m, 5.0) * (exp(PLANCK * SPEED_OF_LIGHT / (wavelength_in_m * BOLTZMAN * temperature_in_K)) - 1));
}
Graphics.h
#pragma once
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <list>
#include <string>
#include <vector>
#include "Graph.h"
struct GLContextInfo
{
struct {
GLint Major, Minor;
std::string Driver;
std::string ShadingLanguage;
} Version;
std::string Vendor;
std::string Renderer;
std::list<std::string> SupportedExtensions;
std::list<std::string> SupportedGLSLVersions;
struct {
bool IsDebugContext;
bool IsForwardCompatibleContext;
bool IsRobustAccessContext;
} Flags;
struct {
bool IsCore;
bool IsCompatiblity;
} Profile;
};
struct DrawArrays
{
GLenum Mode;
GLint First;
GLsizei Count;
};
struct Vertex_PC
{
glm::vec3 Position;
glm::vec4 Color;
};
class Graphics
{
public:
Graphics();
virtual ~Graphics();
bool Initialize(int width, int height, const Graph_CoordinateSystem& coordinate_system);
void CleanUp();
void Render();
static GLContextInfo GetGLContextInfo();
void DefaultFramebufferResized(int width, int height);
void SetCoordinateSystem(const Graph_CoordinateSystem& coordinate_system);
void AddGraph(const Graph_Y_FX& graph);
void AddGraph(const Graph_X_FY& graph);
void ClearGraphs();
private:
bool InitializeBuffers();
bool InitializePrograms();
bool InitializeVertexArrays();
std::vector<Vertex_PC> GenerateVertices(const Graph_Y_FX& graph);
std::vector<Vertex_PC> GenerateVertices(const Graph_X_FY& graph);
static bool CompileShader(GLuint shader, const std::string& source_code);
static bool LinkProgram(GLuint program, const std::list<GLuint>& list_of_shaders);
static std::string ShaderInfoLog(GLuint shader);
static std::string ShaderTypeName(GLuint shader);
static std::string ProgramInfoLog(GLuint program);
static void __CheckForGLError(const char* filename, int line);
glm::ivec2 m_default_framebuffer_size = glm::ivec2(0, 0);
/* GL objects */
GLuint m_buffer = 0;
GLuint m_program = 0;
GLuint m_vertexarray = 0;
/* coordinate system */
Graph_CoordinateSystem m_coordinate_system;
/* graphs */
std::vector<Graph_Y_FX> m_graphs_y_fx;
std::vector<Graph_X_FY> m_graphs_x_fy;
/* draw calls */
std::vector<DrawArrays> m_drawcalls;
/* flag */
bool m_is_initialized = false;
};
Graphics.cpp
#include "Graphics.h"
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include <iostream>
#define CheckForGLErrors __CheckForGLError(__FILE__, __LINE__);
using namespace std;
using namespace glm;
Graphics::Graphics()
{}
Graphics::~Graphics()
{}
bool Graphics::Initialize(int width, int height, const Graph_CoordinateSystem& coordinate_system)
{
m_default_framebuffer_size = ivec2(width, height);
CheckForGLErrors
/* create all objects */
m_program = glCreateProgram();
glGenBuffers(1, &m_buffer);
glGenVertexArrays(1, &m_vertexarray);
CheckForGLErrors
if (!InitializeBuffers())
return false;
if (!InitializePrograms())
return false;
if (!InitializeVertexArrays())
return false;
CheckForGLErrors
/* successfully initialized */
return true;
}
void Graphics::CleanUp()
{
CheckForGLErrors
/* destroy all objects */
glDeleteProgram(m_program);
m_program = 0;
glDeleteBuffers(1, &m_buffer);
glDeleteVertexArrays(1, &m_vertexarray);
CheckForGLErrors
}
void Graphics::Render()
{
/* skip rendering if the default framebuffer is minimized */
if (m_default_framebuffer_size.x <= 0 || m_default_framebuffer_size.y <= 0)
return;
/* update buffer if coordinate system has changed / graph has been added */
if (!m_is_initialized)
if (!InitializeBuffers())
return;
/* clear frame buffer */
glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
/* draw graphs into a sub-section of the framebuffer */
glUseProgram(m_program);
glBindVertexArray(m_vertexarray);
/* transformation */
glUniformMatrix4fv(0, 1, GL_FALSE, value_ptr(mat4(1)));
/* view */
glUniformMatrix4fv(4, 1, GL_FALSE, value_ptr(mat4(1)));
/* projection */
glUniformMatrix4fv(8, 1, GL_FALSE, value_ptr(
ortho(
m_coordinate_system.XMin,
m_coordinate_system.XMax,
m_coordinate_system.YMin,
m_coordinate_system.YMax
)
));
/* draw each graph */
for (auto& drawcall : m_drawcalls)
glDrawArrays(drawcall.Mode, drawcall.First, drawcall.Count);
glBindVertexArray(0);
glUseProgram(0);
CheckForGLErrors
}
void Graphics::DefaultFramebufferResized(int width, int height)
{
m_default_framebuffer_size = ivec2(width, height);
if (m_default_framebuffer_size.x > 0 && m_default_framebuffer_size.y > 0)
glViewport(0, 0, m_default_framebuffer_size.x, m_default_framebuffer_size.y);
}
void Graphics::SetCoordinateSystem(const Graph_CoordinateSystem & coordinate_system)
{
/* if coordinate system changes, buffers need to be updated */
m_is_initialized = false;
m_coordinate_system = coordinate_system;
}
void Graphics::AddGraph(const Graph_Y_FX& graph)
{
/* if a new graph has been added, buffers need to be updated */
m_is_initialized = false;
m_graphs_y_fx.push_back(graph);
}
void Graphics::AddGraph(const Graph_X_FY & graph)
{
/* if a new graph has been added, buffers need to be updated */
m_is_initialized = false;
m_graphs_x_fy.push_back(graph);
}
void Graphics::ClearGraphs()
{
/* if graphs are removed, buffers need to be updated */
m_is_initialized = false;
m_graphs_x_fy.clear();
m_graphs_y_fx.clear();
}
GLContextInfo Graphics::GetGLContextInfo()
{
GLContextInfo infos;
glGetIntegerv(GL_MAJOR_VERSION, &infos.Version.Major);
glGetIntegerv(GL_MINOR_VERSION, &infos.Version.Minor);
infos.Version.Driver = (const char*)glGetString(GL_VERSION);
infos.Vendor = (const char*)glGetString(GL_VENDOR);
infos.Renderer = (const char*)glGetString(GL_RENDERER);
infos.Version.ShadingLanguage = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
GLint numberofextensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &numberofextensions);
for (int i = 0; i < numberofextensions; i++)
infos.SupportedExtensions.push_back((const char*)glGetStringi(GL_EXTENSIONS, i));
GLint numberofsupportedglslversions = 0;
glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &numberofsupportedglslversions);
for (int i = 0; i < numberofsupportedglslversions; i++)
infos.SupportedGLSLVersions.push_back((const char*)glGetStringi(GL_SHADING_LANGUAGE_VERSION, i));
GLint contextflags = 0;
glGetIntegerv(GL_CONTEXT_FLAGS, &contextflags);
infos.Flags.IsDebugContext = contextflags & GL_CONTEXT_FLAG_DEBUG_BIT;
infos.Flags.IsForwardCompatibleContext = contextflags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT;
infos.Flags.IsRobustAccessContext = contextflags & GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT;
GLint contextprofilemask = 0;
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &contextprofilemask);
infos.Profile.IsCore = contextprofilemask & GL_CONTEXT_CORE_PROFILE_BIT;
infos.Profile.IsCompatiblity = contextprofilemask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT;
return infos;
}
bool Graphics::CompileShader(GLuint shader, const std::string & source_code)
{
if (!glIsShader(shader))
{
std::cout << "ERROR: shader compilation failed, no valid shader specified" << std::endl;
return false;
}
if (source_code.empty())
{
std::cout << "ERROR: shader compilation failed, no source code specified (" << ShaderTypeName(shader) << ")" << std::endl;
return false;
}
const char* sourcearray[] = { source_code.c_str() };
glShaderSource(shader, 1, sourcearray, NULL);
glCompileShader(shader);
// check compile status
GLint status = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
// successfully compiled shader
if (status == GL_TRUE)
return true;
// show compile errors
std::cout << "ERROR: shader compilation failed (" << ShaderTypeName(shader) << ")" << std::endl << ShaderInfoLog(shader) << std::endl;
return false;
}
bool Graphics::LinkProgram(GLuint program, const std::list<GLuint>& list_of_shaders)
{
if (!glIsProgram(program))
{
std::cout << "ERROR: shader linking failed, no valid program specified" << std::endl;
return false;
}
// attach all shaders to the program
for (auto& shader : list_of_shaders)
{
if (glIsShader(shader))
glAttachShader(program, shader);
}
// link program
glLinkProgram(program);
// detach all shaders again
for (auto& shader : list_of_shaders)
{
if (glIsShader(shader))
glDetachShader(program, shader);
}
GLint status = 0;
glGetProgramiv(program, GL_LINK_STATUS, &status);
// successfully linked program
if (status == GL_TRUE)
return true;
// show link errors
cout << "ERROR: shader linking failed" << endl << ProgramInfoLog(program) << endl;
return false;
}
std::string Graphics::ShaderInfoLog(GLuint shader)
{
if (glIsShader(shader))
{
GLint logsize = 0;
GLchar infolog[1024] = { 0 };
glGetShaderInfoLog(shader, 1024, &logsize, infolog);
return std::string(infolog);
}
return "invalid shader";
}
std::string Graphics::ShaderTypeName(GLuint shader)
{
if (glIsShader(shader))
{
GLint type = 0;
glGetShaderiv(shader, GL_SHADER_TYPE, &type);
if (type == GL_VERTEX_SHADER)
return "Vertex Shader";
if (type == GL_TESS_CONTROL_SHADER)
return "Tessellation Control Shader";
if (type == GL_TESS_EVALUATION_SHADER)
return "Tessellation Evaluation Shader";
if (type == GL_GEOMETRY_SHADER)
return "Geometry Shader";
if (type == GL_FRAGMENT_SHADER)
return "Fragment Shader";
if (type == GL_COMPUTE_SHADER)
return "Compute Shader";
}
return "invalid shader";
}
std::string Graphics::ProgramInfoLog(GLuint program)
{
if (glIsProgram(program))
{
GLint logsize = 0;
GLchar infolog[1024] = { 0 };
glGetProgramInfoLog(program, 1024, &logsize, infolog);
return std::string(infolog);
}
return "invalid program";
}
void Graphics::__CheckForGLError(const char* filename, int line)
{
for (GLenum error; (error = glGetError()) != GL_NO_ERROR;)
{
cout << (char)7 << "OpenGL Error:\t" << filename << " line " << line << "\n\t";
if (error == GL_INVALID_ENUM)
cout << "GL_INVALID_ENUM";
if (error == GL_INVALID_VALUE)
cout << "GL_INVALID_VALUE";
if (error == GL_INVALID_OPERATION)
cout << "GL_INVALID_OPERATION";
if (error == GL_STACK_OVERFLOW)
cout << "GL_STACK_OVERFLOW";
if (error == GL_STACK_UNDERFLOW)
cout << "GL_STACK_UNDERFLOW";
if (error == GL_OUT_OF_MEMORY)
cout << "GL_OUT_OF_MEMORY";
if (error == GL_INVALID_FRAMEBUFFER_OPERATION)
cout << "GL_INVALID_FRAMEBUFFER_OPERATION";
if (error == GL_CONTEXT_LOST)
cout << "GL_CONTEXT_LOST";
cout << endl << endl; /*play sound*/
cin.get();
}
}
bool Graphics::InitializeBuffers()
{
/* array of vertices */
vector<vector<Vertex_PC>> vertices_list;
/* generate vertices for coordinate axes */
Graph_X_FY x_axis;
x_axis.Color = vec4(0, 0, 0, 0); /* black */
Graph_Y_FX y_axis;
y_axis.Color = vec4(0, 0, 0, 0); /* black */
vertices_list.push_back(GenerateVertices(x_axis));
vertices_list.push_back(GenerateVertices(y_axis));
/* generate vertices for x = f(y) graphs */
for (auto& graph : m_graphs_x_fy)
vertices_list.push_back(GenerateVertices(graph));
/* generate vertices for y = f(x) graphs */
for (auto& graph : m_graphs_y_fx)
vertices_list.push_back(GenerateVertices(graph));
/* finally create draw calls for each graph */
m_drawcalls.clear();
vector<Vertex_PC> all_vertices;
for (auto& vertices : vertices_list)
{
m_drawcalls.push_back({ GL_LINE_STRIP, (GLint)all_vertices.size(), (GLsizei)vertices.size() });
all_vertices.insert(all_vertices.end(), vertices.begin(), vertices.end());
}
/* put vertices into buffer */
glBindBuffer(GL_ARRAY_BUFFER, m_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex_PC) * all_vertices.size(), all_vertices.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
/* buffer is up-to-date */
m_is_initialized = true;
return true;
}
bool Graphics::InitializePrograms()
{
bool success = true;
string vertexshader_source;
string fragmentshader_source;
GLuint vertexshader = 0;
GLuint fragmentshader = 0;
/* program_pc (to draw vertices with position + color) */
/************************************************************************/
vertexshader_source = {
"#version 450 core\n"
"layout (location = 0) uniform mat4 Model = mat4(1);"
"layout (location = 4) uniform mat4 View = mat4(1);"
"layout (location = 8) uniform mat4 Projection = mat4(1);"
"layout (location = 0) in vec3 in_position;"
"layout (location = 1) in vec4 in_color;"
"smooth out vec4 color;"
"void main() {"
"mat4 MVP = Projection * View * Model;"
"gl_Position = MVP * vec4(in_position, 1);"
"color = in_color;"
"}"
};
fragmentshader_source = {
"#version 450 core\n"
"smooth in vec4 color;"
"layout (location = 0) out vec4 out_color;"
"void main() {"
"out_color = color;"
"}"
};
success = true;
vertexshader = glCreateShader(GL_VERTEX_SHADER);
fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
if (!CompileShader(vertexshader, vertexshader_source))
success = false;
else if (!CompileShader(fragmentshader, fragmentshader_source))
success = false;
else if (!LinkProgram(m_program, { vertexshader, fragmentshader }))
success = false;
glDeleteShader(vertexshader);
glDeleteShader(fragmentshader);
if (!success)
return false;
/************************************************************************/
return true;
}
bool Graphics::InitializeVertexArrays()
{
glBindVertexArray(m_vertexarray);
glBindBuffer(GL_ARRAY_BUFFER, m_buffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex_PC), reinterpret_cast<GLvoid*>(offsetof(Vertex_PC, Position)));
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex_PC), reinterpret_cast<GLvoid*>(offsetof(Vertex_PC, Color)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindVertexArray(0);
return true;
}
std::vector<Vertex_PC> Graphics::GenerateVertices(const Graph_Y_FX & graph)
{
std::vector<Vertex_PC> vertices;
for (float x = m_coordinate_system.XMin; x <= m_coordinate_system.XMax; x += m_coordinate_system.XStep)
{
float y = graph.Function(x);
float z = 0;
vertices.push_back( { { x, y, z }, graph.Color } );
}
return vertices;
}
std::vector<Vertex_PC> Graphics::GenerateVertices(const Graph_X_FY & graph)
{
std::vector<Vertex_PC> vertices;
for (float y = m_coordinate_system.YMin; y <= m_coordinate_system.YMax; y += m_coordinate_system.YStep)
{
float x = graph.Function(y);
float z = 0;
vertices.push_back({ { x, y, z }, graph.Color });
}
return vertices;
}