Last active
January 16, 2024 22:37
-
-
Save giuliohome/2b86e64a0186e307f53c3746bfaa6102 to your computer and use it in GitHub Desktop.
example for GTK GL Area issue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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