Skip to content

Instantly share code, notes, and snippets.

@giuliohome
Last active January 16, 2024 22:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save giuliohome/2b86e64a0186e307f53c3746bfaa6102 to your computer and use it in GitHub Desktop.
Save giuliohome/2b86e64a0186e307f53c3746bfaa6102 to your computer and use it in GitHub Desktop.
example for GTK GL Area issue
/* Open GL Area
*
* Gtk::GLArea is a widget that allows custom drawing using OpenGL calls.
*
*/
#include <iostream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <gtkmm.h>
#include <gtkmm/application.h>
#include <giomm/resource.h>
#include <epoxy/gl.h>
// #include <GL/glew.h>
// #include <glew_tail.h>
// #include <glm/glm.hpp>
//#include <GLFW/glfw3.h>
#include <texture.hpp>
#include <objloader.hpp>
#include <shader.hpp>
using std::cerr;
using std::endl;
using std::string;
enum {
X_AXIS,
Y_AXIS,
Z_AXIS,
N_AXIS
};
class Example_GLArea : public Gtk::Window
{
public:
Example_GLArea();
~Example_GLArea() override;
protected:
Gtk::Box m_VBox {Gtk::Orientation::VERTICAL, false};
Gtk::GLArea m_GLArea;
Gtk::Box m_Controls {Gtk::Orientation::VERTICAL, false};
Gtk::Button m_Button {"Quit"};
GLuint m_Vao {0};
GLuint m_Buffer {0};
GLuint m_Program {0};
GLuint m_Mvp {0};
GLuint vertexbuffer;
GLuint uvbuffer;
GLuint normalbuffer;
std::vector<float> m_RotationAngles;
void on_axis_value_change(int axis, const Glib::RefPtr<Gtk::Adjustment>& adj);
void realize();
void unrealize();
bool render(const Glib::RefPtr<Gdk::GLContext>& context);
Gtk::Box* create_axis_slider_box(int axis);
void init_buffers();
void draw_triangle();
};
Gtk::Window* do_glarea()
{
return new Example_GLArea();
}
Example_GLArea::Example_GLArea() : m_RotationAngles(N_AXIS, 0.0f)
{
set_title("GL Area");
set_default_size(400, 500);
m_VBox.set_margin(12);
m_VBox.set_spacing(1);
set_child(m_VBox);
m_GLArea.set_expand(true);
m_GLArea.set_size_request(400, 500);
m_GLArea.set_auto_render(true);
m_GLArea.set_has_depth_buffer(true);
m_GLArea.set_has_stencil_buffer(true);
m_VBox.append(m_GLArea);
// Connect gl area signals
m_GLArea.signal_realize().connect(sigc::mem_fun(*this, &Example_GLArea::realize));
// Important that the unrealize signal calls our handler to clean up
// GL resources _before_ the default unrealize handler is called (the "false")
m_GLArea.signal_unrealize().connect(sigc::mem_fun(*this, &Example_GLArea::unrealize), false);
m_GLArea.signal_render().connect(sigc::mem_fun(*this, &Example_GLArea::render), false);
m_GLArea.set_hexpand(true);
m_VBox.append(m_Controls);
m_Controls.set_hexpand(true);
for(int i = 0 ; i < N_AXIS ; ++i)
{
auto sliderBox = create_axis_slider_box(i);
m_Controls.append(*sliderBox);
}
m_Button.set_hexpand(true);
m_VBox.append(m_Button);
// Connect clicked to close of window
m_Button.signal_clicked().connect(sigc::mem_fun(*this, &Gtk::Window::close));
}
Example_GLArea::~Example_GLArea()
{
}
void Example_GLArea::on_axis_value_change(int axis, const Glib::RefPtr<Gtk::Adjustment>& adj)
{
m_RotationAngles[axis] = adj->get_value();
m_GLArea.queue_draw();
}
void Example_GLArea::realize()
{
m_GLArea.make_current();
try
{
m_GLArea.throw_if_error();
init_buffers();
}
catch(const Gdk::GLError& gle)
{
cerr << "An error occured making the context current during realize:" << endl;
cerr << gle.domain() << "-" << gle.code() << "-" << gle.what() << endl;
}
}
void Example_GLArea::unrealize()
{
m_GLArea.make_current();
try
{
m_GLArea.throw_if_error();
// Delete buffers and program
glDeleteBuffers (1, &vertexbuffer);
glDeleteBuffers (1, &uvbuffer);
glDeleteBuffers (1, &normalbuffer);
glDeleteProgram(m_Program);
}
catch(const Gdk::GLError& gle)
{
cerr << "An error occured making the context current during unrealize" << endl;
cerr << gle.domain() << "-" << gle.code() << "-" << gle.what() << endl;
}
}
bool Example_GLArea::render(const Glib::RefPtr<Gdk::GLContext>& /* context */)
{
try
{
m_GLArea.throw_if_error();
draw_triangle();
glFlush();
return true;
}
catch(const Gdk::GLError& gle)
{
cerr << "An error occurred in the render callback of the GLArea" << endl;
cerr << gle.domain() << "-" << gle.code() << "-" << gle.what() << endl;
return false;
}
}
Gtk::Box* Example_GLArea::create_axis_slider_box(int axis)
{
auto box = Gtk::manage(new Gtk::Box{Gtk::Orientation::HORIZONTAL, false});
const char* text;
switch(axis)
{
case X_AXIS:
{
text = "X axis";
break;
}
case Y_AXIS:
{
text = "Y axis";
break;
}
case Z_AXIS:
{
text = "Z axis";
break;
}
default:
{
g_assert_not_reached();
}
}
auto label = Gtk::manage(new Gtk::Label{text});
box->append(*label);
label->show();
auto adj = Gtk::Adjustment::create(0.0, 0.0, 360.0, 1, 12.0, 0.0);
adj->signal_value_changed().connect(
sigc::bind(sigc::mem_fun(*this, &Example_GLArea::on_axis_value_change), axis, adj)
);
auto slider = Gtk::manage(new Gtk::Scale{adj, Gtk::Orientation::HORIZONTAL});
box->append(*slider);
slider->set_hexpand(true);
slider->show();
box->show();
return box;
}
GLuint ModelMatrixID;
GLuint ViewMatrixID;
GLuint MatrixID;
glm::mat4 ModelMatrix;
glm::mat4 ViewMatrix;
std::vector<glm::vec3> vertices;
std::vector<glm::vec2> uvs;
std::vector<glm::vec3> normals;
GLuint LightID;
GLuint TextureID;
GLuint Texture;
glm::vec3 lightPos;
unsigned int fbo;
void Example_GLArea::init_buffers()
{
// Dark blue background
glClearColor(0.0f, 0.0f, 0.4f, 1.0f);
// Enable depth test
glEnable(GL_DEPTH_TEST);
// Accept fragment if it closer to the camera than the former one
glDepthFunc(GL_LESS);
// Cull triangles which normal is not towards the camera
// glEnable(GL_CULL_FACE);
//GLuint vao = 0;
//glGenVertexArrays( 1, &vao );
//glBindVertexArray( vao );
GLint default_VAO;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &default_VAO);
std::cerr << "default VAO " << std::to_string(default_VAO) << "\n";
std::cerr << "my VAO " << std::to_string(m_Vao) << "\n";
glGenVertexArrays(1, &m_Vao);
glBindVertexArray(m_Vao);
// Read our .obj file
bool res = loadOBJ("suzanne.obj", vertices, uvs, normals);
// Load it into a VBO
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
glBindBuffer (GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &uvbuffer);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(glm::vec2), &uvs[0], GL_STATIC_DRAW);
glBindBuffer (GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &normalbuffer);
glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), &normals[0], GL_STATIC_DRAW);
glBindBuffer (GL_ARRAY_BUFFER, 0);
m_Program = glCreateProgram();
// Create and compile our GLSL program from the shaders
LoadShaders(m_Program, "StandardShading.vertexshader", "StandardShading.fragmentshader" );
// Get a handle for our "MVP" uniform
MatrixID = glGetUniformLocation(m_Program, "MVP");
ViewMatrixID = glGetUniformLocation(m_Program, "V");
ModelMatrixID = glGetUniformLocation(m_Program, "M");
//GLint color_location;
//color_location = glGetAttribLocation (m_Program, "fragment_color");
//GLint color_index = color_location;
//std::cerr << "Color Location " << std::to_string(color_index) << "\n";
// glEnableVertexAttribArray (color_index);
// GL_INVALID_VALUE in glEnableVertexAttribArray(index)
// Load the texture
Texture = loadDDS("uvmap.DDS");
// Get a handle for our "myTextureSampler" uniform
TextureID = glGetUniformLocation(m_Program, "myTextureSampler");
// Get a handle for our "LightPosition" uniform
glUseProgram(m_Program);
LightID = glGetUniformLocation(m_Program, "LightPosition_worldspace");
}
static void compute_mvp(float *res,
float phi,
float theta,
float psi)
{
float x {phi * ((float)G_PI / 180.f)};
float y {theta * ((float)G_PI / 180.f)};
float z {psi * ((float)G_PI / 180.f)};
float c1 {cosf (x)};
float s1 {sinf (x)};
float c2 {cosf (y)};
float s2 {sinf (y)};
float c3 {cosf (z)};
float s3 {sinf (z)};
float c3c2 {c3 * c2};
float s3c1 {s3 * c1};
float c3s2s1 {c3 * s2 * s1};
float s3s1 {s3 * s1};
float c3s2c1 {c3 * s2 * c1};
float s3c2 {s3 * c2};
float c3c1 {c3 * c1};
float s3s2s1 {s3 * s2 * s1};
float c3s1 {c3 * s1};
float s3s2c1 {s3 * s2 * c1};
float c2s1 {c2 * s1};
float c2c1 {c2 * c1};
/* apply all three rotations using the three matrices:
*
* ⎡ c3 s3 0 ⎤ ⎡ c2 0 -s2 ⎤ ⎡ 1 0 0 ⎤
* ⎢ -s3 c3 0 ⎥ ⎢ 0 1 0 ⎥ ⎢ 0 c1 s1 ⎥
* ⎣ 0 0 1 ⎦ ⎣ s2 0 c2 ⎦ ⎣ 0 -s1 c1 ⎦
*/
res[0] = c3c2; res[4] = s3c1 + c3s2s1; res[8] = s3s1 - c3s2c1; res[12] = 0.f;
res[1] = -s3c2; res[5] = c3c1 - s3s2s1; res[9] = c3s1 + s3s2c1; res[13] = 0.f;
res[2] = s2; res[6] = -c2s1; res[10] = c2c1; res[14] = 0.f;
res[3] = 0.f; res[7] = 0.f; res[11] = 0.f; res[15] = 1.f;
}
void Example_GLArea::draw_triangle()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(m_Program);
float mvp[16];
compute_mvp(mvp,
m_RotationAngles[X_AXIS],
m_RotationAngles[Y_AXIS],
m_RotationAngles[Z_AXIS]);
// // Get a handle for our "MVP" uniform
ModelMatrix = glm::mat4(1);
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &mvp[0]);
glUniformMatrix4fv(ModelMatrixID, 1, GL_FALSE, &ModelMatrix[0][0]);
glUniformMatrix4fv(ModelMatrixID, 1, GL_FALSE, &ModelMatrix[0][0]);
glUniformMatrix4fv(ViewMatrixID, 1, GL_FALSE, &ViewMatrix[0][0]);
lightPos = glm::vec3(0,0,100);
glUniform3f(LightID, lightPos.x, lightPos.y, lightPos.z);
// Bind our texture in Texture Unit 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D , Texture);
// Set our "myTextureSampler" sampler to use Texture Unit 0
glUniform1i(TextureID, 0);
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(
0, // attribute
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// 2nd attribute buffer : UVs
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glVertexAttribPointer(
1, // attribute
2, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// 3rd attribute buffer : normals
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
glVertexAttribPointer(
2, // attribute
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// Draw the triangles !
glDrawArrays(GL_TRIANGLES, 0, vertices.size() );
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
int main(int argc, char* argv[])
{
auto app = Gtk::Application::create();
return app->make_window_and_run<Example_GLArea>(argc, argv);
}
#version 330 core
// Interpolated values from the vertex shaders
in vec2 UV;
in vec3 Position_worldspace;
in vec3 Normal_cameraspace;
in vec3 EyeDirection_cameraspace;
in vec3 LightDirection_cameraspace;
// Ouput data
//out vec3 color;
out vec4 fragment_color;
// Values that stay constant for the whole mesh.
uniform sampler2D myTextureSampler;
uniform mat4 MV;
uniform vec3 LightPosition_worldspace;
void main(){
// Light emission properties
// You probably want to put them as uniforms
vec3 LightColor = vec3(1,1,1);
float LightPower = 100.0f;
// Material properties
vec3 MaterialDiffuseColor = texture( myTextureSampler, UV ).rgb;
vec3 MaterialAmbientColor = vec3(0.9,0.9,0.9) * MaterialDiffuseColor;
vec3 MaterialSpecularColor = vec3(0.3,0.3,0.3);
// Distance to the light
float distance = length( LightPosition_worldspace - Position_worldspace );
// Normal of the computed fragment, in camera space
vec3 n = normalize( Normal_cameraspace );
// Direction of the light (from the fragment to the light)
vec3 l = normalize( LightDirection_cameraspace );
// Cosine of the angle between the normal and the light direction,
// clamped above 0
// - light is at the vertical of the triangle -> 1
// - light is perpendicular to the triangle -> 0
// - light is behind the triangle -> 0
float cosTheta = clamp( dot( n,l ), 0, 1);
// Eye vector (towards the camera)
vec3 E = normalize(EyeDirection_cameraspace);
// Direction in which the triangle reflects the light
vec3 R = reflect(-l,n);
// Cosine of the angle between the Eye vector and the Reflect vector,
// clamped to 0
// - Looking into the reflection -> 1
// - Looking elsewhere -> < 1
float cosAlpha = clamp( dot( E,R ), 0, 1);
vec3 color =
// Ambient : simulates indirect lighting
MaterialAmbientColor +
// Diffuse : "color" of the object
MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance*distance) +
// Specular : reflective highlight, like a mirror
MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha,5) / (distance*distance);
fragment_color = vec4 (color, 1);
}
#version 330 core
// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;
layout(location = 2) in vec3 vertexNormal_modelspace;
// Output data ; will be interpolated for each fragment.
out vec2 UV;
out vec3 Position_worldspace;
out vec3 Normal_cameraspace;
out vec3 EyeDirection_cameraspace;
out vec3 LightDirection_cameraspace;
// Values that stay constant for the whole mesh.
uniform mat4 MVP;
uniform mat4 V;
uniform mat4 M;
uniform vec3 LightPosition_worldspace;
void main(){
// Output position of the vertex, in clip space : MVP * position
gl_Position = MVP * vec4(vertexPosition_modelspace,1);
// Position of the vertex, in worldspace : M * position
Position_worldspace = (M * vec4(vertexPosition_modelspace,1)).xyz;
// Vector that goes from the vertex to the camera, in camera space.
// In camera space, the camera is at the origin (0,0,0).
vec3 vertexPosition_cameraspace = ( V * M * vec4(vertexPosition_modelspace,1)).xyz;
EyeDirection_cameraspace = vec3(0,0,0) - vertexPosition_cameraspace;
// Vector that goes from the vertex to the light, in camera space. M is ommited because it's identity.
vec3 LightPosition_cameraspace = ( V * vec4(LightPosition_worldspace,1)).xyz;
LightDirection_cameraspace = LightPosition_cameraspace + EyeDirection_cameraspace;
// Normal of the the vertex, in camera space
Normal_cameraspace = ( V * M * vec4(vertexNormal_modelspace,0)).xyz; // Only correct if ModelMatrix does not scale the model ! Use its inverse transpose if not.
// UV of the vertex. No special space for this one.
UV = vertexUV;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment