110 std::string_view
const hello_message {
111 "Hello! This is a more complex example of staplegl usage, featuring the Utah Teapot model.\n"
112 "Press the U and D keys to increase (U) and decrease (D) the luminosity of the light source.\n"
113 "Play around with them to observe how the bloom effect changes."
116 std::cout << hello_message << std::endl;
124 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
125 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
128 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
131 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
136 GLFWwindow* window = glfwCreateWindow(
SCR_WIDTH,
SCR_HEIGHT,
"Utah Teapot",
nullptr,
nullptr);
137 if (window ==
nullptr) {
138 std::cout <<
"Failed to create GLFW window" << std::endl;
142 glfwMakeContextCurrent(window);
147 if (gladLoadGLLoader(
reinterpret_cast<GLADloadproc
>(glfwGetProcAddress)) == 0) {
148 std::cout <<
"Failed to initialize GLAD" << std::endl;
153 glEnable(GL_DEPTH_TEST);
154 glEnable(GL_DEBUG_OUTPUT);
155 glEnable(GL_CULL_FACE);
156 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
160 glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0,
nullptr, GL_TRUE);
161 std::clog <<
"Printing OpenGL version info:" << std::endl;
162 std::clog <<
"OpenGL version: " << glGetString(GL_VERSION) << std::endl;
163 std::clog <<
"OpenGL vendor: " << glGetString(GL_VENDOR) << std::endl;
164 std::clog <<
"OpenGL renderer: " << glGetString(GL_RENDERER) << std::endl;
176 skybox_shader.
bind();
177 skybox_shader.upload_uniform1i(
"skybox", 0);
179 teapot_shader.bind();
180 teapot_shader.upload_uniform1i(
"environment", 0);
182 passthrough_shader.bind();
183 passthrough_shader.upload_uniform1i(
"scene", 1);
185 tonemap_shader.bind();
186 tonemap_shader.upload_uniform1i(
"scene", 1);
187 tonemap_shader.upload_uniform1i(
"bloom", 2);
189 downsample_shader.bind();
190 downsample_shader.upload_uniform1i(
"scene", 1);
192 upsample_shader.bind();
193 upsample_shader.upload_uniform1i(
"scene", 1);
199 std::span<const float> {},
202 .
internal_format = GL_RGBA16F, .format = GL_RGBA, .datatype = GL_FLOAT },
204 .
min_filter = GL_LINEAR, .mag_filter = GL_LINEAR, .clamping = GL_CLAMP_TO_EDGE },
209 std::span<const float> {},
212 .
internal_format = GL_RGBA16F, .format = GL_RGBA, .datatype = GL_FLOAT },
214 .
min_filter = GL_LINEAR, .mag_filter = GL_LINEAR, .clamping = GL_CLAMP_TO_EDGE }
220 for (
int i = 0;
auto& tex : pyramid_textures) {
222 std::span<const float> {},
225 .
internal_format = GL_RGBA16F, .format = GL_RGBA, .datatype = GL_FLOAT },
227 .
min_filter = GL_LINEAR, .mag_filter = GL_LINEAR, .clamping = GL_CLAMP_TO_EDGE }
264 std::vector<unsigned int> skybox_indices(SKYBOX_VERTS / 3);
265 std::generate(skybox_indices.begin(), skybox_indices.end(),
266 [n = 0]()
mutable { return n++; });
276 glm::mat4
const model = glm::mat4(1.0F);
301 { u_type::mat4,
"projection" },
302 { u_type::mat4,
"view" },
303 { u_type::mat4,
"model" },
304 { u_type::vec4,
"camera_pos" }
310 { u_type::vec4,
"light_pos" },
311 { u_type::vec4,
"light_color" },
312 { u_type::vec4,
"light_attenuation" },
313 { u_type::vec2,
"light_intensities" }
318 const glm::vec4 light_pos { 1.0F, 1.0F, 10.0F, 1.0F };
319 const glm::vec4 light_color { 0.9333F, 0.5098, 0.9333F, 1.0F };
322 light_block.set_attribute_data(std::span { glm::value_ptr(light_pos), 4 },
"light_pos");
323 light_block.set_attribute_data(std::span { glm::value_ptr(light_color), 4 },
"light_color");
324 light_block.set_attribute_data(std::span { glm::value_ptr(glm::vec4(1.0F, 0.22F, 0.20F, 0.0F)), 4 },
"light_attenuation");
325 light_block.set_attribute_data(std::span { glm::value_ptr(glm::vec2(
luminosity, 1.2F)), 2 },
"light_intensities");
326 light_block.unbind();
329 { u_type::vec4,
"material_color" },
330 { u_type::float32,
"material_shininess" },
331 { u_type::float32,
"material_roughness" }
336 const glm::vec4 teapot_color { 0.51F, 0.55F, 0.66F, 1.0F };
337 const float teapot_shininess = 32.0F;
338 const float teapot_roughness = 0.80F;
340 material_block.
bind();
341 material_block.set_attribute_data(std::span { glm::value_ptr(teapot_color), 4 },
"material_color");
342 material_block.set_attribute_data(std::span { &teapot_shininess, 1 },
"material_shininess");
343 material_block.set_attribute_data(std::span { &teapot_roughness, 1 },
"material_roughness");
344 material_block.unbind();
348 std::array<std::string, 6>
const faces {
349 "./assets/skybox/right.jpg",
"./assets/skybox/left.jpg",
350 "./assets/skybox/top.jpg",
"./assets/skybox/bottom.jpg",
351 "./assets/skybox/front.jpg",
"./assets/skybox/back.jpg"
355 std::array<std::span<std::byte>, 6> cube_data;
357 std::int32_t width = 0, height = 0, nrChannels = 0;
359 for (
auto& face : faces) {
360 auto* data =
reinterpret_cast<std::byte*
>(
361 stbi_load(face.c_str(), &width, &height, &nrChannels, 0));
362 if (data ==
nullptr) {
363 std::cout <<
"Failed to load texture" << std::endl;
367 cube_data[i++] = std::span<std::byte> { data,
static_cast<size_t>(width * height * nrChannels) };
373 cube_data, { width, height },
374 { .internal_format = GL_SRGB8, .format = GL_RGB, .datatype = GL_UNSIGNED_BYTE },
375 { .min_filter = GL_LINEAR, .mag_filter = GL_LINEAR, .clamping = GL_CLAMP_TO_EDGE },
379 for (
auto& face : cube_data) {
380 stbi_image_free(face.data());
386 glClearColor(0.F, 0.F, 0.F, 1.0F);
388 while (glfwWindowShouldClose(window) == 0) {
394 light_block.set_attribute_data(std::span { glm::value_ptr(glm::vec2(
luminosity, 1.2F)), 2 },
"light_intensities");
397 const auto time =
static_cast<float>(glfwGetTime());
398 const float radius = 4.0F;
399 const float slow_factor = 0.25F;
401 const float camX = sin(time * slow_factor) * radius;
402 const float camZ = cos(time * slow_factor) * radius;
403 const float camY = sin(time * slow_factor) + 0.5F;
406 glm::vec4 camera_pos { camX, camY, camZ, 1.0F };
409 glm::mat4 view = glm::lookAt(glm::vec3(camera_pos), glm::vec3(0.0F, 0.0F, 0.0F),
410 glm::vec3(0.0F, 1.0F, 0.0F));
411 glm::mat4 projection = glm::perspective(glm::radians(90.0F),
aspect_ratio, 0.01F, 100.0F);
415 camera_block.set_attribute_data(
416 std::span { glm::value_ptr(camera_pos), 4 },
"camera_pos");
417 camera_block.set_attribute_data(std::span { glm::value_ptr(view), 16 },
"view");
418 camera_block.set_attribute_data(std::span { glm::value_ptr(projection), 16 },
"projection");
422 msaa_fbo.set_texture(msaa_color);
423 msaa_fbo.set_renderbuffer(
429 if (!msaa_fbo.assert_completeness()) [[unlikely]] {
430 std::cerr <<
"Framebuffer not complete, line: " << __LINE__ << std::endl;
434 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
444 skybox_shader.bind();
448 glm::mat4 skybox_mat = glm::scale(glm::mat4(1.0F), glm::vec3(50.0F, 50.0F, 50.0F));
449 camera_block.set_attribute_data(std::span { glm::value_ptr(skybox_mat), 16 },
"model");
453 glDepthMask(GL_FALSE);
454 glDrawElements(GL_TRIANGLES, skybox_VAO.
index_data().count(), GL_UNSIGNED_INT,
nullptr);
455 glDepthMask(GL_TRUE);
460 glm::mat4 light_mat = glm::translate(glm::mat4(1.0F), glm::vec3(light_pos));
461 camera_block.set_attribute_data(std::span { glm::value_ptr(light_mat), 16 },
"model");
467 glDrawElements(GL_TRIANGLES, skybox_VAO.
index_data().count(), GL_UNSIGNED_INT,
nullptr);
473 glm::mat4 model_mat = glm::scale(model, glm::vec3(1.0F, -1.0F, 1.0F));
474 camera_block.set_attribute_data(std::span { glm::value_ptr(model_mat), 16 },
"model");
477 teapot_shader.bind();
479 glDrawElements(GL_TRIANGLES, VAO.
index_data().count(), GL_UNSIGNED_INT,
nullptr);
488 post_fbo.set_texture(hdr_color);
507 passthrough_shader.bind();
508 hdr_color.set_unit(1);
509 post_fbo.set_texture(pyramid_textures[0], 0);
511 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
513 downsample_shader.bind();
516 for (
size_t i = 0; i < pyramid_textures.size() - 1; i++) {
519 auto& draw_source = pyramid_textures[i];
520 auto& draw_target = pyramid_textures[i + 1];
521 auto const& t_res = draw_target.get_resolution();
523 draw_source.set_unit(1);
525 post_fbo.set_texture(draw_target, 0);
526 post_fbo.set_viewport(
530 downsample_shader.upload_uniform2f(
531 "uResolution",
static_cast<float>(t_res.width),
532 static_cast<float>(t_res.height));
534 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
542 glBlendFunc(GL_ONE, GL_ONE);
543 glBlendEquation(GL_FUNC_ADD);
545 upsample_shader.bind();
547 for (
int i = pyramid_textures.size() - 1; i > 0; i--) {
550 auto& draw_source = pyramid_textures[i];
551 auto& draw_target = pyramid_textures[i - 1];
553 draw_source.set_unit(1);
555 post_fbo.set_texture(draw_target, 0);
556 post_fbo.set_viewport(
557 { draw_target.get_resolution().width,
558 draw_target.get_resolution().height });
559 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
568 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
570 tonemap_shader.bind();
572 hdr_color.set_unit(1);
573 pyramid_textures[0].set_unit(2);
575 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
577 glfwSwapBuffers(window);