librashader/librashader-runtime-gl/src/hello_triangle.rs

402 lines
13 KiB
Rust
Raw Normal View History

use std::convert::TryInto;
2022-11-19 17:55:49 +11:00
use std::ffi::{c_void, CStr};
use std::sync::mpsc::Receiver;
use glfw;
use glfw::{Context, Glfw, Window, WindowEvent};
use gl;
2022-11-19 17:55:49 +11:00
use gl::types::{GLchar, GLenum, GLint, GLsizei, GLuint};
use glfw::Key::P;
2022-11-19 17:55:49 +11:00
use crate::FilterChain;
use crate::util::{GlImage, Size, Viewport};
const WIDTH: u32 = 900;
const HEIGHT: u32 = 700;
const TITLE: &str = "Hello From OpenGL World!";
pub fn compile_program(vertex: &str, fragment: &str) -> GLuint {
let vertex_shader = unsafe { gl::CreateShader(gl::VERTEX_SHADER) };
unsafe {
gl::ShaderSource(vertex_shader, 1, &vertex.as_bytes().as_ptr().cast(), &vertex.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 log_len = 0_i32;
// gl::GetShaderiv(vertex_shader, gl::INFO_LOG_LENGTH, &mut log_len);
// let mut v: Vec<u8> = Vec::with_capacity(log_len as usize);
// gl::GetShaderInfoLog(vertex_shader, log_len, &mut log_len, v.as_mut_ptr().cast());
let mut v: Vec<u8> = Vec::with_capacity(1024);
gl::GetShaderInfoLog(vertex_shader, 1024, &mut log_len, v.as_mut_ptr().cast());
v.set_len(log_len.try_into().unwrap());
panic!("Vertex Shader Compile Error: {}", String::from_utf8_lossy(&v));
}
}
let fragment_shader = unsafe { gl::CreateShader(gl::FRAGMENT_SHADER) };
unsafe {
gl::ShaderSource(fragment_shader, 1, &fragment.as_bytes().as_ptr().cast(), &fragment.len().try_into().unwrap());
gl::CompileShader(fragment_shader);
let mut success = 0;
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 Shader Compile Error: {}", String::from_utf8_lossy(&v));
}
}
let shader_program = unsafe { gl::CreateProgram() };
unsafe {
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::DetachShader(shader_program, vertex_shader);
gl::DetachShader(shader_program, fragment_shader);
gl::DeleteShader(vertex_shader);
gl::DeleteShader(fragment_shader);
}
shader_program
}
2022-11-19 17:55:49 +11:00
extern "system" fn debug_callback(source: GLenum, err_type: GLenum, id: GLuint, severity: GLenum, length: GLsizei, message: *const GLchar, _user: *mut c_void) {
unsafe {
let message = CStr::from_ptr(message);
eprintln!("{:?}", message);
}
}
pub fn setup() -> (Glfw, Window, Receiver<(f64, WindowEvent)>, GLuint, GLuint) {
let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
glfw.window_hint(glfw::WindowHint::ContextVersion(3, 3));
glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core));
glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true));
glfw.window_hint(glfw::WindowHint::Resizable(false));
2022-11-19 17:55:49 +11:00
glfw.window_hint(glfw::WindowHint::OpenGlDebugContext(true));
let (mut window, events) = glfw.create_window(WIDTH, HEIGHT, TITLE, glfw::WindowMode::Windowed).unwrap();
let (screen_width, screen_height) = window.get_framebuffer_size();
window.make_current();
window.set_key_polling(true);
gl::load_with(|ptr| window.get_proc_address(ptr) as *const _);
2022-11-19 17:55:49 +11:00
unsafe {
gl::Enable(gl::DEBUG_OUTPUT);
gl::Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS);
gl::DebugMessageCallback(Some(debug_callback), std::ptr::null_mut());
gl::DebugMessageControl(gl::DONT_CARE,
gl::DONT_CARE,
gl::DONT_CARE,
0, std::ptr::null(), gl::TRUE);
}
unsafe {
gl::Viewport(0, 0, screen_width, screen_height);
clear_color(Color(0.4, 0.4, 0.4, 1.0));
}
// -------------------------------------------
const VERT_SHADER: &str = "#version 330 core
layout (location = 0) in vec3 Position;
layout (location = 1) in vec3 Color;
out VS_OUTPUT {
vec3 Color;
} OUT;
void main()
{
gl_Position = vec4(Position, 1.0);
OUT.Color = Color;
}";
const FRAG_SHADER: &str = "#version 330 core
in VS_OUTPUT {
vec3 Color;
} IN;
layout(location = 0) out vec4 Color;
void main()
{
Color = vec4(IN.Color, 1.0f);
}";
let shader_program = compile_program(VERT_SHADER, FRAG_SHADER);
let vertices = &[
// positions // colors
0.5f32, -0.5, 0.0, 1.0, 0.0, 0.0, // bottom right
-0.5, -0.5, 0.0, 0.0, 1.0, 0.0, // bottom left
0.0, 0.5, 0.0, 0.0, 0.0, 1.0 // top
];
let mut vbo: gl::types::GLuint = 0;
unsafe {
gl::GenBuffers(1, &mut vbo);
}
unsafe {
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(
gl::ARRAY_BUFFER, // target
(vertices.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr, // size of data in bytes
vertices.as_ptr() as *const gl::types::GLvoid, // pointer to data
gl::STATIC_DRAW, // usage
);
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
}
// set up vertex array object
let mut vao: gl::types::GLuint = 0;
unsafe {
gl::GenVertexArrays(1, &mut vao);
}
unsafe {
gl::BindVertexArray(vao);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::EnableVertexAttribArray(0); // this is "layout (location = 0)" in vertex shader
gl::VertexAttribPointer(
0, // index of the generic vertex attribute ("layout (location = 0)")
3, // the number of components per generic vertex attribute
gl::FLOAT, // data type
gl::FALSE, // normalized (int-to-float conversion)
(6 * std::mem::size_of::<f32>()) as gl::types::GLint, // stride (byte offset between consecutive attributes)
std::ptr::null(), // offset of the first component
);
gl::EnableVertexAttribArray(1); // this is "layout (location = 0)" in vertex shader
gl::VertexAttribPointer(
1, // index of the generic vertex attribute ("layout (location = 0)")
3, // the number of components per generic vertex attribute
gl::FLOAT, // data type
gl::FALSE, // normalized (int-to-float conversion)
(6 * std::mem::size_of::<f32>()) as gl::types::GLint, // stride (byte offset between consecutive attributes)
(3 * std::mem::size_of::<f32>()) as *const gl::types::GLvoid, // offset of the first component
);
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
gl::BindVertexArray(0);
}
// set up shared state for window
unsafe {
gl::Viewport(0, 0, 900, 700);
gl::ClearColor(0.3, 0.3, 0.5, 1.0);
}
// -------------------------------------------
println!("OpenGL version: {}", gl_get_string(gl::VERSION));
println!("GLSL version: {}", gl_get_string(gl::SHADING_LANGUAGE_VERSION));
(glfw, window, events, shader_program, vao)
}
2022-11-19 17:55:49 +11:00
pub fn do_loop(mut glfw: Glfw, mut window: Window, events: Receiver<(f64, WindowEvent)>, triangle_program: GLuint, vao: GLuint, filter: &mut FilterChain) {
let mut framebuffer_handle = 0;
let mut rendered_texture = 0;
let mut quad_vbuf = 0;
unsafe {
// do frmaebuffer
gl::GenFramebuffers(1, &mut framebuffer_handle);
gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer_handle);
// make tetxure
gl::GenTextures(1, &mut rendered_texture);
gl::BindTexture(gl::TEXTURE_2D, rendered_texture);
// empty image
2022-11-19 17:55:49 +11:00
gl::TexStorage2D(gl::TEXTURE_2D, 1, gl::RGBA8, WIDTH as GLsizei, HEIGHT as GLsizei);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint);
// set color attachment
2022-11-19 17:55:49 +11:00
gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, rendered_texture, 0);
let buffers = [gl::COLOR_ATTACHMENT0];
gl::DrawBuffers(1, buffers.as_ptr());
if gl::CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE {
panic!("failed to create fbo")
}
let fullscreen_fbo = [
-1.0f32, -1.0, 0.0,
1.0, -1.0, 0.0,
-1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
1.0, -1.0, 0.0,
1.0, 1.0, 0.0,
];
gl::GenBuffers(1, &mut quad_vbuf);
gl::BindBuffer(gl::ARRAY_BUFFER, quad_vbuf);
gl::BufferData(
gl::ARRAY_BUFFER, // target
(fullscreen_fbo.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr, // size of data in bytes
fullscreen_fbo.as_ptr() as *const gl::types::GLvoid, // pointer to data
gl::STATIC_DRAW, // usage
);
}
const VERT_SHADER: &str = r"#version 150 core
out vec2 v_tex;
const vec2 pos[4]=vec2[4](vec2(-1.0, 1.0),
vec2(-1.0,-1.0),
vec2( 1.0, 1.0),
vec2( 1.0,-1.0));
void main()
{
v_tex=0.5*pos[gl_VertexID] + vec2(0.5);
gl_Position=vec4(pos[gl_VertexID], 0.0, 1.0);
}
";
const FRAG_SHADER: &str = r"
#version 150 core
in vec2 v_tex;
uniform sampler2D texSampler;
out vec4 color;
void main()
{
color=texture(texSampler, v_tex);
}";
let quad_programid = compile_program(VERT_SHADER, FRAG_SHADER);
let mut quad_vao = 0;
unsafe {
gl::GenVertexArrays(1, &mut quad_vao);
}
while !window.should_close() {
glfw.poll_events();
for (_, event) in glfw::flush_messages(&events) {
glfw_handle_event(&mut window, event);
}
unsafe {
// render to fb
gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer_handle);
// gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl::Viewport(0, 0, WIDTH as GLsizei, HEIGHT as GLsizei);
// clear color
2022-11-19 17:55:49 +11:00
clear_color(Color(0.3, 0.4, 0.6, 1.0));
gl::Clear(gl::COLOR_BUFFER_BIT);
// do the drawing
2022-11-19 17:55:49 +11:00
gl::UseProgram(triangle_program);
// select vertices
gl::BindVertexArray(vao);
// draw to bound target
gl::DrawArrays(gl::TRIANGLES, 0, 3);
// unselect vertices
gl::BindVertexArray(0);
2022-11-19 17:55:49 +11:00
// unselect fbo
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
}
// eprintln!("[core] rendered texture is {rendered_texture}");
2022-11-19 17:55:49 +11:00
unsafe {
filter.frame(0, &Viewport {
x: 0,
y: 0,
size: Size {
width: WIDTH,
height: HEIGHT
}
}, GlImage {
handle: rendered_texture,
format: gl::RGBA,
size: Size {
width: WIDTH,
height: HEIGHT
},
padded_size: Default::default()
}, false)
}
unsafe {
// texture is done now.
// todo: insert postprocessing stuff to rendered_texture
// map quad to screen
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
gl::ActiveTexture(gl::TEXTURE0);
gl::BindTexture(gl::TEXTURE_2D, rendered_texture);
gl::UseProgram(quad_programid);
gl::BindVertexArray(quad_vao);
gl::DrawArrays(gl::TRIANGLE_STRIP, 0, 4)
}
window.swap_buffers();
}
}
pub struct Color(f32, f32, f32, f32);
pub fn clear_color(c: Color) {
unsafe { gl::ClearColor(c.0, c.1, c.2, c.3) }
}
pub fn gl_get_string<'a>(name: gl::types::GLenum) -> &'a str {
let v = unsafe { gl::GetString(name) };
let v: &std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(v as *const i8) };
v.to_str().unwrap()
}
fn glfw_handle_event(window: &mut glfw::Window, event: glfw::WindowEvent) {
use glfw::WindowEvent as Event;
use glfw::Key;
use glfw::Action;
match event {
Event::Key(Key::Escape, _, Action::Press, _) => {
window.set_should_close(true);
},
_ => {},
}
}