Draw a triangle

Description

OpenGL renders triangles using a pipeline. There are multiple stages of the pipeline and some of them are configurable via shaders. Shaders are programs written in the OpenGL Shading Language (GLSL) that tell the OpenGL how to transform vertices into pixels on the screen.

To draw a triangle on the screen, we have to provide shaders for two of the pipeline stages: the vertex and fragment stage.

The vertex shader is run for every vertex passed through the pipeline. The output of the vertex shader specifies the position of the vertex.

The fragment shader is run for every pixel on the rasterized triangles. It specifies the color the each pixel.

Shaders must be compiled and linked before they can be used. This happens at runtime after we load the OpenGL library. To keep this exapmle simpler, we're skipping all error checking.

To start the pipeline, we call glDrawTriangles and specify how many vertices to use and that they should be combined as triangles. Since we're only drawing one triangle, we send three vertices for each point of the triangle.

OpenGL has ways of passing in vertex data using buffers, but in this case we're hardcoding the vertex data right into the shader. We operate in the -1.0 to 1.0 range on the X, Y, and Z axes. The gl_VertexID is an incremental index, so we use that to pick the three different vertices.

For the fragment shader, we're just hard coding the color to be green for everything.

Screenshot

Commands

git clone git@github.com:atsheehan/iridium
cd iridium
git checkout ff0697d53cb21800a903c9075b73324e34217b7d
cargo run --release

Code Changes

Added shaders/cube.fragGitHub

1+ #version 150
2+
3+ out vec4 color;
4+
5+ void main() {
6+ color = vec4(80.0 / 255.0, 200.0 / 255.0, 120.0 / 255.0, 1.0);
7+ }
1+ #version 150
2+
3+ out vec4 color;
4+
5+ void main() {
6+ color = vec4(80.0 / 255.0, 200.0 / 255.0, 120.0 / 255.0, 1.0);
7+ }

Added shaders/cube.vertGitHub

1+ #version 150
2+
3+ void main() {
4+ vec2 vertices[3];
5+ vertices[0] = vec2(-0.5, -0.5);
6+ vertices[1] = vec2(0.5, -0.5);
7+ vertices[2] = vec2(0.0, 0.5);
8+
9+ gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0);
10+ }
1+ #version 150
2+
3+ void main() {
4+ vec2 vertices[3];
5+ vertices[0] = vec2(-0.5, -0.5);
6+ vertices[1] = vec2(0.5, -0.5);
7+ vertices[2] = vec2(0.0, 0.5);
8+
9+ gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0);
10+ }

Modified src/main.rsGitHub

@@ -23,6 +23,7 @@
2323 window_id,
2424 } if window_id == renderer.window_id() => {
2525 renderer.clear();
26+ renderer.draw_triangle();
2627 renderer.present();
2728 }
2829 _ => (),
@@ -23,6 +23,7 @@
23 window_id,
24 } if window_id == renderer.window_id() => {
25 renderer.clear();
 
26 renderer.present();
27 }
28 _ => (),
@@ -23,6 +23,7 @@
23 window_id,
24 } if window_id == renderer.window_id() => {
25 renderer.clear();
26+ renderer.draw_triangle();
27 renderer.present();
28 }
29 _ => (),

Modified src/render.rsGitHub

@@ -1,5 +1,6 @@
11 use std::ffi::CString;
22
3+ use gl::types::{GLenum, GLuint};
34 use glutin::{
45 config::ConfigTemplateBuilder,
56 context::{ContextApi, ContextAttributesBuilder, PossiblyCurrentContext, Version},
@@ -14,10 +15,15 @@
1415 window::{Window, WindowBuilder, WindowId},
1516 };
1617
18+ const CUBE_VERTEX_SHADER_SRC: &str = include_str!("../shaders/cube.vert");
19+ const CUBE_FRAGMENT_SHADER_SRC: &str = include_str!("../shaders/cube.frag");
20+
1721 pub(crate) struct Renderer {
1822 window: Window,
1923 context: PossiblyCurrentContext,
2024 surface: Surface<WindowSurface>,
25+ cube_program: Program,
26+ cube_vertex_array_id: GLuint,
2127 }
2228
2329 impl Renderer {
@@ -56,7 +62,20 @@
5662
5763 gl::load_with(|s| display.get_proc_address(&CString::new(s).unwrap()));
5864
65+ let cube_program = Program::build(CUBE_VERTEX_SHADER_SRC, CUBE_FRAGMENT_SHADER_SRC);
66+
5967 unsafe {
68+ gl::UseProgram(cube_program.gl_id());
69+ }
70+
71+ let cube_vertex_array_id = unsafe {
72+ let mut cube_vertex_array_id = 0;
73+ gl::GenVertexArrays(1, &mut cube_vertex_array_id);
74+ gl::BindVertexArray(cube_vertex_array_id);
75+ cube_vertex_array_id
76+ };
77+
78+ unsafe {
6079 gl::ClearColor(0.6, 0.4, 0.8, 1.0);
6180 }
6281
@@ -64,6 +83,8 @@
6483 window,
6584 surface,
6685 context,
86+ cube_program,
87+ cube_vertex_array_id,
6788 }
6889 }
6990
@@ -77,7 +98,84 @@
7798 }
7899 }
79100
101+ pub(crate) fn draw_triangle(&mut self) {
102+ unsafe {
103+ gl::UseProgram(self.cube_program.gl_id());
104+ gl::BindVertexArray(self.cube_vertex_array_id);
105+ gl::DrawArrays(gl::TRIANGLES, 0, 3);
106+ }
107+ }
108+
80109 pub(crate) fn present(&mut self) {
81110 self.surface.swap_buffers(&self.context).unwrap();
82111 }
83112 }
113+
114+ struct ProgramId(GLuint);
115+
116+ struct Program {
117+ id: ProgramId,
118+ }
119+
120+ impl Program {
121+ fn build(vertex_shader_src: &str, fragment_shader_src: &str) -> Self {
122+ let vertex_shader = Shader::compile(vertex_shader_src, ShaderType::Vertex);
123+ let fragment_shader = Shader::compile(fragment_shader_src, ShaderType::Fragment);
124+
125+ let program_id = unsafe { gl::CreateProgram() };
126+
127+ unsafe {
128+ gl::AttachShader(program_id, vertex_shader.gl_id());
129+ gl::AttachShader(program_id, fragment_shader.gl_id());
130+ gl::LinkProgram(program_id);
131+ gl::DetachShader(program_id, vertex_shader.gl_id());
132+ gl::DetachShader(program_id, fragment_shader.gl_id());
133+ }
134+
135+ Self {
136+ id: ProgramId(program_id),
137+ }
138+ }
139+
140+ fn gl_id(&self) -> GLuint {
141+ self.id.0
142+ }
143+ }
144+
145+ struct ShaderId(GLuint);
146+
147+ struct Shader {
148+ id: ShaderId,
149+ }
150+
151+ impl Shader {
152+ fn compile(source: &str, shader_type: ShaderType) -> Self {
153+ let source = CString::new(source).unwrap();
154+ let id = unsafe { gl::CreateShader(shader_type.gl_shader_type()) };
155+
156+ unsafe {
157+ gl::ShaderSource(id, 1, &source.as_ptr(), std::ptr::null());
158+ gl::CompileShader(id);
159+ }
160+
161+ Self { id: ShaderId(id) }
162+ }
163+
164+ fn gl_id(&self) -> GLuint {
165+ self.id.0
166+ }
167+ }
168+
169+ enum ShaderType {
170+ Vertex,
171+ Fragment,
172+ }
173+
174+ impl ShaderType {
175+ fn gl_shader_type(&self) -> GLenum {
176+ match self {
177+ Self::Vertex => gl::VERTEX_SHADER,
178+ Self::Fragment => gl::FRAGMENT_SHADER,
179+ }
180+ }
181+ }
@@ -1,5 +1,6 @@
1 use std::ffi::CString;
2
 
3 use glutin::{
4 config::ConfigTemplateBuilder,
5 context::{ContextApi, ContextAttributesBuilder, PossiblyCurrentContext, Version},
@@ -14,10 +15,15 @@
14 window::{Window, WindowBuilder, WindowId},
15 };
16
 
 
 
17 pub(crate) struct Renderer {
18 window: Window,
19 context: PossiblyCurrentContext,
20 surface: Surface<WindowSurface>,
 
 
21 }
22
23 impl Renderer {
@@ -56,7 +62,20 @@
56
57 gl::load_with(|s| display.get_proc_address(&CString::new(s).unwrap()));
58
 
 
59 unsafe {
 
 
 
 
 
 
 
 
 
 
 
60 gl::ClearColor(0.6, 0.4, 0.8, 1.0);
61 }
62
@@ -64,6 +83,8 @@
64 window,
65 surface,
66 context,
 
 
67 }
68 }
69
@@ -77,7 +98,84 @@
77 }
78 }
79
 
 
 
 
 
 
 
 
80 pub(crate) fn present(&mut self) {
81 self.surface.swap_buffers(&self.context).unwrap();
82 }
83 }
@@ -1,5 +1,6 @@
1 use std::ffi::CString;
2
3+ use gl::types::{GLenum, GLuint};
4 use glutin::{
5 config::ConfigTemplateBuilder,
6 context::{ContextApi, ContextAttributesBuilder, PossiblyCurrentContext, Version},
@@ -14,10 +15,15 @@
15 window::{Window, WindowBuilder, WindowId},
16 };
17
18+ const CUBE_VERTEX_SHADER_SRC: &str = include_str!("../shaders/cube.vert");
19+ const CUBE_FRAGMENT_SHADER_SRC: &str = include_str!("../shaders/cube.frag");
20+
21 pub(crate) struct Renderer {
22 window: Window,
23 context: PossiblyCurrentContext,
24 surface: Surface<WindowSurface>,
25+ cube_program: Program,
26+ cube_vertex_array_id: GLuint,
27 }
28
29 impl Renderer {
@@ -56,7 +62,20 @@
62
63 gl::load_with(|s| display.get_proc_address(&CString::new(s).unwrap()));
64
65+ let cube_program = Program::build(CUBE_VERTEX_SHADER_SRC, CUBE_FRAGMENT_SHADER_SRC);
66+
67 unsafe {
68+ gl::UseProgram(cube_program.gl_id());
69+ }
70+
71+ let cube_vertex_array_id = unsafe {
72+ let mut cube_vertex_array_id = 0;
73+ gl::GenVertexArrays(1, &mut cube_vertex_array_id);
74+ gl::BindVertexArray(cube_vertex_array_id);
75+ cube_vertex_array_id
76+ };
77+
78+ unsafe {
79 gl::ClearColor(0.6, 0.4, 0.8, 1.0);
80 }
81
@@ -64,6 +83,8 @@
83 window,
84 surface,
85 context,
86+ cube_program,
87+ cube_vertex_array_id,
88 }
89 }
90
@@ -77,7 +98,84 @@
98 }
99 }
100
101+ pub(crate) fn draw_triangle(&mut self) {
102+ unsafe {
103+ gl::UseProgram(self.cube_program.gl_id());
104+ gl::BindVertexArray(self.cube_vertex_array_id);
105+ gl::DrawArrays(gl::TRIANGLES, 0, 3);
106+ }
107+ }
108+
109 pub(crate) fn present(&mut self) {
110 self.surface.swap_buffers(&self.context).unwrap();
111 }
112 }
113+
114+ struct ProgramId(GLuint);
115+
116+ struct Program {
117+ id: ProgramId,
118+ }
119+
120+ impl Program {
121+ fn build(vertex_shader_src: &str, fragment_shader_src: &str) -> Self {
122+ let vertex_shader = Shader::compile(vertex_shader_src, ShaderType::Vertex);
123+ let fragment_shader = Shader::compile(fragment_shader_src, ShaderType::Fragment);
124+
125+ let program_id = unsafe { gl::CreateProgram() };
126+
127+ unsafe {
128+ gl::AttachShader(program_id, vertex_shader.gl_id());
129+ gl::AttachShader(program_id, fragment_shader.gl_id());
130+ gl::LinkProgram(program_id);
131+ gl::DetachShader(program_id, vertex_shader.gl_id());
132+ gl::DetachShader(program_id, fragment_shader.gl_id());
133+ }
134+
135+ Self {
136+ id: ProgramId(program_id),
137+ }
138+ }
139+
140+ fn gl_id(&self) -> GLuint {
141+ self.id.0
142+ }
143+ }
144+
145+ struct ShaderId(GLuint);
146+
147+ struct Shader {
148+ id: ShaderId,
149+ }
150+
151+ impl Shader {
152+ fn compile(source: &str, shader_type: ShaderType) -> Self {
153+ let source = CString::new(source).unwrap();
154+ let id = unsafe { gl::CreateShader(shader_type.gl_shader_type()) };
155+
156+ unsafe {
157+ gl::ShaderSource(id, 1, &source.as_ptr(), std::ptr::null());
158+ gl::CompileShader(id);
159+ }
160+
161+ Self { id: ShaderId(id) }
162+ }
163+
164+ fn gl_id(&self) -> GLuint {
165+ self.id.0
166+ }
167+ }
168+
169+ enum ShaderType {
170+ Vertex,
171+ Fragment,
172+ }
173+
174+ impl ShaderType {
175+ fn gl_shader_type(&self) -> GLenum {
176+ match self {
177+ Self::Vertex => gl::VERTEX_SHADER,
178+ Self::Fragment => gl::FRAGMENT_SHADER,
179+ }
180+ }
181+ }