From 801be81baa9d34da2238119bf4dfd700ba317b30 Mon Sep 17 00:00:00 2001 From: Jonas Sorgenfrei Date: Mon, 4 Apr 2022 01:48:17 +0200 Subject: [PATCH] end of file --- CMakeLists.txt | 3 + includes/learnopengl/shader_m.h | 6 +- .../computeShader.comp | 2 +- .../computeShader.h | 2 +- .../computer_shader_hello_world.cpp | 2 +- .../5.computeshader_helloworld/screenQuad.fs | 2 +- .../5.computeshader_helloworld/screenQuad.vs | 2 +- src/8.guest/2022/composite.fs | 51 +++ src/8.guest/2022/composite.vs | 9 + src/8.guest/2022/weighted_blended.cpp | 382 ++++++++++++++++++ 10 files changed, 455 insertions(+), 6 deletions(-) create mode 100644 src/8.guest/2022/composite.fs create mode 100644 src/8.guest/2022/composite.vs create mode 100644 src/8.guest/2022/weighted_blended.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 38aea7e..194824f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,6 +182,7 @@ set(GUEST_ARTICLES #8.guest/2021/3.tessellation/terrain_gpu_dist #8.guest/2021/3.tessellation/terrain_cpu_src 8.guest/2021/4.dsa + 8.guest/2022/5.computeshader_helloworld ) configure_file(configuration/root_directory.h.in configuration/root_directory.h) @@ -205,6 +206,7 @@ function(create_project_from_sources chapter demo) "src/${chapter}/${demo}/*.vs" "src/${chapter}/${demo}/*.fs" "src/${chapter}/${demo}/*.gs" + "src/${chapter}/${demo}/*.comp" ) if (demo STREQUAL "") SET(replaced "") @@ -235,6 +237,7 @@ function(create_project_from_sources chapter demo) # "src/${chapter}/${demo}/*.frag" "src/${chapter}/${demo}/*.fs" "src/${chapter}/${demo}/*.gs" + "src/${chapter}/${demo}/*.comp" ) foreach(SHADER ${SHADERS}) if(WIN32) diff --git a/includes/learnopengl/shader_m.h b/includes/learnopengl/shader_m.h index b554064..b8c96e0 100644 --- a/includes/learnopengl/shader_m.h +++ b/includes/learnopengl/shader_m.h @@ -13,6 +13,10 @@ class Shader { public: unsigned int ID; + Shader() { + ID = -1; + } + // constructor generates the shader on the fly // ------------------------------------------------------------------------ Shader(const char* vertexPath, const char* fragmentPath) @@ -135,7 +139,7 @@ public: glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]); } -private: +protected: // utility function for checking shader compilation/linking errors. // ------------------------------------------------------------------------ void checkCompileErrors(GLuint shader, std::string type) diff --git a/src/8.guest/2022/5.computeshader_helloworld/computeShader.comp b/src/8.guest/2022/5.computeshader_helloworld/computeShader.comp index f614624..c7f11d6 100644 --- a/src/8.guest/2022/5.computeshader_helloworld/computeShader.comp +++ b/src/8.guest/2022/5.computeshader_helloworld/computeShader.comp @@ -25,4 +25,4 @@ void main() { pixel.x = float(int((float(pixelCoord.x)/(gl_NumWorkGroups.x*gl_WorkGroupSize.x)+t*speed)*100)%100)/100; pixel.y = float(pixelCoord.y)/(gl_NumWorkGroups.y*gl_WorkGroupSize.y); imageStore(imgOutput, pixelCoord, pixel); -} \ No newline at end of file +} diff --git a/src/8.guest/2022/5.computeshader_helloworld/computeShader.h b/src/8.guest/2022/5.computeshader_helloworld/computeShader.h index ebe5c85..d1a4ecb 100644 --- a/src/8.guest/2022/5.computeshader_helloworld/computeShader.h +++ b/src/8.guest/2022/5.computeshader_helloworld/computeShader.h @@ -46,4 +46,4 @@ class ComputeShader : public Shader glDeleteShader(compute); } -}; \ No newline at end of file +}; diff --git a/src/8.guest/2022/5.computeshader_helloworld/computer_shader_hello_world.cpp b/src/8.guest/2022/5.computeshader_helloworld/computer_shader_hello_world.cpp index 369f5df..44aa433 100644 --- a/src/8.guest/2022/5.computeshader_helloworld/computer_shader_hello_world.cpp +++ b/src/8.guest/2022/5.computeshader_helloworld/computer_shader_hello_world.cpp @@ -193,4 +193,4 @@ void framebuffer_size_callback(GLFWwindow* window, int width, int height) // make sure the viewport matches the new window dimensions; note that width and // height will be significantly larger than specified on retina displays. glViewport(0, 0, width, height); -} \ No newline at end of file +} diff --git a/src/8.guest/2022/5.computeshader_helloworld/screenQuad.fs b/src/8.guest/2022/5.computeshader_helloworld/screenQuad.fs index bcfde96..736e174 100644 --- a/src/8.guest/2022/5.computeshader_helloworld/screenQuad.fs +++ b/src/8.guest/2022/5.computeshader_helloworld/screenQuad.fs @@ -9,4 +9,4 @@ void main() { vec3 texCol = texture(tex, TexCoords).rgb; FragColor = vec4(texCol, 1.0); -} \ No newline at end of file +} diff --git a/src/8.guest/2022/5.computeshader_helloworld/screenQuad.vs b/src/8.guest/2022/5.computeshader_helloworld/screenQuad.vs index 9f93e29..38324be 100644 --- a/src/8.guest/2022/5.computeshader_helloworld/screenQuad.vs +++ b/src/8.guest/2022/5.computeshader_helloworld/screenQuad.vs @@ -8,4 +8,4 @@ void main() { TexCoords = aTexCoords; gl_Position = vec4(aPos, 1.0); -} \ No newline at end of file +} diff --git a/src/8.guest/2022/composite.fs b/src/8.guest/2022/composite.fs new file mode 100644 index 0000000..ab1c21f --- /dev/null +++ b/src/8.guest/2022/composite.fs @@ -0,0 +1,51 @@ +#version 400 core + +// shader outputs +layout (location = 0) out vec4 frag; + +// color accumulation buffer +layout (binding = 0) uniform sampler2D accum; + +// revealage threshold buffer +layout (binding = 1) uniform sampler2D reveal; + +// epsilon number +const float EPSILON = 0.00001f; + +// caluclate floating point numbers equality accurately +bool isApproximatelyEqual(float a, float b) +{ + return abs(a - b) <= (abs(a) < abs(b) ? abs(b) : abs(a)) * EPSILON; +} + +// get the max value between three values +float max3(vec3 v) +{ + return max(max(v.x, v.y), v.z); +} + +void main() +{ + // fragment coordination + ivec2 coords = ivec2(gl_FragCoord.xy); + + // fragment revealage + float revealage = texelFetch(reveal, coords, 0).r; + + // save the blending and color texture fetch cost if there is not a transparent fragment + if (isApproximatelyEqual(revealage, 1.0f)) + discard; + + // fragment color + vec4 accumulation = texelFetch(accum, coords, 0); + + // suppress overflow + if (isinf(max3(abs(accumulation.rgb)))) + accumulation.rgb = vec3(accumulation.a); + + // prevent floating point precision bug + vec3 average_color = accumulation.rgb / max(accumulation.a, EPSILON); + + // blend pixels + frag = vec4(average_color, 1.0f - revealage); +} \ No newline at end of file diff --git a/src/8.guest/2022/composite.vs b/src/8.guest/2022/composite.vs new file mode 100644 index 0000000..87fa99c --- /dev/null +++ b/src/8.guest/2022/composite.vs @@ -0,0 +1,9 @@ +#version 400 core + +// shader inputs +layout (location = 0) in vec3 position; + +void main() +{ + gl_Position = vec4(position, 1.0f); +} \ No newline at end of file diff --git a/src/8.guest/2022/weighted_blended.cpp b/src/8.guest/2022/weighted_blended.cpp new file mode 100644 index 0000000..b382f37 --- /dev/null +++ b/src/8.guest/2022/weighted_blended.cpp @@ -0,0 +1,382 @@ +#include +#include + +#include +#include +#include + +#include +#include + +#include + +void framebuffer_size_callback(GLFWwindow* window, int width, int height); +void mouse_callback(GLFWwindow* window, double xpos, double ypos); +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +void process_input(GLFWwindow *window); +glm::mat4 calculate_model_matrix(const glm::vec3& position, const glm::vec3& rotation = glm::vec3(0.0f), const glm::vec3& scale = glm::vec3(1.0f)); + +// settings +const unsigned int SCR_WIDTH = 800; +const unsigned int SCR_HEIGHT = 600; + +// camera +Camera camera(glm::vec3(0.0f, 0.0f, 5.0f)); +float lastX = (float)SCR_WIDTH / 2.0; +float lastY = (float)SCR_HEIGHT / 2.0; +bool firstMouse = true; + +// timing +float deltaTime = 0.0f; +float lastFrame = 0.0f; + +int main(int argc, char* argv[]) +{ + // glfw: initialize and configure + // ------------------------------ + glfwInit(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + #ifdef __APPLE__ + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + #endif + + // glfw window creation + // -------------------- + GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL); + if (window == NULL) + { + std::cout << "Failed to create GLFW window" << std::endl; + glfwTerminate(); + return -1; + } + glfwMakeContextCurrent(window); + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetCursorPosCallback(window, mouse_callback); + glfwSetScrollCallback(window, scroll_callback); + + // tell GLFW to capture our mouse + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // glad: load all OpenGL function pointers + // --------------------------------------- + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD" << std::endl; + return -1; + } + + // build and compile shaders + // ------------------------- + Shader solidShader("solid.vs", "solid.fs"); + Shader transparentShader("transparent.vs", "transparent.fs"); + Shader compositeShader("composite.vs", "composite.fs"); + Shader screenShader("screen.vs", "screen.fs"); + + // set up vertex data (and buffer(s)) and configure vertex attributes + // ------------------------------------------------------------------ + float quadVertices[] = { + // positions // uv + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f + }; + + // quad VAO + unsigned int quadVAO, quadVBO; + glGenVertexArrays(1, &quadVAO); + glGenBuffers(1, &quadVBO); + glBindVertexArray(quadVAO); + glBindBuffer(GL_ARRAY_BUFFER, quadVBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); + glBindVertexArray(0); + + // set up framebuffers and their texture attachments + // ------------------------------------------------------------------ + unsigned int opaqueFBO, transparentFBO; + glGenFramebuffers(1, &opaqueFBO); + glGenFramebuffers(1, &transparentFBO); + + // set up attachments for opaque framebuffer + unsigned int opaqueTexture; + glGenTextures(1, &opaqueTexture); + glBindTexture(GL_TEXTURE_2D, opaqueTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_HALF_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + + unsigned int depthTexture; + glGenTextures(1, &depthTexture); + glBindTexture(GL_TEXTURE_2D, depthTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, opaqueFBO); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, opaqueTexture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + std::cout << "ERROR::FRAMEBUFFER:: Opaque framebuffer is not complete!" << std::endl; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // set up attachments for transparent framebuffer + unsigned int accumTexture; + glGenTextures(1, &accumTexture); + glBindTexture(GL_TEXTURE_2D, accumTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_HALF_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + + unsigned int revealTexture; + glGenTextures(1, &revealTexture); + glBindTexture(GL_TEXTURE_2D, revealTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, SCR_WIDTH, SCR_HEIGHT, 0, GL_RED, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, transparentFBO); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, accumTexture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, revealTexture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0); // opaque framebuffer's depth texture + + const GLenum transparentDrawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + glDrawBuffers(2, transparentDrawBuffers); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + std::cout << "ERROR::FRAMEBUFFER:: Transparent framebuffer is not complete!" << std::endl; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // set up transformation matrices + // ------------------------------------------------------------------ + glm::mat4 redModelMat = calculate_model_matrix(glm::vec3(0.0f, 0.0f, 1.0f)); + glm::mat4 greenModelMat = calculate_model_matrix(glm::vec3(0.0f, 0.0f, 0.0f)); + glm::mat4 blueModelMat = calculate_model_matrix(glm::vec3(0.0f, 0.0f, 2.0f)); + + // set up intermediate variables + // ------------------------------------------------------------------ + glm::vec4 zeroFillerVec(0.0f); + glm::vec4 oneFillerVec(1.0f); + + // render loop + // ----------- + while (!glfwWindowShouldClose(window)) + { + // per-frame time logic + // -------------------- + float currentFrame = glfwGetTime(); + deltaTime = currentFrame - lastFrame; + lastFrame = currentFrame; + + // camera matrices + glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f); + glm::mat4 view = camera.GetViewMatrix(); + glm::mat4 vp = projection * view; + + // input + // ----- + process_input(window); + + // render + // ------ + + // draw solid objects (solid pass) + // ------ + + // configure render states + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); + glDisable(GL_BLEND); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + // bind opaque framebuffer to render solid objects + glBindFramebuffer(GL_FRAMEBUFFER, opaqueFBO); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // use solid shader + solidShader.use(); + + // draw red quad + solidShader.setMat4("mvp", vp * redModelMat); + solidShader.setVec3("color", glm::vec3(1.0f, 0.0f, 0.0f)); + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + + // draw transparent objects (transparent pass) + // ----- + + // configure render states + glDepthMask(GL_FALSE); + glEnable(GL_BLEND); + glBlendFunci(0, GL_ONE, GL_ONE); + glBlendFunci(1, GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + glBlendEquation(GL_FUNC_ADD); + + // bind transparent framebuffer to render transparent objects + glBindFramebuffer(GL_FRAMEBUFFER, transparentFBO); + glClearBufferfv(GL_COLOR, 0, &zeroFillerVec[0]); + glClearBufferfv(GL_COLOR, 1, &oneFillerVec[0]); + + // use transparent shader + transparentShader.use(); + + // draw green quad + transparentShader.setMat4("mvp", vp * greenModelMat); + transparentShader.setVec4("color", glm::vec4(0.0f, 1.0f, 0.0f, 0.5f)); + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + + // draw blue quad + transparentShader.setMat4("mvp", vp * blueModelMat); + transparentShader.setVec4("color", glm::vec4(0.0f, 0.0f, 1.0f, 0.5f)); + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + + // draw composite image (composite pass) + // ----- + + // set render states + glDepthFunc(GL_ALWAYS); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // bind opaque framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, opaqueFBO); + + // use composite shader + compositeShader.use(); + + // draw screen quad + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, accumTexture); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, revealTexture); + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + + // draw to backbuffer (final pass) + // ----- + + // set render states + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); // enable depth writes so glClear won't ignore clearing the depth buffer + glDisable(GL_BLEND); + + // bind backbuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + // use screen shader + screenShader.use(); + + // draw final screen quad + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, opaqueTexture); + glBindVertexArray(quadVAO); + glDrawArrays(GL_TRIANGLES, 0, 6); + + // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) + // ------------------------------------------------------------------------------- + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // optional: de-allocate all resources once they've outlived their purpose: + // ------------------------------------------------------------------------ + glDeleteVertexArrays(1, &quadVAO); + glDeleteBuffers(1, &quadVBO); + glDeleteTextures(1, &opaqueTexture); + glDeleteTextures(1, &depthTexture); + glDeleteTextures(1, &accumTexture); + glDeleteTextures(1, &revealTexture); + glDeleteFramebuffers(1, &opaqueFBO); + glDeleteFramebuffers(1, &transparentFBO); + + glfwTerminate(); + + return EXIT_SUCCESS; +} + +// glfw: whenever the window size changed (by OS or user resize) this callback function executes +// --------------------------------------------------------------------------------------------- +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + // make sure the viewport matches the new window dimensions; note that width and + // height will be significantly larger than specified on retina displays. + glViewport(0, 0, width, height); +} + +// glfw: whenever the mouse moves, this callback is called +// ------------------------------------------------------- +void mouse_callback(GLFWwindow* window, double xpos, double ypos) +{ + if (firstMouse) + { + lastX = xpos; + lastY = ypos; + firstMouse = false; + } + + float xoffset = xpos - lastX; + float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top + + lastX = xpos; + lastY = ypos; + + camera.ProcessMouseMovement(xoffset, yoffset); +} + +// glfw: whenever the mouse scroll wheel scrolls, this callback is called +// ---------------------------------------------------------------------- +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ + camera.ProcessMouseScroll(yoffset); +} + +// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly +// --------------------------------------------------------------------------------------------------------- +void process_input(GLFWwindow *window) +{ + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) + camera.ProcessKeyboard(FORWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) + camera.ProcessKeyboard(BACKWARD, deltaTime); + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) + camera.ProcessKeyboard(LEFT, deltaTime); + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) + camera.ProcessKeyboard(RIGHT, deltaTime); +} + +// generate a model matrix +// --------------------------------------------------------------------------------------------------------- +glm::mat4 calculate_model_matrix(const glm::vec3& position, const glm::vec3& rotation, const glm::vec3& scale) +{ + glm::mat4 trans = glm::mat4(1.0f); + + trans = glm::translate(trans, position); + trans = glm::rotate(trans, glm::radians(rotation.x), glm::vec3(1.0, 0.0, 0.0)); + trans = glm::rotate(trans, glm::radians(rotation.y), glm::vec3(0.0, 1.0, 0.0)); + trans = glm::rotate(trans, glm::radians(rotation.z), glm::vec3(0.0, 0.0, 1.0)); + trans = glm::scale(trans, scale); + + return trans; +}