Add skybox
Description
I'm tired of looking at our mauve background. We can add some texture to our sky with a skybox.
The way we draw a skybox is different than our cubes. Rather than modifying the cube shader, we'll introduce a second skybox shader to render this.
Using multiple shaders means we need to do more bookkeeping and better management of binding our objects. OpenGL is an opaque state machine. Even though the gl functions don't modify a given struct, they are setting some global state that will persist between frames.
The skybox shader will render a box that stretches from (-1, -1, -1) to (1, 1, 1), i.e. the entire OpenGL clipping box. The camera will always be located at the center of the box (i.e. the origin). The camera can rotate within the box, but as it moves the box moves with it. In this way the camera can never close to the edges of the box, giving the appearance of something rendered in the distance.
We'll texture six sides of the box. We pass the viewing angle to the fragment shader which uses a special function to convert the angle into a texture coordinate.
Screenshot
Commands
git clone git@github.com:atsheehan/iridium
cd iridium
git checkout cff4f2177f428fe7ba7cbd29e7a1a288d468b790
cargo run --release
Code Changes
Added shaders/skybox.fragGitHub
1+ #version 1502+ 3+ uniform samplerCube skybox;4+ in vec3 frag_position;5+ 6+ out vec4 color;7+ 8+ void main() {9+ color = texture(skybox, frag_position);10+ }
1+ #version 1502+ 3+ uniform samplerCube skybox;4+ in vec3 frag_position;5+ 6+ out vec4 color;7+ 8+ void main() {9+ color = texture(skybox, frag_position);10+ }
Added shaders/skybox.vertGitHub
1+ #version 1502+ 3+ uniform float camera_heading;4+ uniform float camera_pitch;5+ uniform float aspect_ratio = 1.0;6+ 7+ const float NEAR = 0.1;8+ const float FAR = 10000.0;9+ 10+ out vec3 frag_position;11+ 12+ void main() {13+ vec3 near_bottom_left = vec3(-1.0, -1.0, -1.0);14+ vec3 near_bottom_right = vec3(1.0, -1.0, -1.0);15+ vec3 near_top_left = vec3(-1.0, 1.0, -1.0);16+ vec3 near_top_right = vec3(1.0, 1.0, -1.0);17+ vec3 far_bottom_left = vec3(-1.0, -1.0, 1.0);18+ vec3 far_bottom_right = vec3(1.0, -1.0, 1.0);19+ vec3 far_top_left = vec3(-1.0, 1.0, 1.0);20+ vec3 far_top_right = vec3(1.0, 1.0, 1.0);21+ 22+ vec3 vertices[36];23+ // Front side24+ vertices[0] = near_bottom_left;25+ vertices[1] = near_bottom_right;26+ vertices[2] = near_top_right;27+ 28+ vertices[3] = near_top_right;29+ vertices[4] = near_top_left;30+ vertices[5] = near_bottom_left;31+ 32+ // Right side33+ vertices[6] = near_bottom_right;34+ vertices[7] = far_bottom_right;35+ vertices[8] = far_top_right;36+ 37+ vertices[9] = far_top_right;38+ vertices[10] = near_top_right;39+ vertices[11] = near_bottom_right;40+ 41+ // Far side42+ vertices[12] = far_bottom_right;43+ vertices[13] = far_bottom_left;44+ vertices[14] = far_top_left;45+ 46+ vertices[15] = far_top_left;47+ vertices[16] = far_top_right;48+ vertices[17] = far_bottom_right;49+ 50+ // Left side51+ vertices[18] = far_bottom_left;52+ vertices[19] = near_bottom_left;53+ vertices[20] = near_top_left;54+ 55+ vertices[21] = near_top_left;56+ vertices[22] = far_top_left;57+ vertices[23] = far_bottom_left;58+ 59+ // Top side60+ vertices[24] = near_top_left;61+ vertices[25] = near_top_right;62+ vertices[26] = far_top_right;63+ 64+ vertices[27] = far_top_right;65+ vertices[28] = far_top_left;66+ vertices[29] = near_top_left;67+ 68+ // Bottom side69+ vertices[30] = far_bottom_left;70+ vertices[31] = far_bottom_right;71+ vertices[32] = near_bottom_right;72+ 73+ vertices[33] = near_bottom_right;74+ vertices[34] = near_bottom_left;75+ vertices[35] = far_bottom_left;76+ 77+ frag_position = vertices[gl_VertexID];78+ 79+ mat4 camera_heading_rotation =80+ mat4(cos(camera_heading), 0.0, -sin(camera_heading), 0.0,81+ 0.0, 1.0, 0.0, 0.0,82+ sin(camera_heading), 0.0, cos(camera_heading), 0.0,83+ 0.0, 0.0, 0.0, 1.0);84+ 85+ mat4 camera_pitch_rotation =86+ mat4(1.0, 0.0, 0.0, 0.0,87+ 0.0, cos(camera_pitch), sin(camera_pitch), 0.0,88+ 0.0, -sin(camera_pitch), cos(camera_pitch), 0.0,89+ 0.0, 0.0, 0.0, 1.0);90+ 91+ mat4 model_to_camera_transform = camera_heading_rotation * camera_pitch_rotation;92+ 93+ mat4 camera_to_clip_transform =94+ mat4(1.0, 0.0, 0.0, 0.0,95+ 0.0, aspect_ratio, 0.0, 0.0,96+ 0.0, 0.0, (-NEAR - FAR) / (NEAR - FAR), 2.0 * FAR * NEAR / (NEAR - FAR),97+ 0.0, 0.0, 1.0, 0.0);98+ 99+ gl_Position = (vec4(vertices[gl_VertexID], 1.0) * model_to_camera_transform * camera_to_clip_transform).xyww;100+ }
1+ #version 1502+ 3+ uniform float camera_heading;4+ uniform float camera_pitch;5+ uniform float aspect_ratio = 1.0;6+ 7+ const float NEAR = 0.1;8+ const float FAR = 10000.0;9+ 10+ out vec3 frag_position;11+ 12+ void main() {13+ vec3 near_bottom_left = vec3(-1.0, -1.0, -1.0);14+ vec3 near_bottom_right = vec3(1.0, -1.0, -1.0);15+ vec3 near_top_left = vec3(-1.0, 1.0, -1.0);16+ vec3 near_top_right = vec3(1.0, 1.0, -1.0);17+ vec3 far_bottom_left = vec3(-1.0, -1.0, 1.0);18+ vec3 far_bottom_right = vec3(1.0, -1.0, 1.0);19+ vec3 far_top_left = vec3(-1.0, 1.0, 1.0);20+ vec3 far_top_right = vec3(1.0, 1.0, 1.0);21+ 22+ vec3 vertices[36];23+ // Front side24+ vertices[0] = near_bottom_left;25+ vertices[1] = near_bottom_right;26+ vertices[2] = near_top_right;27+ 28+ vertices[3] = near_top_right;29+ vertices[4] = near_top_left;30+ vertices[5] = near_bottom_left;31+ 32+ // Right side33+ vertices[6] = near_bottom_right;34+ vertices[7] = far_bottom_right;35+ vertices[8] = far_top_right;36+ 37+ vertices[9] = far_top_right;38+ vertices[10] = near_top_right;39+ vertices[11] = near_bottom_right;40+ 41+ // Far side42+ vertices[12] = far_bottom_right;43+ vertices[13] = far_bottom_left;44+ vertices[14] = far_top_left;45+ 46+ vertices[15] = far_top_left;47+ vertices[16] = far_top_right;48+ vertices[17] = far_bottom_right;49+ 50+ // Left side51+ vertices[18] = far_bottom_left;52+ vertices[19] = near_bottom_left;53+ vertices[20] = near_top_left;54+ 55+ vertices[21] = near_top_left;56+ vertices[22] = far_top_left;57+ vertices[23] = far_bottom_left;58+ 59+ // Top side60+ vertices[24] = near_top_left;61+ vertices[25] = near_top_right;62+ vertices[26] = far_top_right;63+ 64+ vertices[27] = far_top_right;65+ vertices[28] = far_top_left;66+ vertices[29] = near_top_left;67+ 68+ // Bottom side69+ vertices[30] = far_bottom_left;70+ vertices[31] = far_bottom_right;71+ vertices[32] = near_bottom_right;72+ 73+ vertices[33] = near_bottom_right;74+ vertices[34] = near_bottom_left;75+ vertices[35] = far_bottom_left;76+ 77+ frag_position = vertices[gl_VertexID];78+ 79+ mat4 camera_heading_rotation =80+ mat4(cos(camera_heading), 0.0, -sin(camera_heading), 0.0,81+ 0.0, 1.0, 0.0, 0.0,82+ sin(camera_heading), 0.0, cos(camera_heading), 0.0,83+ 0.0, 0.0, 0.0, 1.0);84+ 85+ mat4 camera_pitch_rotation =86+ mat4(1.0, 0.0, 0.0, 0.0,87+ 0.0, cos(camera_pitch), sin(camera_pitch), 0.0,88+ 0.0, -sin(camera_pitch), cos(camera_pitch), 0.0,89+ 0.0, 0.0, 0.0, 1.0);90+ 91+ mat4 model_to_camera_transform = camera_heading_rotation * camera_pitch_rotation;92+ 93+ mat4 camera_to_clip_transform =94+ mat4(1.0, 0.0, 0.0, 0.0,95+ 0.0, aspect_ratio, 0.0, 0.0,96+ 0.0, 0.0, (-NEAR - FAR) / (NEAR - FAR), 2.0 * FAR * NEAR / (NEAR - FAR),97+ 0.0, 0.0, 1.0, 0.0);98+ 99+ gl_Position = (vec4(vertices[gl_VertexID], 1.0) * model_to_camera_transform * camera_to_clip_transform).xyww;100+ }
Modified src/main.rsGitHub
@@ -119,6 +119,7 @@119119 renderer.clear();
120120
121121 renderer.draw_cubes();
122+ renderer.draw_skybox();
122123
123124 renderer.present();
124125 fps_counter.finish_frame(current_instant);
@@ -119,6 +119,7 @@119 renderer.clear();
120
121 renderer.draw_cubes();
122
123 renderer.present();
124 fps_counter.finish_frame(current_instant);
@@ -119,6 +119,7 @@119 renderer.clear();
120
121 renderer.draw_cubes();
122+ renderer.draw_skybox();
123
124 renderer.present();
125 fps_counter.finish_frame(current_instant);
Modified src/render.rsGitHub
@@ -26,6 +26,8 @@2626
2727 const CUBE_VERTEX_SHADER_SRC: &str = include_str!("../shaders/cube.vert");
2828 const CUBE_FRAGMENT_SHADER_SRC: &str = include_str!("../shaders/cube.frag");
29+ const SKYBOX_VERTEX_SHADER_SRC: &str = include_str!("../shaders/skybox.vert");
30+ const SKYBOX_FRAGMENT_SHADER_SRC: &str = include_str!("../shaders/skybox.frag");
2931
3032 pub(crate) struct Renderer {
3133 window: Window,
@@ -36,6 +38,9 @@3638 cube_texture_id: GLuint,
3739 position_array_buffer_id: GLuint,
3840 cube_count: usize,
41+ skybox_program: Program,
42+ skybox_vertex_array_id: GLuint,
43+ skybox_texture_id: GLuint,
3944 }
4045
4146 impl Renderer {
@@ -86,7 +91,8 @@8691 Program::build(CUBE_VERTEX_SHADER_SRC, CUBE_FRAGMENT_SHADER_SRC).unwrap();
8792
8893 unsafe {
89- gl::UseProgram(cube_program.gl_id());
94+ gl::Enable(gl::DEPTH_TEST);
95+ gl::DepthFunc(gl::LEQUAL);
9096 }
9197
9298 let cube_vertex_array_id = unsafe {
@@ -145,6 +151,110 @@145151 cube_texture_id
146152 };
147153
154+ let skybox_program =
155+ Program::build(SKYBOX_VERTEX_SHADER_SRC, SKYBOX_FRAGMENT_SHADER_SRC).unwrap();
156+
157+ let skybox_vertex_array_id = unsafe {
158+ let mut skybox_vertex_array_id = 0;
159+ gl::GenVertexArrays(1, &mut skybox_vertex_array_id);
160+ gl::BindVertexArray(skybox_vertex_array_id);
161+ skybox_vertex_array_id
162+ };
163+
164+ let skybox_texture_id = unsafe {
165+ let mut skybox_texture_id = 0;
166+ gl::GenTextures(1, &mut skybox_texture_id);
167+ gl::BindTexture(gl::TEXTURE_CUBE_MAP, skybox_texture_id);
168+
169+ let image_data: Vec<u8> = vec![100, 100, 100, 200, 200, 200, 50, 50, 50, 210, 210, 100];
170+
171+ gl::TexImage2D(
172+ gl::TEXTURE_CUBE_MAP_POSITIVE_Z,
173+ 0,
174+ gl::RGB8 as GLint,
175+ 2,
176+ 2,
177+ 0,
178+ gl::RGB,
179+ gl::UNSIGNED_BYTE,
180+ image_data.as_ptr() as *const c_void,
181+ );
182+ gl::TexImage2D(
183+ gl::TEXTURE_CUBE_MAP_NEGATIVE_Z,
184+ 0,
185+ gl::RGB8 as GLint,
186+ 2,
187+ 2,
188+ 0,
189+ gl::RGB,
190+ gl::UNSIGNED_BYTE,
191+ image_data.as_ptr() as *const c_void,
192+ );
193+ gl::TexImage2D(
194+ gl::TEXTURE_CUBE_MAP_POSITIVE_Y,
195+ 0,
196+ gl::RGB8 as GLint,
197+ 2,
198+ 2,
199+ 0,
200+ gl::RGB,
201+ gl::UNSIGNED_BYTE,
202+ image_data.as_ptr() as *const c_void,
203+ );
204+ gl::TexImage2D(
205+ gl::TEXTURE_CUBE_MAP_NEGATIVE_Y,
206+ 0,
207+ gl::RGB8 as GLint,
208+ 2,
209+ 2,
210+ 0,
211+ gl::RGB,
212+ gl::UNSIGNED_BYTE,
213+ image_data.as_ptr() as *const c_void,
214+ );
215+ gl::TexImage2D(
216+ gl::TEXTURE_CUBE_MAP_POSITIVE_X,
217+ 0,
218+ gl::RGB8 as GLint,
219+ 2,
220+ 2,
221+ 0,
222+ gl::RGB,
223+ gl::UNSIGNED_BYTE,
224+ image_data.as_ptr() as *const c_void,
225+ );
226+ gl::TexImage2D(
227+ gl::TEXTURE_CUBE_MAP_NEGATIVE_X,
228+ 0,
229+ gl::RGB8 as GLint,
230+ 2,
231+ 2,
232+ 0,
233+ gl::RGB,
234+ gl::UNSIGNED_BYTE,
235+ image_data.as_ptr() as *const c_void,
236+ );
237+
238+ // gl::TexParameteri(gl::TEXTURE_CUBE_MAP, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
239+ // gl::TexParameteri(
240+ // gl::TEXTURE_CUBE_MAP,
241+ // gl::TEXTURE_MIN_FILTER,
242+ // gl::NEAREST_MIPMAP_NEAREST as GLint,
243+ // );
244+ gl::TexParameteri(
245+ gl::TEXTURE_CUBE_MAP,
246+ gl::TEXTURE_MIN_FILTER,
247+ gl::NEAREST as i32,
248+ );
249+ gl::TexParameteri(
250+ gl::TEXTURE_CUBE_MAP,
251+ gl::TEXTURE_MAG_FILTER,
252+ gl::NEAREST as i32,
253+ );
254+
255+ skybox_texture_id
256+ };
257+
148258 unsafe {
149259 gl::ClearColor(0.6, 0.4, 0.8, 1.0);
150260 gl::Enable(gl::DEPTH_TEST);
@@ -159,6 +269,9 @@159269 cube_texture_id,
160270 position_array_buffer_id,
161271 cube_count: 0,
272+ skybox_program,
273+ skybox_vertex_array_id,
274+ skybox_texture_id,
162275 }
163276 }
164277
@@ -181,6 +294,15 @@181294 }
182295 }
183296
297+ pub(crate) fn draw_skybox(&mut self) {
298+ unsafe {
299+ gl::UseProgram(self.skybox_program.gl_id());
300+ gl::BindVertexArray(self.skybox_vertex_array_id);
301+ gl::BindTexture(gl::TEXTURE_CUBE_MAP, self.skybox_texture_id);
302+ gl::DrawArrays(gl::TRIANGLES, 0, 36);
303+ }
304+ }
305+
184306 pub(crate) fn update_block_cache(&mut self, positions: impl Iterator<Item = Vec3>) {
185307 let position_buffer: Vec<f32> = positions
186308 .flat_map(|position| [position.x(), position.y(), position.z()])
@@ -206,21 +328,36 @@206328 pub(crate) fn set_viewport(&mut self) {
207329 let window_size = self.window.inner_size();
208330 let aspect_ratio = window_size.width as f32 / window_size.height as f32;
331+
332+ self.cube_program.activate();
209333 self.cube_program
210334 .set_uniform_f32("aspect_ratio", &aspect_ratio);
211335
336+ self.skybox_program.activate();
337+ self.skybox_program
338+ .set_uniform_f32("aspect_ratio", &aspect_ratio);
339+
212340 unsafe {
213341 gl::Viewport(0, 0, window_size.width as i32, window_size.height as i32);
214342 }
215343 }
216344
217345 pub(crate) fn set_camera(&mut self, camera: &Camera) {
346+ self.cube_program.activate();
218347 self.cube_program
219348 .set_uniform_vec3("camera_position", camera.position());
220349 self.cube_program
221350 .set_uniform_f32("camera_heading", &camera.heading());
222351 self.cube_program
223352 .set_uniform_f32("camera_pitch", &camera.pitch());
353+
354+ self.skybox_program.activate();
355+ self.skybox_program
356+ .set_uniform_vec3("camera_position", camera.position());
357+ self.skybox_program
358+ .set_uniform_f32("camera_heading", &camera.heading());
359+ self.skybox_program
360+ .set_uniform_f32("camera_pitch", &camera.pitch());
224361 }
225362 }
226363
@@ -311,6 +448,12 @@311448 }
312449 }
313450 }
451+
452+ fn activate(&mut self) {
453+ unsafe {
454+ gl::UseProgram(self.gl_id());
455+ }
456+ }
314457 }
315458
316459 struct ShaderId(GLuint);
@@ -26,6 +26,8 @@26
27 const CUBE_VERTEX_SHADER_SRC: &str = include_str!("../shaders/cube.vert");
28 const CUBE_FRAGMENT_SHADER_SRC: &str = include_str!("../shaders/cube.frag");
29
30 pub(crate) struct Renderer {
31 window: Window,
@@ -36,6 +38,9 @@36 cube_texture_id: GLuint,
37 position_array_buffer_id: GLuint,
38 cube_count: usize,
39 }
40
41 impl Renderer {
@@ -86,7 +91,8 @@86 Program::build(CUBE_VERTEX_SHADER_SRC, CUBE_FRAGMENT_SHADER_SRC).unwrap();
87
88 unsafe {
89- gl::UseProgram(cube_program.gl_id());
90 }
91
92 let cube_vertex_array_id = unsafe {
@@ -145,6 +151,110 @@145 cube_texture_id
146 };
147
148 unsafe {
149 gl::ClearColor(0.6, 0.4, 0.8, 1.0);
150 gl::Enable(gl::DEPTH_TEST);
@@ -159,6 +269,9 @@159 cube_texture_id,
160 position_array_buffer_id,
161 cube_count: 0,
162 }
163 }
164
@@ -181,6 +294,15 @@181 }
182 }
183
184 pub(crate) fn update_block_cache(&mut self, positions: impl Iterator<Item = Vec3>) {
185 let position_buffer: Vec<f32> = positions
186 .flat_map(|position| [position.x(), position.y(), position.z()])
@@ -206,21 +328,36 @@206 pub(crate) fn set_viewport(&mut self) {
207 let window_size = self.window.inner_size();
208 let aspect_ratio = window_size.width as f32 / window_size.height as f32;
209 self.cube_program
210 .set_uniform_f32("aspect_ratio", &aspect_ratio);
211
212 unsafe {
213 gl::Viewport(0, 0, window_size.width as i32, window_size.height as i32);
214 }
215 }
216
217 pub(crate) fn set_camera(&mut self, camera: &Camera) {
218 self.cube_program
219 .set_uniform_vec3("camera_position", camera.position());
220 self.cube_program
221 .set_uniform_f32("camera_heading", &camera.heading());
222 self.cube_program
223 .set_uniform_f32("camera_pitch", &camera.pitch());
224 }
225 }
226
@@ -311,6 +448,12 @@311 }
312 }
313 }
314 }
315
316 struct ShaderId(GLuint);
@@ -26,6 +26,8 @@26
27 const CUBE_VERTEX_SHADER_SRC: &str = include_str!("../shaders/cube.vert");
28 const CUBE_FRAGMENT_SHADER_SRC: &str = include_str!("../shaders/cube.frag");
29+ const SKYBOX_VERTEX_SHADER_SRC: &str = include_str!("../shaders/skybox.vert");
30+ const SKYBOX_FRAGMENT_SHADER_SRC: &str = include_str!("../shaders/skybox.frag");
31
32 pub(crate) struct Renderer {
33 window: Window,
@@ -36,6 +38,9 @@38 cube_texture_id: GLuint,
39 position_array_buffer_id: GLuint,
40 cube_count: usize,
41+ skybox_program: Program,
42+ skybox_vertex_array_id: GLuint,
43+ skybox_texture_id: GLuint,
44 }
45
46 impl Renderer {
@@ -86,7 +91,8 @@91 Program::build(CUBE_VERTEX_SHADER_SRC, CUBE_FRAGMENT_SHADER_SRC).unwrap();
92
93 unsafe {
94+ gl::Enable(gl::DEPTH_TEST);
95+ gl::DepthFunc(gl::LEQUAL);
96 }
97
98 let cube_vertex_array_id = unsafe {
@@ -145,6 +151,110 @@151 cube_texture_id
152 };
153
154+ let skybox_program =
155+ Program::build(SKYBOX_VERTEX_SHADER_SRC, SKYBOX_FRAGMENT_SHADER_SRC).unwrap();
156+
157+ let skybox_vertex_array_id = unsafe {
158+ let mut skybox_vertex_array_id = 0;
159+ gl::GenVertexArrays(1, &mut skybox_vertex_array_id);
160+ gl::BindVertexArray(skybox_vertex_array_id);
161+ skybox_vertex_array_id
162+ };
163+
164+ let skybox_texture_id = unsafe {
165+ let mut skybox_texture_id = 0;
166+ gl::GenTextures(1, &mut skybox_texture_id);
167+ gl::BindTexture(gl::TEXTURE_CUBE_MAP, skybox_texture_id);
168+
169+ let image_data: Vec<u8> = vec![100, 100, 100, 200, 200, 200, 50, 50, 50, 210, 210, 100];
170+
171+ gl::TexImage2D(
172+ gl::TEXTURE_CUBE_MAP_POSITIVE_Z,
173+ 0,
174+ gl::RGB8 as GLint,
175+ 2,
176+ 2,
177+ 0,
178+ gl::RGB,
179+ gl::UNSIGNED_BYTE,
180+ image_data.as_ptr() as *const c_void,
181+ );
182+ gl::TexImage2D(
183+ gl::TEXTURE_CUBE_MAP_NEGATIVE_Z,
184+ 0,
185+ gl::RGB8 as GLint,
186+ 2,
187+ 2,
188+ 0,
189+ gl::RGB,
190+ gl::UNSIGNED_BYTE,
191+ image_data.as_ptr() as *const c_void,
192+ );
193+ gl::TexImage2D(
194+ gl::TEXTURE_CUBE_MAP_POSITIVE_Y,
195+ 0,
196+ gl::RGB8 as GLint,
197+ 2,
198+ 2,
199+ 0,
200+ gl::RGB,
201+ gl::UNSIGNED_BYTE,
202+ image_data.as_ptr() as *const c_void,
203+ );
204+ gl::TexImage2D(
205+ gl::TEXTURE_CUBE_MAP_NEGATIVE_Y,
206+ 0,
207+ gl::RGB8 as GLint,
208+ 2,
209+ 2,
210+ 0,
211+ gl::RGB,
212+ gl::UNSIGNED_BYTE,
213+ image_data.as_ptr() as *const c_void,
214+ );
215+ gl::TexImage2D(
216+ gl::TEXTURE_CUBE_MAP_POSITIVE_X,
217+ 0,
218+ gl::RGB8 as GLint,
219+ 2,
220+ 2,
221+ 0,
222+ gl::RGB,
223+ gl::UNSIGNED_BYTE,
224+ image_data.as_ptr() as *const c_void,
225+ );
226+ gl::TexImage2D(
227+ gl::TEXTURE_CUBE_MAP_NEGATIVE_X,
228+ 0,
229+ gl::RGB8 as GLint,
230+ 2,
231+ 2,
232+ 0,
233+ gl::RGB,
234+ gl::UNSIGNED_BYTE,
235+ image_data.as_ptr() as *const c_void,
236+ );
237+
238+ // gl::TexParameteri(gl::TEXTURE_CUBE_MAP, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint);
239+ // gl::TexParameteri(
240+ // gl::TEXTURE_CUBE_MAP,
241+ // gl::TEXTURE_MIN_FILTER,
242+ // gl::NEAREST_MIPMAP_NEAREST as GLint,
243+ // );
244+ gl::TexParameteri(
245+ gl::TEXTURE_CUBE_MAP,
246+ gl::TEXTURE_MIN_FILTER,
247+ gl::NEAREST as i32,
248+ );
249+ gl::TexParameteri(
250+ gl::TEXTURE_CUBE_MAP,
251+ gl::TEXTURE_MAG_FILTER,
252+ gl::NEAREST as i32,
253+ );
254+
255+ skybox_texture_id
256+ };
257+
258 unsafe {
259 gl::ClearColor(0.6, 0.4, 0.8, 1.0);
260 gl::Enable(gl::DEPTH_TEST);
@@ -159,6 +269,9 @@269 cube_texture_id,
270 position_array_buffer_id,
271 cube_count: 0,
272+ skybox_program,
273+ skybox_vertex_array_id,
274+ skybox_texture_id,
275 }
276 }
277
@@ -181,6 +294,15 @@294 }
295 }
296
297+ pub(crate) fn draw_skybox(&mut self) {
298+ unsafe {
299+ gl::UseProgram(self.skybox_program.gl_id());
300+ gl::BindVertexArray(self.skybox_vertex_array_id);
301+ gl::BindTexture(gl::TEXTURE_CUBE_MAP, self.skybox_texture_id);
302+ gl::DrawArrays(gl::TRIANGLES, 0, 36);
303+ }
304+ }
305+
306 pub(crate) fn update_block_cache(&mut self, positions: impl Iterator<Item = Vec3>) {
307 let position_buffer: Vec<f32> = positions
308 .flat_map(|position| [position.x(), position.y(), position.z()])
@@ -206,21 +328,36 @@328 pub(crate) fn set_viewport(&mut self) {
329 let window_size = self.window.inner_size();
330 let aspect_ratio = window_size.width as f32 / window_size.height as f32;
331+
332+ self.cube_program.activate();
333 self.cube_program
334 .set_uniform_f32("aspect_ratio", &aspect_ratio);
335
336+ self.skybox_program.activate();
337+ self.skybox_program
338+ .set_uniform_f32("aspect_ratio", &aspect_ratio);
339+
340 unsafe {
341 gl::Viewport(0, 0, window_size.width as i32, window_size.height as i32);
342 }
343 }
344
345 pub(crate) fn set_camera(&mut self, camera: &Camera) {
346+ self.cube_program.activate();
347 self.cube_program
348 .set_uniform_vec3("camera_position", camera.position());
349 self.cube_program
350 .set_uniform_f32("camera_heading", &camera.heading());
351 self.cube_program
352 .set_uniform_f32("camera_pitch", &camera.pitch());
353+
354+ self.skybox_program.activate();
355+ self.skybox_program
356+ .set_uniform_vec3("camera_position", camera.position());
357+ self.skybox_program
358+ .set_uniform_f32("camera_heading", &camera.heading());
359+ self.skybox_program
360+ .set_uniform_f32("camera_pitch", &camera.pitch());
361 }
362 }
363
@@ -311,6 +448,12 @@448 }
449 }
450 }
451+
452+ fn activate(&mut self) {
453+ unsafe {
454+ gl::UseProgram(self.gl_id());
455+ }
456+ }
457 }
458
459 struct ShaderId(GLuint);