Using various code samples I've created a trivial OpenGL app, project dependencies below:
[dependencies]
epoxy = "0.1.0"
gl = "0.14.0"
gtk = { version = "0.7.3", package = "gtk4", features = ["v4_12"] }
libloading = "0.8.1"
The logics is also trivial: initialize a window using GTK+ v4.12 and an OpenGL context and try to draw a triangle.
main.rs
mod renderer;
use gtk::prelude::*;
use gtk::{glib, Application, ApplicationWindow};
use renderer::{on_render, on_realize};
const APP_ID: &str = "com.noobie.teapot";
const APP_NAME: &str = "Teapot";
fn main() -> glib::ExitCode {
init_gl();
// Create a new application
let app = Application::builder()
.application_id(APP_ID)
.build();
// Connect to "activate" signal of `app`
app.connect_activate(on_activate);
// Run the application
app.run()
}
fn init_gl() {
#[cfg(target_os = "macos")]
let library = unsafe { libloading::os::unix::Library::new("libepoxy.0.dylib") }.unwrap();
#[cfg(all(unix, not(target_os = "macos")))]
let library = unsafe { libloading::os::unix::Library::new("libepoxy.so.0") }.unwrap();
#[cfg(windows)]
let library = libloading::os::windows::Library::open_already_loaded("epoxy-0.dll").unwrap();
epoxy::load_with(|name| {
unsafe { library.get::<_>(name.as_bytes()) }
.map(|symbol| *symbol)
.unwrap_or(std::ptr::null())
});
gl::load_with(|s| epoxy::get_proc_addr(s));
}
fn on_activate(app: &Application) {
// Create a window and set the title
let window = ApplicationWindow::builder()
.application(app)
.title(APP_NAME)
.default_width(800)
.default_height(600)
.build();
let container = gtk::Paned::builder()
.orientation(gtk::Orientation::Vertical)
.shrink_end_child(true)
.build();
let gl_area = gtk::GLArea::builder()
.auto_render(false)
.height_request(8)
.build();
gl_area.connect_realize(on_realize);
gl_area.connect_render(on_render);
container.set_start_child(Some(&gl_area));
let grid_view = gtk::GridView::builder()
.height_request(4)
.build();
container.set_end_child(Some(&grid_view));
window.set_child(Some(&container));
// Present window
window.present();
}
renderer.rs
use std::mem::{size_of_val, size_of};
use gtk::prelude::*;
use gtk::{glib::Propagation, gdk::GLContext};
type Vertex = [f32; 3];
const VERTICES: [Vertex; 3] = [
[-0.5, -0.5, 0.0],
[0.5, -0.5, 0.0],
[0.0, 0.5, 0.0]
];
const VERT_SHADER: &str = r#"#version 330 core
layout (location = 0) in vec3 pos;
void main() {
gl_Position = vec4(pos.x, pos.y, pos.z, 1.0);
}
"#;
const FRAG_SHADER: &str = r#"#version 330 core
out vec4 final_color;
void main() {
final_color = vec4(1.0, 0.5, 0.2, 1.0);
}
"#;
pub fn on_realize(_gl_area: >k::GLArea) {
}
pub fn on_render(_gl_area: >k::GLArea, _ctx: &GLContext) -> Propagation {
unsafe {
gl::ClearColor(0.3, 0.3, 0.3, 1.0);
let mut vao = 0;
gl::GenVertexArrays(1, &mut vao);
assert_ne!(vao, 0);
let mut vbo = 0;
gl::GenBuffers(1, &mut vbo);
assert_ne!(vbo, 0);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(gl::ARRAY_BUFFER, size_of_val(&VERTICES) as isize, VERTICES.as_ptr().cast(), gl::STATIC_DRAW);
gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, size_of::<Vertex>().try_into().unwrap(), 0 as *const _);
gl::EnableVertexAttribArray(0);
let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER);
assert_ne!(vertex_shader, 0);
gl::ShaderSource(
vertex_shader,
1,
&(VERT_SHADER.as_bytes().as_ptr().cast()),
&(VERT_SHADER.len().try_into().unwrap()),
);
gl::CompileShader(vertex_shader);
let mut success = 0;
gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success);
if success == 0 {
let mut v: Vec<u8> = Vec::with_capacity(1024);
let mut log_len = 0_i32;
gl::GetShaderInfoLog(
vertex_shader,
1024,
&mut log_len,
v.as_mut_ptr().cast(),
);
v.set_len(log_len.try_into().unwrap());
panic!("Vertex Compile Error: {}", String::from_utf8_lossy(&v));
}
let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
assert_ne!(fragment_shader, 0);
gl::ShaderSource(
fragment_shader,
1,
&(FRAG_SHADER.as_bytes().as_ptr().cast()),
&(FRAG_SHADER.len().try_into().unwrap()),
);
gl::CompileShader(fragment_shader);
gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success);
if success == 0 {
let mut v: Vec<u8> = Vec::with_capacity(1024);
let mut log_len = 0_i32;
gl::GetShaderInfoLog(
fragment_shader,
1024,
&mut log_len,
v.as_mut_ptr().cast(),
);
v.set_len(log_len.try_into().unwrap());
panic!("Fragment Compile Error: {}", String::from_utf8_lossy(&v));
}
let shader_program = gl::CreateProgram();
gl::AttachShader(shader_program, vertex_shader);
gl::AttachShader(shader_program, fragment_shader);
gl::LinkProgram(shader_program);
let mut success = 0;
gl::GetProgramiv(shader_program, gl::LINK_STATUS, &mut success);
if success == 0 {
let mut v: Vec<u8> = Vec::with_capacity(1024);
let mut log_len = 0_i32;
gl::GetProgramInfoLog(
shader_program,
1024,
&mut log_len,
v.as_mut_ptr().cast(),
);
v.set_len(log_len.try_into().unwrap());
panic!("Program Link Error: {}", String::from_utf8_lossy(&v));
}
gl::DeleteShader(vertex_shader);
gl::DeleteShader(fragment_shader);
gl::UseProgram(shader_program);
gl::Clear(gl::COLOR_BUFFER_BIT);
gl::DrawArrays(gl::TRIANGLES, 0, 3);
_gl_area.queue_draw();
}
return Propagation::Proceed;
}
However, when I launch my program it just shows a solid gray color:
I am a total noobie in OpenGL, so I can't even get what's the problem. There are no any errors in the console output.
The reason you only see gray is because that is your current clear color and the triangle is not drawn on top.
As far as I can tell there are multiple issues with your current implementation and I am not sure if it would make sense to go through all of them.
If you are only looking to make it work, here is a very basic sample for how renderer.rs could look:
which is heavily based on the official triangle example of
gl-rs
.However @BDL's comment among other things still applies of course and is not considered here, which is why I would advise you to look into the (
glium
based) OpenGL example ofgtk4-rs
.