Skip to content

Instantly share code, notes, and snippets.

@DarinM223
Created January 16, 2017 05:12
Show Gist options
  • Save DarinM223/60428aae18c1b937171f9b3ca1d2fa48 to your computer and use it in GitHub Desktop.
Save DarinM223/60428aae18c1b937171f9b3ca1d2fa48 to your computer and use it in GitHub Desktop.
Glium drawing a textured rectangle
#[macro_use]
extern crate glium;
extern crate cgmath;
extern crate image;
use cgmath::{Vector2, Matrix4};
use glium::glutin;
use glium::{DisplayBuild, Surface};
use std::io::Cursor;
#[derive(Clone, Copy)]
struct Vertex {
// The fields in Vertex are usually there
// to be passed into the shader file.
position: [f32; 2],
}
// This line implements the Vertex using a macro inside glium.
// Don't forget to include all of the fields as parameters otherwise
// glium won't pass those into the shader.
implement_vertex!(Vertex, position);
const VERTEX_SHADER: &'static str = r#"
#version 140
// Input parameter from the Vertex struct.
in vec2 position;
// Uniform parameter passed in from the frame.draw() call.
uniform mat4 projection;
// Output texture coordinates that gets passed into the fragment shader.
out vec2 v_tex_coords;
void main() {
// In order to return the texture coordinate for a specific
// vertex we have to know what vertex is currently being passed in.
// We do this through gl_VertexID which increments with every vertex passed in.
// We can figure out the rectangle specific index from the vertex id by modding it
// by 4. Example: if a vertex has id 16, then it is the first vertex of the fourth
// rectangle being drawn. 16 % 4 == 0 which correctly returns the first index.
if (gl_VertexID % 4 == 0) { // First vertex
v_tex_coords = vec2(0.0, 1.0);
} else if (gl_VertexID % 4 == 1) { // Second vertex
v_tex_coords = vec2(1.0, 1.0);
} else if (gl_VertexID % 4 == 2) { // Third vertex
v_tex_coords = vec2(0.0, 0.0);
} else { // Fourth vertex
v_tex_coords = vec2(1.0, 0.0);
}
gl_Position = projection * vec4(position, 0.0, 1.0);
}
"#;
const FRAGMENT_SHADER: &'static str = r#"
#version 140
// Input texture coordinates passed from the vertex shader.
in vec2 v_tex_coords;
// Outputs the color for the specific fragment.
out vec4 color;
// Uniform parameter passed in from the frame.draw() call.
uniform sampler2D tex;
void main() {
// Applies a texture to the rectangle.
color = texture(tex, v_tex_coords);
}
"#;
const SCREEN_WIDTH: u32 = 1024;
const SCREEN_HEIGHT: u32 = 768;
fn main() {
let display = glutin::WindowBuilder::new()
.with_dimensions(SCREEN_WIDTH, SCREEN_HEIGHT)
.with_title(format!("Hello world!"))
.build_glium()
.unwrap();
// Load the texture.
let texture = {
let img = image::load(
Cursor::new(&include_bytes!("../assets/megumin.png")[..]),
image::PNG
).unwrap().to_rgba();
let img_dim = img.dimensions();
let img = glium::texture::RawImage2d::from_raw_rgba_reversed(img.into_raw(), img_dim);
glium::texture::Texture2d::new(&display, img).unwrap()
};
// Before we can draw the rectangle we have to
// tell OpenGL what a rectangle is. All OpenGL needs
// to know is that a rectangle is four vertexes (points)
// and that you can make two triangles from the four points.
let (rect_vertices, rect_indices) = {
// Data specifying how triangles would be made from the 4 points.
// The first triangle consists of vertexes 0, 1, and 2,
// while the second triangle consists of 1, 3, 2.
//
// 0 1
// +------+
// | / |
// | / |
// |/ |
// +------+
// 2 3
let ib_data: Vec<u16> = vec![0, 1, 2, 1, 3, 2];
// Creates a dynamic vertex buffer with four points.
// Dynamic means that the actual vertexes will be specified later which means
// the size of our rectangle is not fixed.
let vb = glium::VertexBuffer::empty_dynamic(&display, 4).unwrap();
// Creates an index buffer showing how the triangles would be made from the four points.
let ib = glium::IndexBuffer::new(
&display,
glium::index::PrimitiveType::TrianglesList,
&ib_data
).unwrap();
(vb, ib)
};
// Create a program from the two shaders.
// A "program" is just a bunch of shaders so you can have multiple programs
// for drawing different things.
let rect_program = glium::Program::from_source(
&display,
VERTEX_SHADER,
FRAGMENT_SHADER,
None
).unwrap();
let perspective = {
let matrix: Matrix4<f32> = cgmath::ortho(
0.0,
SCREEN_WIDTH as f32,
SCREEN_HEIGHT as f32,
0.0,
-1.0,
1.0
);
Into::<[[f32; 4]; 4]>::into(matrix)
};
let rect_size = Vector2 {
x: 300.0,
y: 300.0,
};
let mut rect_position = Vector2 {
x: (SCREEN_WIDTH / 2) as f32,
y: (SCREEN_HEIGHT / 2) as f32,
};
// Main event loop where all the drawing code is contained.
loop {
let mut frame = display.draw();
// Start with white background.
frame.clear_color(1.0, 1.0, 1.0, 1.0);
// Dynamically set the rectangle's vertices.
{
let left = rect_position.x - rect_size.x / 2.0;
let right = rect_position.x + rect_size.x / 2.0;
let bottom = rect_position.y + rect_size.y / 2.0;
let top = rect_position.y - rect_size.y / 2.0;
let vb_data = vec![
Vertex { position: [left, top] },
Vertex { position: [right, top] },
Vertex { position: [left, bottom] },
Vertex { position: [right, bottom] }
];
rect_vertices.write(&vb_data);
}
// Draw the rectangle.
{
// Uniform parameters to pass into the shaders.
let uniforms = uniform! {
projection: perspective,
tex: &texture,
};
frame.draw(
&rect_vertices,
&rect_indices,
&rect_program,
&uniforms,
&Default::default()
).unwrap();
}
frame.finish().unwrap();
// Handles keyboard input.
for event in display.poll_events() {
match event {
glutin::Event::Closed => return,
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(glutin::VirtualKeyCode::Escape)) => return,
_ => {}
}
}
}
}
Copy link

ghost commented Jan 30, 2023

stupid question : how do i draw a second one

@DarinM223
Copy link
Author

After drawing the first rectangle, set the rectangle's vertices again and then draw it again before finishing the frame. It would be better for the rectangle to take in a model matrix uniform so it can be drawn multiple times in different positions and scales though.

Copy link

ghost commented Jan 30, 2023

oh ok thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment