Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // OpenGL example application, that renders a bunch of trees via instancing.
- #include "core.hpp"
- #include <simplex_noise.hpp>
- #include "glm/glm.hpp"
- #include "glm/gtc/matrix_transform.hpp"
- #include "glm/gtc/type_ptr.hpp"
- #include "lodepng/lodepng.h"
- #include <array>
- #include <chrono>
- #include <iostream>
- #include <random>
- #include <thread>
- /// Layout of the per-object data submitted to the graphics device.
- struct PerObject
- {
- glm::mat4 model; // object coords -> world coords
- };
- /// Combination of a vertex array object and an index buffer object.
- struct RenderBatch {
- GLuint vao; // vertex array object
- GLuint ibo; // index buffer object
- int32_t index_count;
- PerObject per_object;
- };
- /// Layout of the vertices, used to store them in vertex buffer objects.
- struct Vertex
- {
- glm::vec3 pos;
- glm::vec4 color;
- glm::vec3 normal;
- };
- /// Layout of the per-frame data submitted to the graphics device.
- struct PerFrame
- {
- glm::mat4 view; // world coords -> clip coords
- glm::mat4 proj;
- glm::vec4 light_dir;
- };
- RenderBatch CreateRenderBatch(const std::vector<Vertex>& vertices,
- const std::vector<uint32_t>& indices,
- const glm::mat4& model = glm::mat4{ 1 });
- GLuint CreateSpriteTexture(std::string const& filename);
- void InitRenderer();
- void InitScene();
- void UpdateShaders();
- int KeyAxisValue(GLFWwindow* window, int key1, int key2);
- void applyJoystickInput();
- bool NeedSceneUpdate();
- void UpdateScene();
- void RenderFrame();
- void RenderObject(const RenderBatch& batch);
- void FramebufferSizeCallback(GLFWwindow* window, int width, int height);
- void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
- RenderBatch CreateGround(glm::vec4 color, bool flat = false, float height = 0.f);
- float GetGroundHeight(glm::ivec2 coords);
- glm::vec3 GetGroundNormal(glm::ivec2 coords);
- GLFWwindow* g_window;
- int g_window_width = 960, g_window_height = 540;
- GLuint g_shader_program_ground = 0; // shader program for rendering the ground
- GLuint g_shader_program_tree_imposter = 0; // shader program for rendering the tree imposters
- RenderBatch g_render_batch_ground; // geometry of the ground
- RenderBatch g_render_batch_water; // geometry of the water
- RenderBatch g_render_batch_tree_imposter; // geometry of a tree imposter
- GLuint g_texture_name_tree_sprite; // texture for the tree imposters
- GLuint g_ubo_per_frame; // uniform buffer containing per-frame data
- GLuint g_ubo_per_object;
- GLuint g_ubo_trees; // uniform buffer containing per-object data for all trees
- const int g_ground_extent = 512;
- const float g_ground_y_scale = 30.0f;
- PerFrame g_per_frame; // local copy of the per-frame uniform buffer
- glm::vec3 g_cam_pos{ -8, 5, 12 }; // camera position
- glm::vec3 g_cam_velocity{ 0, 0, 0 }; // change of g_cam_pos per tick
- float g_cam_yaw = -0.85f; // camera left-right orientation in [-pi, pi]
- float g_diff_cam_yaw = 0.f; // change of g_cam_yaw per tick
- float g_cam_pitch = -0.12f; // camera up down orientation in [-1.5, 1.5]
- float g_diff_cam_pitch = 0.f; // change of g_cam_pitch per tick
- constexpr int numTrees = 1024; // number of trees to render
- float g_light_yaw = 0.02f; // light horizontal orientation in [-pi, pi]
- /// Entry point into the OpenGL example application.
- int main() {
- try {
- // initialize glfw
- GlfwInstance::init();
- // initialize this application
- InitRenderer();
- InitScene();
- // render until the window should close
- while (!glfwWindowShouldClose(g_window)) {
- glfwPollEvents(); // handle events
- while (NeedSceneUpdate())
- UpdateScene(); // per-tick updates
- RenderFrame(); // render image
- glfwSwapBuffers(g_window); // present image
- }
- }
- catch (const std::exception & e) {
- std::cerr << e.what() << "\nPress enter.\n";
- getchar();
- return -1;
- }
- }
- /// Creates the window and sets persistent render settings and compiles shaders.
- /// @throw std::runtime_error If an error occurs during intialization.
- void InitRenderer() {
- // create window
- g_window = glfwCreateWindow(g_window_width, g_window_height, // initial resolution
- "Terrain", // window title
- nullptr, nullptr);
- if (!g_window)
- throw std::runtime_error("Failed to create window.");
- // use the window that was just created
- glfwMakeContextCurrent(g_window);
- // get window resolution
- glfwGetFramebufferSize(g_window, &g_window_width, &g_window_height);
- // set callbacks for when the resolution changes or a key is pressed
- glfwSetFramebufferSizeCallback(g_window, &FramebufferSizeCallback);
- glfwSetKeyCallback(g_window, &KeyCallback);
- // enable vsync
- glfwSwapInterval(1);
- // load OpenGL (return value of 0 indicates error)
- if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)))
- throw std::runtime_error("Failed to load OpenGL context.");
- std::cout << "OpenGL " << glGetString(GL_VERSION) << '\n';
- std::cout << "GLSL " << glGetString(GL_SHADING_LANGUAGE_VERSION) << '\n';
- // enable depth buffering
- glEnable(GL_DEPTH_TEST);
- // enable simple blending
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- // create shader programs
- UpdateShaders();
- // create per-frame uniform buffer (will be filled later, once per frame)
- glGenBuffers(1, &g_ubo_per_frame);
- glBindBuffer(GL_UNIFORM_BUFFER, g_ubo_per_frame);
- glBufferData(GL_UNIFORM_BUFFER, sizeof(PerFrame), nullptr, GL_DYNAMIC_DRAW);
- glBindBufferBase(GL_UNIFORM_BUFFER, 0, g_ubo_per_frame); // binding 0
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
- // create the per-object uniform buffer (will be filled later, once per rendering an object)
- glGenBuffers(1, &g_ubo_per_object);
- glBindBuffer(GL_UNIFORM_BUFFER, g_ubo_per_object);
- glBufferData(GL_UNIFORM_BUFFER, sizeof(PerObject), nullptr, GL_DYNAMIC_DRAW);
- glBindBufferBase(GL_UNIFORM_BUFFER, 1, g_ubo_per_object); // binding 1
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
- }
- /// Creates geometry and uniform buffers.
- void InitScene()
- {
- // create ground geometry: each vertex consists of a position and a color
- float height = 1.0f;
- g_render_batch_ground = CreateGround(glm::vec4{ 0.3f, 0.3f, 0.3f, 1.f });
- g_render_batch_water = CreateGround(glm::vec4{ 0.53f, 0.81f, 0.98f, 0.3f }, true, height);
- g_per_frame.light_dir = glm::vec4(2.0f, 0.0f, 2.0f, 0.0f);
- // create tree sprite geometry: a quad in the range [-1.5 , 1.5] x [0.0 3.0]
- // and with texture coordinates in the range [0,1] x [1,0]
- // use the color attribute of the vertices to store the texture coordinates (simply set the
- // third entry to 0)
- std::vector<Vertex> tree_sprite_vertices{ {{-1.5f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f, 1.f}},
- {{-1.5f, 3.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 1.f}},
- {{1.5f, 3.0f, 0.0f}, {1.0f, 0.0f, 0.0f, 1.f}},
- {{1.5f, 0.0f, 0.0f}, {1.0f, 1.0f, 0.0f, 1.f}} };
- std::vector<uint32_t> tree_sprite_indices{ 0, 1, 2, 0, 2, 3 };
- g_render_batch_tree_imposter = CreateRenderBatch(tree_sprite_vertices, tree_sprite_indices);
- // create tree sprite texture
- g_texture_name_tree_sprite = CreateSpriteTexture("../resources/pine_tree_sprite.png");
- /*
- * TODO: Aufgabe 3 Teilaufgabe 3: Berechnen Sie hier die positionen der Bäume und die
- * Model-Matrizen. Achten sie darauf das die Bäume nicht in Seen stehen und nutzen Sie
- * GetGroundHeight um die Höhe an der Position zu bestimmen.
- * Erstellen und füllen Sie dann den Uniform Buffer der das PerObject struct als
- * Datenformat verwendet
- */
- }
- /// @return The ground's geometry wrapped in a RenderBatch object.
- RenderBatch CreateGround(glm::vec4 color, bool flat, float height)
- {
- std::vector<Vertex> vertices;
- std::vector<uint32_t> indices;
- /*
- * TODO: Aufgabe 1 Teilaufgabe 2 Mesh-Generierung: Füllen sie den Vertex und
- * Index buffer mit dem generierten Mesh. Die Gitterpunkte entsprechen den
- * ganzzahligen Koordinaten, die Hohe und Normale eines Gitterpunktes (x; z)
- * werden in den Funktionen GetGroundHeight bzw. GetGroundNormal berechnet.
- * Jeder Vertex soll Information zu seiner Position, Farbe und Normalen
- * enthalten.
- *
- * Hinweis: Im Vektor indices stehen immer drei aufeinanderfolgende Integerwerte
- * für ein Dreieck. Diese Integers reprasentieren die drei Indices der Vertices
- * im Vektor vertices, aus denen das entsprechende Dreieck gebildet werden soll.
- */
- /*
- * TODO: Aufgabe 3 Teilaufgabe 1: Nutzen Sie die Parameter flat und height um
- * ein Mesh zu erstellen das keine Höhenwerte der Funktion GetGroundHeight
- * verwendet.
- */
- if (flat) {
- vertices.push_back({ {0, height, 0}, color, GetGroundNormal(glm::ivec2(0,0)) });
- vertices.push_back({ {g_ground_extent - 1, height+1, 0}, color, GetGroundNormal(glm::ivec2(0,0)) });
- vertices.push_back({ {0, height, g_ground_extent - 1}, color, GetGroundNormal(glm::ivec2(0,0)) });
- vertices.push_back({ {g_ground_extent - 1, height, g_ground_extent - 1},color, GetGroundNormal(glm::ivec2(0,0)) });
- indices.push_back(0);
- indices.push_back(1);
- indices.push_back(2);
- indices.push_back(1);
- indices.push_back(2);
- indices.push_back(3);
- }
- else
- {
- for (int z = 0; z < g_ground_extent; z++) {
- for (int x = 0; x < g_ground_extent; x++) {
- vertices.push_back({ {x, GetGroundHeight(glm::ivec2(x,z)), z},
- color,
- GetGroundNormal(glm::ivec2(x,z)) });
- if (x < g_ground_extent - 1 && z < g_ground_extent - 1) {
- indices.push_back((z * g_ground_extent) + x);
- indices.push_back((z * g_ground_extent) + (x + 1));
- indices.push_back(((z + 1) * g_ground_extent) + x);
- indices.push_back(((z + 1) * g_ground_extent) + x);
- indices.push_back((z * g_ground_extent) + (x + 1));
- indices.push_back(((z + 1) * g_ground_extent) + (x + 1));
- }
- }
- }
- }
- //if (flat) {
- //
- //
- //
- // vertices.push_back({ {0, height, 0}, color, GetGroundNormal(glm::ivec2(0,0)) });
- // vertices.push_back({ {g_ground_extent - 1, height, 0}, color, GetGroundNormal(glm::ivec2(0,0)) });
- // vertices.push_back({ {0, height, g_ground_extent - 1}, color, GetGroundNormal(glm::ivec2(0,0)) });
- // vertices.push_back({ {g_ground_extent - 1, height, g_ground_extent - 1},color, GetGroundNormal(glm::ivec2(0,0)) });
- // indices.push_back(0);
- // indices.push_back(1);
- // indices.push_back(2);
- // indices.push_back(1);
- // indices.push_back(2);
- // indices.push_back(3);
- //}
- //std::cout << indices.size();
- //std::cout << indices.at(offset);
- //std::cout << vertices[512*512 +2].pos.x;
- //vertices.push_back({ {0, height, 0}, glm::vec4{ 0.53f, 0.81f, 0.98f, 0.3f }, GetGroundNormal(glm::ivec2(0,0)) });
- //vertices.push_back({ {511, height, 0}, glm::vec4{ 0.53f, 0.81f, 0.98f, 0.3f }, GetGroundNormal(glm::ivec2(0,0)) });
- //vertices.push_back({ {0, height, 511}, glm::vec4{ 0.53f, 0.81f, 0.98f, 0.3f }, GetGroundNormal(glm::ivec2(0,0)) });
- //vertices.push_back({ {0, height, 511}, glm::vec4{ 0.53f, 0.81f, 0.98f, 0.3f }, GetGroundNormal(glm::ivec2(0,0)) });
- //vertices.push_back({ {511, height, 0}, glm::vec4{ 0.53f, 0.81f, 0.98f, 0.3f }, GetGroundNormal(glm::ivec2(0,0)) });
- //vertices.push_back({ {511, height, 511},glm::vec4{ 0.53f, 0.81f, 0.98f, 0.3f }, GetGroundNormal(glm::ivec2(0,0)) });
- //
- //
- //indices.push_back(512*512 + 1);
- //indices.push_back(512*512 + 2);
- //indices.push_back(512*512 + 3);
- //indices.push_back(512*512 + 4);
- //indices.push_back(512*512 + 5);
- //indices.push_back(512*512 + 6);
- //int sizevertices = vertices.size();
- //
- //height += 1;
- //
- //vertices.push_back({ {0, height, 0},glm::vec4{ 0.53f, 0.81f, 0.98f, 0.3f },GetGroundNormal(glm::ivec2(0,0)) });
- //vertices.push_back({ {511, height, 0},glm::vec4{ 0.53f, 0.81f, 0.98f, 0.3f },GetGroundNormal(glm::ivec2(0,0)) });
- //vertices.push_back({ {0, height, 511},glm::vec4{ 0.53f, 0.81f, 0.98f, 0.3f },GetGroundNormal(glm::ivec2(0,0)) });
- //vertices.push_back({ {511, height, 511},glm::vec4{ 0.53f, 0.81f, 0.98f, 0.3f },GetGroundNormal(glm::ivec2(0,0)) });
- //indices.push_back(sizevertices + 0);
- //indices.push_back(sizevertices + 1);
- //indices.push_back(sizevertices + 2);
- //indices.push_back(sizevertices + 1);
- //indices.push_back(sizevertices + 2);
- //indices.push_back(sizevertices + 3);
- //indices.push_back(0);
- //indices.push_back(511);
- //indices.push_back(511*511-511);
- //indices.push_back(1000);
- //indices.push_back(511);
- //indices.push_back(511*511);
- //std::cout << indices.at(offset+1.f);
- //std::cout << vertices[512*512 +3].pos.x;
- //std::cout << vertices[3].pos.y;
- //std::cout << vertices[3].pos.z;
- //std::vector<uint32_t> indices_water;
- //std::cout << vertices;
- //at(512 * 512 *4)
- //uint32_t offset = vertices.size();
- //if (flat)
- //{
- // for (int z = 0; z < g_ground_extent; z++) {
- // for (int x = 0; x < g_ground_extent; x++) {
- //
- // vertices.push_back({ {x, height, z},
- // color,
- // GetGroundNormal(glm::ivec2(x,z)) });
- //
- // if (x < g_ground_extent - 1 && z < g_ground_extent - 1) {
- // indices.push_back(((z * g_ground_extent) + x) + offset);
- // indices.push_back(((z * g_ground_extent) + (x + 1)) + offset);
- // indices.push_back((((z + 1) * g_ground_extent) + x) + offset);
- // indices.push_back((((z + 1) * g_ground_extent) + x) + offset);
- // indices.push_back(((z * g_ground_extent) + (x + 1)) + offset);
- // indices.push_back((((z + 1) * g_ground_extent) + (x + 1)) + offset);
- //
- // }
- // }
- // }
- //}
- //else
- //{
- // for (int z = 0; z < g_ground_extent; z++) {
- // for (int x = 0; x < g_ground_extent; x++) {
- //
- // vertices.push_back({ {x, 0, z},
- // color,
- // GetGroundNormal(glm::ivec2(x,z)) });
- //
- // if (x < g_ground_extent - 1 && z < g_ground_extent - 1) {
- // indices.push_back(((z * g_ground_extent) + x) + offset);
- // indices.push_back(((z * g_ground_extent) + (x + 1)) + offset);
- // indices.push_back((((z + 1) * g_ground_extent) + x) + offset);
- // indices.push_back((((z + 1) * g_ground_extent) + x) + offset);
- // indices.push_back(((z * g_ground_extent) + (x + 1)) + offset);
- // indices.push_back((((z + 1) * g_ground_extent) + (x + 1)) + offset);
- //
- // }
- // }
- // }
- //}
- //std::cout << indices_water.size();
- //for (int i = 0; i < g_ground_extent*g_ground_extent; i++)
- //{
- // indices.push_back(indices_water.at(i));
- //}
- glm::vec3 translation{ -g_ground_extent / 2.f, 0.01f, -g_ground_extent / 2.f };
- glm::mat4 transform = glm::translate(glm::mat4{ 1 }, translation);
- return CreateRenderBatch(vertices, indices, transform);
- }
- /// @param coord Coordinates on the ground's grid.
- /// @return The disired height of the ground at that coordinate.
- /// @throws std::runtime_error if coord contains a negative value or a value above g_ground_extent.
- float GetGroundHeight(glm::ivec2 coord)
- {
- static std::vector<std::array<float, g_ground_extent>> height_map;
- if (glm::clamp(coord, 0, g_ground_extent - 1) != coord)
- throw std::runtime_error("Index out of bounds.");
- if (height_map.empty())
- {
- height_map.resize(g_ground_extent);
- for (int x = 0; x < g_ground_extent; ++x)
- {
- for (int y = 0; y < g_ground_extent; ++y)
- {
- float noise = simplex::fractal2D(0.0158229f, // frequency
- 2.0f, // lacunarity
- 0.4f, // persistence
- 5, // number of octaves
- static_cast<float>(x),
- static_cast<float>(y)); // coodinates
- noise = noise * 0.5f + 0.5f;
- noise *= noise;
- float hilliness = simplex::noise2D(0.011887f * x, 0.011887f * y);
- hilliness *= hilliness;
- hilliness = 1.0f - hilliness;
- hilliness *= hilliness;
- hilliness = 0.4f + 0.6f * hilliness;
- int dx = g_ground_extent / 2 - x;
- int dy = g_ground_extent / 2 - y;
- float plain = static_cast<float>(dx * dx + dy * dy);
- plain = std::min(plain / 400.f, 1.0f);
- plain = 1.0f - plain;
- plain *= plain;
- plain = 1.0f - plain;
- height_map[x][y] = g_ground_y_scale * noise * hilliness * plain;
- }
- }
- }
- return height_map[coord.x][coord.y];
- }
- /// @param coords Coordinates on the ground's grid.
- /// @return The disired normal vector of the ground at that coordinate.
- /// @throws std::runtime_error if coord contains a negative value or a value above g_ground_extent.
- glm::vec3 GetGroundNormal(glm::ivec2 coords)
- {
- static std::vector<std::array<glm::vec3, g_ground_extent>> normal_map;
- if (glm::clamp(coords, 0, g_ground_extent - 1) != coords)
- throw std::runtime_error("Index out of bounds (GetGroundNormal).");
- if (normal_map.empty())
- {
- normal_map.resize(g_ground_extent);
- for (int x = 0; x < g_ground_extent; ++x)
- {
- for (int z = 0; z < g_ground_extent; ++z)
- {
- normal_map[x][z] = glm::vec3(0.f, 1.f, 0.f);
- /*
- * TODO: Aufgabe 1 Teilaufgabe 3 Normalen: Berechnen Sie die normale für
- * die Position (x,y).
- *
- * Hinweise: - Achten Sie auf Grenzfälle.
- * - Sollten Sie das Kreuzproduktes in Ihrer Berechnung verwenden,
- * achten Sie auf eine korrekte Orientierung der Vektoren.
- * - Zum schnellen Debuggen der Normalen bietet es sich an, die
- * Normale als Terrainfarbe zu ubergeben
- */
- if (x > 0 && x < g_ground_extent - 1 && z > 0 && z < g_ground_extent - 1) {
- float sizefactor = 1.0f / (8.0f * 0.96f);
- float hnw = GetGroundHeight(glm::ivec2(x - 1, z - 1));
- float hn = GetGroundHeight(glm::ivec2(x, z - 1));
- float hne = GetGroundHeight(glm::ivec2(x + 1, z - 1));
- float he = GetGroundHeight(glm::ivec2(x + 1, z));
- float hse = GetGroundHeight(glm::ivec2(x + 1, z + 1));
- float hs = GetGroundHeight(glm::ivec2(x, z + 1));
- float hsw = GetGroundHeight(glm::ivec2(x - 1, z + 1));
- float hw = GetGroundHeight(glm::ivec2(x - 1, z));
- float dydx = ((hne + 2 * he + hse) - (hnw + 2 * hw + hsw)) * sizefactor;
- float dydz = ((hsw + 2 * hs + hse) - (hnw + 2 * hn + hne)) * sizefactor;
- normal_map[x][z] = glm::normalize(glm::vec3(-dydx, 1.0f, -dydz));
- }
- }
- }
- }
- return normal_map[coords.x][coords.y];
- }
- /// @param vertices A vertex buffer.
- /// @param out_indices An index buffer.
- /// @return RenderBatch object created using the given buffers.
- RenderBatch CreateRenderBatch(const std::vector<Vertex>& vertices,
- const std::vector<uint32_t>& indices, const glm::mat4& model)
- {
- RenderBatch batch;
- batch.per_object.model = model;
- batch.index_count = static_cast<int32_t>(indices.size());
- // create & fill index buffer object
- glGenBuffers(1, &batch.ibo);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.ibo);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER,
- sizeof(uint32_t) * indices.size(), // data size
- indices.data(), // data pointer
- GL_STATIC_DRAW); // usage
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- // create vertex array object
- glGenVertexArrays(1, &batch.vao);
- glBindVertexArray(batch.vao);
- // create & fill vertex buffer object
- GLuint vbo;
- glGenBuffers(1, &vbo);
- glBindBuffer(GL_ARRAY_BUFFER, vbo);
- glBufferData(GL_ARRAY_BUFFER,
- sizeof(Vertex) * vertices.size(), // data size
- vertices.data(), // data pointer
- GL_STATIC_DRAW); // usage
- // specify & enable position attribute (the data is taken from the currently bound vbo)
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(
- 0, // index of the attribute
- 3, GL_FLOAT, // a position consists of 3 floats
- GL_FALSE,
- sizeof(Vertex), // stride between consecutive vertices in the buffer
- reinterpret_cast<const void*>(offsetof(Vertex, pos))); // offset of this attribute
- // specify & enable color attribute (the data is taken from the currently bound vbo)
- glEnableVertexAttribArray(1);
- glVertexAttribPointer(
- 1, // index of the attribute
- 4, GL_FLOAT, // a color consists of 4 floats
- GL_FALSE,
- sizeof(Vertex), // stride between consecutive colors in the buffer
- reinterpret_cast<const void*>(offsetof(Vertex, color))); // offset of this attribute
- // specify & enable normal attribute (the data is taken from the currently bound vbo)
- glEnableVertexAttribArray(2);
- glVertexAttribPointer(
- 2, // index of the attribute
- 3, GL_FLOAT, // a normal consists of 3 floats
- GL_FALSE,
- sizeof(Vertex), // stride between consecutive normals in the buffer
- reinterpret_cast<const void*>(offsetof(Vertex, normal))); // offset of this attribute
- // unbind bound objects
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindVertexArray(0);
- return batch;
- }
- /// Creates an OpenGL texture object using a texture from a file.
- /// @param filename Path to the file containing the texture.
- /// @return OpenGL handle of the created texture object.
- GLuint CreateSpriteTexture(std::string const& filename)
- {
- // Load file and decode image.
- unsigned int width, height;
- std::vector<unsigned char> image_data;
- unsigned int error = lodepng::decode(image_data, width, height, filename);
- GLint level = 0;
- GLint internal_format = GL_RGBA8;
- GLenum format = GL_RGBA;
- GLenum type = GL_UNSIGNED_BYTE;
- GLint border = 0;
- GLuint texture_name = 0;
- // generate a new texture object using glGenTextures and store its name in texture_name
- glGenTextures(1, &texture_name);
- // bind the created texture as a 2D texture
- glBindTexture(GL_TEXTURE_2D, texture_name);
- // use glTexParameteri to set the texture wrap mode to GL_MIRRORED_REPEAT,
- // the minification filter to GL_LINEAR_MIPMAP_LINEAR and the maxification filter to GL_LINEAR
- // hint: you have to do four calls to the function
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- // use the (oldschool) function glTexImage2D to buffer the actual texture data using the
- // information and data supplied above
- glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, type,
- image_data.data());
- // generate mipmaps for the texture
- glGenerateMipmap(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, 0);
- return texture_name;
- }
- /// Loads the shader programs or reloads them.
- void UpdateShaders()
- {
- glDeleteProgram(g_shader_program_ground);
- try {
- // compile vertex & fragment shader
- GLuint vertex_shader_ground = glCreateShader(GL_VERTEX_SHADER);
- CompileShader(vertex_shader_ground, "../shaders/vert_ground.glsl");
- GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
- CompileShader(fragment_shader, "../shaders/frag.glsl");
- GLuint vertex_shdr_tree_sprite = glCreateShader(GL_VERTEX_SHADER);
- CompileShader(vertex_shdr_tree_sprite, "../shaders/vert_tree_imposter.glsl");
- GLuint fragment_shdr_tree_sprite = glCreateShader(GL_FRAGMENT_SHADER);
- CompileShader(fragment_shdr_tree_sprite, "../shaders/frag_tree_imposter.glsl");
- // link shaders
- g_shader_program_ground = glCreateProgram();
- LinkProgram(g_shader_program_ground, { vertex_shader_ground, fragment_shader });
- g_shader_program_tree_imposter = glCreateProgram();
- LinkProgram(g_shader_program_tree_imposter,
- { vertex_shdr_tree_sprite, fragment_shdr_tree_sprite });
- // clean shaders up
- glDetachShader(g_shader_program_ground, fragment_shader);
- glDetachShader(g_shader_program_ground, vertex_shader_ground);
- glDetachShader(g_shader_program_tree_imposter, vertex_shdr_tree_sprite);
- glDetachShader(g_shader_program_tree_imposter, fragment_shdr_tree_sprite);
- glDeleteShader(fragment_shader);
- glDeleteShader(vertex_shader_ground);
- glDeleteShader(vertex_shdr_tree_sprite);
- glDeleteShader(fragment_shdr_tree_sprite);
- }
- catch (const std::runtime_error & e) {
- std::cerr << e.what() << "\nPress 'R' to reload the shaders.\n";
- }
- }
- /// Helper function for controlling an axis with 2 keys.
- /// @param window The window where the keys should be pressed.
- /// @param key1 The first key.
- /// @param key2 The second key.
- /// @return -1 if only the first key is pressed, 1 if only the second key is pressed and 0 if none
- /// of the keys or both keys are pressed.
- int KeyAxisValue(GLFWwindow* window, int key1, int key2)
- {
- bool key1_pressed = glfwGetKey(g_window, key1) == GLFW_PRESS;
- bool key2_pressed = glfwGetKey(g_window, key2) == GLFW_PRESS;
- return key2_pressed - key1_pressed;
- }
- double t0, t1 = 0.0;
- void applyJoystickInput()
- {
- if (!glfwJoystickPresent(GLFW_JOYSTICK_1))
- return;
- t0 = t1;
- t1 = glfwGetTime();
- double dt = t1 - t0;
- int count;
- const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &count);
- // update yaw and pitch
- float rot_dx = axes[2];
- float rot_dy = axes[3];
- if (abs(rot_dx) < 0.025f)
- rot_dx = 0.0f;
- if (abs(rot_dy) < 0.025f)
- rot_dy = 0.0f;
- rot_dx = std::abs(rot_dx) * rot_dx * 1.5f;
- rot_dy = std::abs(rot_dy) * rot_dy * 1.5f;
- g_cam_yaw += -rot_dx * static_cast<float>(dt);
- g_cam_yaw = std::remainder(g_cam_yaw, 2 * glm::pi<float>());
- g_cam_pitch += rot_dy * static_cast<float>(dt);
- g_cam_pitch = glm::clamp(g_cam_pitch, -1.5f, 1.5f);
- float trans_dx = axes[0];
- float trans_dy = axes[1];
- if (abs(trans_dx) < 0.05f)
- trans_dx = 0.0f;
- if (abs(trans_dy) < 0.05f)
- trans_dy = 0.0f;
- glm::vec3 position = g_cam_pos;
- // calculate camera direction
- glm::vec3 vfront{ 0, 0, -1 };
- vfront = glm::mat3(glm::rotate(glm::mat4(1), g_cam_pitch, glm::vec3(1, 0, 0))) * vfront;
- vfront = glm::mat3(glm::rotate(glm::mat4(1), g_cam_yaw, glm::vec3(0, 1, 0))) * vfront;
- glm::vec3 vUp = glm::vec3(0.0f, 1.0f, 0.0f);
- glm::vec3 vRighthand = glm::normalize(glm::cross(vfront, vUp));
- g_cam_pos = position + trans_dy * 20.0f * vfront * (float)dt + trans_dx * 20.0f * vRighthand * (float)dt;
- }
- /// Synchronizes scene updates to a frequenzy of 100 Hz.
- /// This function assumes that the scene is updated each time it returns true.
- /// @return true iff the scene should be updated now.
- bool NeedSceneUpdate()
- {
- static auto nextUpdate = std::chrono::high_resolution_clock::now();
- auto now = std::chrono::high_resolution_clock::now();
- if (nextUpdate > now)
- return false;
- nextUpdate += std::chrono::milliseconds{ 10 };
- return true;
- }
- /// Applies per-frame updates of the scene.
- void UpdateScene()
- {
- // update camera postion and orientation from gamepad input
- applyJoystickInput();
- // update camera yaw and pitch
- int rot_horz_target = KeyAxisValue(g_window, GLFW_KEY_RIGHT, GLFW_KEY_LEFT);
- int rot_vert_target = KeyAxisValue(g_window, GLFW_KEY_DOWN, GLFW_KEY_UP);
- g_diff_cam_yaw = lerp(0.1f, g_diff_cam_yaw, static_cast<float>(rot_horz_target));
- g_diff_cam_pitch = lerp(0.1f, g_diff_cam_pitch, static_cast<float>(rot_vert_target));
- g_cam_yaw += 0.02f * g_diff_cam_yaw;
- g_cam_yaw = std::remainder(g_cam_yaw, 2 * glm::pi<float>());
- g_cam_pitch += 0.03f * g_diff_cam_pitch;
- g_cam_pitch = glm::clamp(g_cam_pitch, -1.5f, 1.5f);
- // calculate camera direction
- glm::vec3 cam_dir{ 0, 0, -1 };
- cam_dir = glm::mat3(glm::rotate(glm::mat4(1), g_cam_pitch, glm::vec3(1, 0, 0))) * cam_dir;
- cam_dir = glm::mat3(glm::rotate(glm::mat4(1), g_cam_yaw, glm::vec3(0, 1, 0))) * cam_dir;
- // calculate the rotation matrix used to orient the target velocity along the camera
- glm::vec2 cam_dir_2d{ cam_dir.x, cam_dir.z }; // only consider the xz-plane
- cam_dir_2d = glm::normalize(cam_dir_2d);
- glm::mat3 rot{ -cam_dir_2d.y, 0, cam_dir_2d.x, 0, 1, 0, -cam_dir_2d.x, 0, -cam_dir_2d.y };
- // update camera velocity and position
- glm::vec3 target_velocity =
- rot * glm::vec3{ KeyAxisValue(g_window, GLFW_KEY_A, GLFW_KEY_D),
- KeyAxisValue(g_window, GLFW_KEY_LEFT_SHIFT, GLFW_KEY_SPACE),
- KeyAxisValue(g_window, GLFW_KEY_W, GLFW_KEY_S) };
- g_cam_velocity = lerp(0.08f, g_cam_velocity, target_velocity);
- g_cam_pos += 0.05f * g_cam_velocity;
- // calculate view matrix
- g_per_frame.view = glm::lookAt(g_cam_pos, // camera position
- g_cam_pos + cam_dir, // point to look towards (not a direction)
- glm::vec3{ 0, 1, 0 }); // up direction
- // calculate projection matrix
- float aspect = static_cast<float>(g_window_width) / g_window_height;
- g_per_frame.proj = glm::perspective(0.25f * glm::pi<float>(), // fov in y-direction in radians
- aspect, // aspect ratio w/h
- 0.1f, // distance to near plane
- 500.f); // distance to far plane
- /*
- * TODO: Aufgabe 2: Nutzen Sie die Parameter g_light_yaw sowie die Funktion KeyAxisValue
- * um eine Rotation der Lichtrichtung um die y-Achse zu implementieren. Die Taste 1
- * soll gegen den Uhrzeigersinn rotieren und die Taste 2 soll im Uhrzeigersinn rotieren.
- *
- * Hinweise: - Die Werte für Taste 1 und Taste 2 sind GLFW_KEY_1 und GLFW_KEY_2.
- * - Das Ergebnis der rotation muss in g_per_frame.light_dir gespeichert werden.
- */
- int state = KeyAxisValue(g_window, GLFW_KEY_1, GLFW_KEY_2);
- if (state == -1) {
- g_per_frame.light_dir.x = g_per_frame.light_dir.x * cos(g_light_yaw) - g_per_frame.light_dir.y * sin(g_light_yaw);
- g_per_frame.light_dir.z = -g_per_frame.light_dir.x * sin(g_light_yaw) + g_per_frame.light_dir.z * cos(g_light_yaw);
- }
- else if (state == 1) {
- g_per_frame.light_dir.x = g_per_frame.light_dir.x * cos(-g_light_yaw) - g_per_frame.light_dir.y * sin(-g_light_yaw);
- g_per_frame.light_dir.z = -g_per_frame.light_dir.x * sin(-g_light_yaw) + g_per_frame.light_dir.z * cos(-g_light_yaw);
- }
- }
- /// Renders a RenderBatch.
- /// @param batch The RenderBatch to render.
- void RenderObject(const RenderBatch& batch)
- {
- // upload model matrix
- glUniformMatrix4fv(glGetUniformLocation(g_shader_program_ground, "model"),
- 1, false, glm::value_ptr(batch.per_object.model));
- // render
- glBindVertexArray(batch.vao);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, batch.ibo);
- glDrawElements(GL_TRIANGLES, batch.index_count, GL_UNSIGNED_INT, nullptr);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glBindVertexArray(0);
- }
- /// Renders a frame.
- void RenderFrame()
- {
- // update per-frame uniform buffer
- glBindBuffer(GL_UNIFORM_BUFFER, g_ubo_per_frame);
- void* data = glMapBuffer(GL_UNIFORM_BUFFER, GL_WRITE_ONLY);
- memcpy(data, &g_per_frame, sizeof(g_per_frame));
- glUnmapBuffer(GL_UNIFORM_BUFFER);
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
- // setup rendering
- glViewport(0, 0, g_window_width, g_window_height);
- glClearColor(0.53f, 0.81f, 0.98f, 1);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- // render the ground
- glUseProgram(g_shader_program_ground);
- RenderObject(g_render_batch_ground);
- /*
- * TODO: Aufgabe 3 Teilaufgabe 2: Aktivieren sie Blending und rendern sie das Wasser Mesh.
- */
- //glEnable(GL_BLEND);
- //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- RenderObject(g_render_batch_water);
- //glEnable(GL_BLEND);
- //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- /*
- * TODO: Aufgabe 3 Teilaufgabe 3: Nutzen Sie das OpenGL program g_shader_program_tree_imposter
- * um die Bäume zu rendern.
- */
- // unbind buffers
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glBindVertexArray(0);
- }
- /// Updates the resolution. This is called by GLFW when the resolution changes.
- /// @param window The window that changed resolution.
- /// @param width New width.
- /// @param height New height.
- void FramebufferSizeCallback(GLFWwindow* window, int width, int height)
- {
- g_window_width = width;
- g_window_height = height;
- }
- /// Reloads the shaders if 'R' is pressed. This is called by GLFW when a key is pressed.
- /// @param window The window in which a key was pressed.
- /// @param key GLFW key code of the pressed key.
- /// @param scancode platform-specific key code.
- /// @param action One of GLFW_PRESS, GLFW_REPEAT or GLFW_RELEASE
- /// @param mods Modifier bits.
- void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
- {
- if (key == GLFW_KEY_R && action == GLFW_PRESS)
- UpdateShaders();
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement