├── .gitattributes ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── bin └── .gitkeep ├── include ├── glad │ ├── include │ │ ├── KHR │ │ │ └── khrplatform.h │ │ └── glad │ │ │ └── glad.h │ └── src │ │ └── glad.c └── projects │ ├── base_app.h │ ├── camera.h │ └── glsl_program.h └── src └── projects ├── base_app ├── base_app.cpp ├── camera.cpp └── glsl_program.cpp ├── basic_cube ├── CMakeLists.txt ├── assets │ └── shaders │ │ ├── cube.frag │ │ └── cube.vert └── src │ └── main.cpp ├── compute_shader ├── CMakeLists.txt ├── assets │ └── shaders │ │ ├── cube.frag │ │ ├── cube.vert │ │ ├── full_screen_quad.frag │ │ ├── full_screen_quad.vert │ │ └── shader.comp └── src │ └── main.cpp ├── framebuffers ├── CMakeLists.txt ├── assets │ └── shaders │ │ ├── cube.frag │ │ ├── cube.vert │ │ ├── cube2.frag │ │ └── cube2.vert └── src │ └── main.cpp ├── fullscreen_quad ├── CMakeLists.txt ├── assets │ └── shaders │ │ ├── quad.frag │ │ └── quad.vert └── src │ └── main.cpp ├── geometry_shader ├── CMakeLists.txt ├── assets │ └── shaders │ │ ├── shader.frag │ │ ├── shader.geom │ │ ├── shader.tesc │ │ ├── shader.tese │ │ └── shader.vert └── src │ └── main.cpp ├── geometry_shader_normal_viewer ├── CMakeLists.txt ├── assets │ └── shaders │ │ ├── cube.frag │ │ ├── cube.vert │ │ ├── normal_viewer.frag │ │ ├── normal_viewer.geom │ │ └── normal_viewer.vert └── src │ └── main.cpp ├── multiple_attributes_and_buffers ├── CMakeLists.txt ├── assets │ └── shaders │ │ ├── shader.frag │ │ └── shader.vert └── src │ └── main.cpp ├── phong_lighting ├── CMakeLists.txt ├── assets │ ├── images │ │ ├── container2.jpg │ │ └── container2_specular.jpg │ └── shaders │ │ ├── lamp.frag │ │ ├── lamp.vert │ │ ├── phong.frag │ │ └── phong.vert └── src │ └── main.cpp ├── point_sprites ├── CMakeLists.txt ├── assets │ └── shaders │ │ ├── point_sprite.frag │ │ └── point_sprite.vert └── src │ └── main.cpp ├── raytracer ├── CMakeLists.txt ├── assets │ └── shaders │ │ ├── blit.frag │ │ ├── blit.vert │ │ ├── raytracer.frag │ │ ├── raytracer.vert │ │ ├── trace_prepare.frag │ │ └── trace_prepare.vert └── src │ └── main.cpp ├── read_pixels ├── CMakeLists.txt ├── assets │ └── shaders │ │ ├── cube.frag │ │ └── cube.vert └── src │ └── main.cpp ├── tesselation ├── CMakeLists.txt ├── assets │ └── shaders │ │ ├── shader.frag │ │ ├── shader.tesc │ │ ├── shader.tese │ │ ├── shader.vert │ │ ├── shader2.tese │ │ └── shader3.tese └── src │ └── main.cpp ├── tesselation_displacement_map ├── CMakeLists.txt ├── assets │ ├── images │ │ ├── noise.jpg │ │ └── noise_color.jpg │ └── shaders │ │ ├── terrain_disp.frag │ │ ├── terrain_disp.tesc │ │ ├── terrain_disp.tese │ │ └── terrain_disp.vert └── src │ └── main.cpp ├── texture ├── CMakeLists.txt ├── assets │ ├── images │ │ └── 0.jpg │ └── shaders │ │ ├── simple_quad.frag │ │ └── simple_quad.vert └── src │ └── main.cpp ├── texture_array ├── CMakeLists.txt ├── assets │ ├── shaders │ │ ├── simple_quad.frag │ │ └── simple_quad.vert │ └── texture_array │ │ ├── 0.jpg │ │ ├── 1.jpg │ │ ├── 2.jpg │ │ └── 3.jpg └── src │ └── main.cpp └── transform_feedback ├── CMakeLists.txt └── src └── main.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | *.jpg filter=lfs diff=lfs merge=lfs -text 2 | '*.jpg' filter=lfs diff=lfs merge=lfs -text 3 | *.jpeg filter=lfs diff=lfs merge=lfs -text 4 | *.png filter=lfs diff=lfs merge=lfs -text 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | 3 | bin/* 4 | !bin/.gitkeep 5 | 6 | build/* 7 | !build/.gitkeep 8 | 9 | *kdev4* 10 | .vscode/ 11 | .idea 12 | cmake-build* 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "include/glfw"] 2 | path = include/glfw 3 | url = https://github.com/glfw/glfw.git 4 | [submodule "include/stb"] 5 | path = include/stb 6 | url = https://github.com/nothings/stb.git 7 | [submodule "include/glm"] 8 | path = include/glm 9 | url = https://github.com/g-truc/glm.git 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project initialization 2 | cmake_minimum_required(VERSION 2.7.2 FATAL_ERROR) 3 | 4 | project("opengl_examples") 5 | 6 | # Compiler settings 7 | if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") 8 | # Wall: Enable all warnings about constructors 9 | # Wextra: Enable some extra warnings 10 | # Werror: Treat all warnings as errors 11 | # set(warnings "-Wall -Wextra -Werror") 12 | elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 13 | # W4: Show the most warnings 14 | # WX: Treat all warnings as errors 15 | # EHsc: Only emit code for exception filters when detected that the code in the try block might throw a C++ exception 16 | set(warnings "/W4 /EHsc") 17 | set(CMAKE_CONFIGURATION_TYPES Debug Release) 18 | endif() 19 | 20 | # Set warnings 21 | if (NOT CONFIGURED_ONCE) 22 | set(CMAKE_CXX_FLAGS "${warnings}" CACHE STRING "Flags used by the compiler during all build types." FORCE) 23 | endif() 24 | 25 | # Set variables 26 | set(INCLUDE_DIR ${CMAKE_SOURCE_DIR}/include) 27 | set(PROJECTS_DIR ${CMAKE_SOURCE_DIR}/src/projects) 28 | 29 | # Add include directories 30 | include_directories(${INCLUDE_DIR}) 31 | include_directories(${INCLUDE_DIR}/projects) 32 | include_directories(${INCLUDE_DIR}/glad/include) 33 | 34 | # Build and include GLFW 35 | set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) 36 | set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) 37 | set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) 38 | add_subdirectory(${INCLUDE_DIR}/glfw) 39 | include_directories(${INCLUDE_DIR}/glfw/include) 40 | include_directories(${INCLUDE_DIR}/glfw/deps) 41 | 42 | # Add subdirectories (projects) 43 | add_subdirectory(${PROJECTS_DIR}/basic_cube) 44 | add_subdirectory(${PROJECTS_DIR}/multiple_attributes_and_buffers) 45 | add_subdirectory(${PROJECTS_DIR}/compute_shader) 46 | add_subdirectory(${PROJECTS_DIR}/texture) 47 | add_subdirectory(${PROJECTS_DIR}/texture_array) 48 | add_subdirectory(${PROJECTS_DIR}/tesselation) 49 | add_subdirectory(${PROJECTS_DIR}/tesselation_displacement_map) 50 | add_subdirectory(${PROJECTS_DIR}/transform_feedback) 51 | add_subdirectory(${PROJECTS_DIR}/geometry_shader) 52 | add_subdirectory(${PROJECTS_DIR}/geometry_shader_normal_viewer) 53 | add_subdirectory(${PROJECTS_DIR}/framebuffers) 54 | add_subdirectory(${PROJECTS_DIR}/read_pixels) 55 | add_subdirectory(${PROJECTS_DIR}/point_sprites) 56 | add_subdirectory(${PROJECTS_DIR}/phong_lighting) 57 | add_subdirectory(${PROJECTS_DIR}/raytracer) 58 | add_subdirectory(${PROJECTS_DIR}/fullscreen_quad) 59 | 60 | # Create a variable that is set to true and cached after the first configuration 61 | set(CONFIGURED_ONCE TRUE CACHE INTERNAL "A flag showing that CMake has configured at least once.") 62 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Willy Nolan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DSA OpenGL Examples 2 | *Examples of DSA OpenGL* 3 | 4 | ## Description 5 | The evolution of OpenGL basically goes like this: 6 | Immediate mode -> Modern OpenGL (3.0+) -> [Direct State Access](https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_direct_state_access.txt) -> Vulkan. 7 | 8 | Direct State Access in OpenGL provides a nice balance between verbosity and control. In particular, getting rid of the “state-machine” construct makes programming feel more similar to C++. 9 | 10 | Early OpenGL does not provide enough control to the user, but Vulkan can make getting started with graphics very intimidating and prototyping / experimenting with the GPU challenging. 11 | 12 | 13 | The examples contained in this repo examine many OpenGL concepts and almost exclusively use DSA techniques. There remains more that could be covered. 14 | 15 | A very small amount of redundant code was abstracted in the `base_app` class. This takes care of loading shaders and setting up a window. 16 | 17 | Most of the examples in this project are API examples with limited graphical output, but there is some interesting graphical output. 18 | 19 |

20 | 21 |

22 | 23 | ### Dependencies 24 | - [GLM](https://github.com/g-truc/glm) (math) 25 | - [GLFW](https://github.com/glfw/glfw) (window creation) 26 | - [GLAD](https://github.com/Dav1dde/gladhttps://github.com/Dav1dde/glad) (OpenGL function loading) 27 | - [STB](https://github.com/nothings/stb) (image loading) 28 | 29 | (All of these are included) 30 | 31 | ## Tested On 32 | - Linux 33 | - Windows 34 | 35 | ## To Build 36 | This project uses [Git LFS](https://git-lfs.github.com/) for assets and CMAKE to build 37 | - Clone this project using git 38 | - From the root of this project update all the submodules with: `git submodule update --init --recursive` 39 | - From the root of this project type `cmake -G ${GENERATOR} -DCMAKE_BUILD_TYPE=Debug` or open the project in an editor that directly supports CMAKE files 40 | 41 | Where `${GENERATOR}` is the platform (i.e. `"Unix Makefiles"`) 42 | 43 | ## To Use 44 | - Build and run the individual examples 45 | 46 | NOTE: Asset paths are relative and expect your current working directory to be a directory below the assets folder. 47 | 48 | i.e. the `basic_cube` example should be run from `/bin/basic_cube/Debug` 49 | 50 | ## Project Structure 51 | - `bin`: This is where the final binary applications are put 52 | - `build`: This holds the generates build files 53 | - `include`: This includes headers from this project and third parties 54 | - `src`: This holds the source code for these examples 55 | 56 | There are three classes used in the examples: `Application`, `Camera` and `Glslprogram`. 57 | These provided basic needed functionality. 58 | 59 | ## Functionality 60 | 61 | #### base_app 62 | This is an abstract base class used in the examples but it has no graphical output 63 | 64 | #### basic_cube 65 | This is a rotating cube with model-space positions as colors 66 | 67 | #### compute_shader 68 | This renders a cube to an FBO then uses a compute shader to invert that image 69 | 70 | #### framebuffers 71 | This renders to a texture using an FBO 72 | 73 | #### geometry_shader 74 | This is a pass through geometry shader 75 | 76 | #### geometry_shader_normal_viewer 77 | This is a cube geometry shader which converts faces to lines to show normals 78 | 79 | #### multiple_attributes_and_buffers 80 | This uses mapped buffers to upload data 81 | 82 | The buffer backing the VAO is switched to change vertex data quickly 83 | 84 | The attribute binding could also be switched to change the vertex data quickly 85 | 86 | Interactivity: The spacebar toggles which buffer backs the active VAO 87 | 88 | #### phong_lighting 89 | This is a Phong lighting example 90 | 91 | Interactivity: This implements cursor lock to control camera position 92 | 93 | Mouse wheel to move forward / black 94 | 95 | #### point_sprites 96 | This is a very simple point sprite example 97 | 98 | #### raytracer 99 | A raytracer 100 | 101 | #### read_pixels 102 | This uses glReadnPixels and mapped Pixel Buffer Objects (to improve performance) 103 | 104 | It saves the rendered image as a .tga file 105 | 106 | Interactivity: Press spacebar to take a screenshot 107 | 108 | #### tesselation 109 | This compares the various tesselation spacing options 110 | 111 | Interactivity: The up arrow increases tesselation, down arrow decreases tesselationThis compares the var 112 | 113 | #### tesselation_displacement_map 114 | This uses a displacment map to offset vertices and tesselation 115 | 116 | This uses an instanced quad with vertices embedded in the shader (there are no vertex attributes) 117 | 118 | Interactivity: `w` key toggles showing wireframeThis uses a displacment map to offset vertices and tesselation 119 | 120 | #### texture 121 | This loads an image in the S3TC format 122 | 123 | It checks to make sure it is compressed and gets the compressed size 124 | 125 | #### texture_array 126 | This loads a jpeg image and stores it in the S3TC compressed format 127 | 128 | It checks that it is compressed and gets the compressed size 129 | 130 | #### transform_feedback 131 | This calculates the square root of some values and reads it back with transform feedback 132 | 133 | There is no graphical output 134 | 135 | ## Extra notes 136 | - To use these examples your graphics card must support at least OpenGL 4.5 137 | 138 | ### License 139 | 140 | :copyright: Willy Nolan 2018 141 | 142 | [MIT License](http://en.wikipedia.org/wiki/MIT_License) 143 | -------------------------------------------------------------------------------- /bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/computersarecool/dsa_opengl_examples/658196529420dcdded5436ef4a118cb4a3bc9b2c/bin/.gitkeep -------------------------------------------------------------------------------- /include/glad/include/KHR/khrplatform.h: -------------------------------------------------------------------------------- 1 | #ifndef __khrplatform_h_ 2 | #define __khrplatform_h_ 3 | 4 | /* 5 | ** Copyright (c) 2008-2009 The Khronos Group Inc. 6 | ** 7 | ** Permission is hereby granted, free of charge, to any person obtaining a 8 | ** copy of this software and/or associated documentation files (the 9 | ** "Materials"), to deal in the Materials without restriction, including 10 | ** without limitation the rights to use, copy, modify, merge, publish, 11 | ** distribute, sublicense, and/or sell copies of the Materials, and to 12 | ** permit persons to whom the Materials are furnished to do so, subject to 13 | ** the following conditions: 14 | ** 15 | ** The above copyright notice and this permission notice shall be included 16 | ** in all copies or substantial portions of the Materials. 17 | ** 18 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 25 | */ 26 | 27 | /* Khronos platform-specific types and definitions. 28 | * 29 | * $Revision: 32517 $ on $Date: 2016-03-11 02:41:19 -0800 (Fri, 11 Mar 2016) $ 30 | * 31 | * Adopters may modify this file to suit their platform. Adopters are 32 | * encouraged to submit platform specific modifications to the Khronos 33 | * group so that they can be included in future versions of this file. 34 | * Please submit changes by sending them to the public Khronos Bugzilla 35 | * (http://khronos.org/bugzilla) by filing a bug against product 36 | * "Khronos (general)" component "Registry". 37 | * 38 | * A predefined template which fills in some of the bug fields can be 39 | * reached using http://tinyurl.com/khrplatform-h-bugreport, but you 40 | * must create a Bugzilla login first. 41 | * 42 | * 43 | * See the Implementer's Guidelines for information about where this file 44 | * should be located on your system and for more details of its use: 45 | * http://www.khronos.org/registry/implementers_guide.pdf 46 | * 47 | * This file should be included as 48 | * #include 49 | * by Khronos client API header files that use its types and defines. 50 | * 51 | * The types in khrplatform.h should only be used to define API-specific types. 52 | * 53 | * Types defined in khrplatform.h: 54 | * khronos_int8_t signed 8 bit 55 | * khronos_uint8_t unsigned 8 bit 56 | * khronos_int16_t signed 16 bit 57 | * khronos_uint16_t unsigned 16 bit 58 | * khronos_int32_t signed 32 bit 59 | * khronos_uint32_t unsigned 32 bit 60 | * khronos_int64_t signed 64 bit 61 | * khronos_uint64_t unsigned 64 bit 62 | * khronos_intptr_t signed same number of bits as a pointer 63 | * khronos_uintptr_t unsigned same number of bits as a pointer 64 | * khronos_ssize_t signed size 65 | * khronos_usize_t unsigned size 66 | * khronos_float_t signed 32 bit floating point 67 | * khronos_time_ns_t unsigned 64 bit time in nanoseconds 68 | * khronos_utime_nanoseconds_t unsigned time interval or absolute time in 69 | * nanoseconds 70 | * khronos_stime_nanoseconds_t signed time interval in nanoseconds 71 | * khronos_boolean_enum_t enumerated boolean type. This should 72 | * only be used as a base type when a client API's boolean type is 73 | * an enum. Client APIs which use an integer or other type for 74 | * booleans cannot use this as the base type for their boolean. 75 | * 76 | * Tokens defined in khrplatform.h: 77 | * 78 | * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. 79 | * 80 | * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. 81 | * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. 82 | * 83 | * Calling convention macros defined in this file: 84 | * KHRONOS_APICALL 85 | * KHRONOS_APIENTRY 86 | * KHRONOS_APIATTRIBUTES 87 | * 88 | * These may be used in function prototypes as: 89 | * 90 | * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( 91 | * int arg1, 92 | * int arg2) KHRONOS_APIATTRIBUTES; 93 | */ 94 | 95 | /*------------------------------------------------------------------------- 96 | * Definition of KHRONOS_APICALL 97 | *------------------------------------------------------------------------- 98 | * This precedes the return type of the function in the function prototype. 99 | */ 100 | #if defined(_WIN32) && !defined(__SCITECH_SNAP__) 101 | # define KHRONOS_APICALL __declspec(dllimport) 102 | #elif defined (__SYMBIAN32__) 103 | # define KHRONOS_APICALL IMPORT_C 104 | #elif defined(__ANDROID__) 105 | # define KHRONOS_APICALL __attribute__((visibility("default"))) 106 | #else 107 | # define KHRONOS_APICALL 108 | #endif 109 | 110 | /*------------------------------------------------------------------------- 111 | * Definition of KHRONOS_APIENTRY 112 | *------------------------------------------------------------------------- 113 | * This follows the return type of the function and precedes the function 114 | * name in the function prototype. 115 | */ 116 | #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) 117 | /* Win32 but not WinCE */ 118 | # define KHRONOS_APIENTRY __stdcall 119 | #else 120 | # define KHRONOS_APIENTRY 121 | #endif 122 | 123 | /*------------------------------------------------------------------------- 124 | * Definition of KHRONOS_APIATTRIBUTES 125 | *------------------------------------------------------------------------- 126 | * This follows the closing parenthesis of the function prototype arguments. 127 | */ 128 | #if defined (__ARMCC_2__) 129 | #define KHRONOS_APIATTRIBUTES __softfp 130 | #else 131 | #define KHRONOS_APIATTRIBUTES 132 | #endif 133 | 134 | /*------------------------------------------------------------------------- 135 | * basic type definitions 136 | *-----------------------------------------------------------------------*/ 137 | #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) 138 | 139 | 140 | /* 141 | * Using 142 | */ 143 | #include 144 | typedef int32_t khronos_int32_t; 145 | typedef uint32_t khronos_uint32_t; 146 | typedef int64_t khronos_int64_t; 147 | typedef uint64_t khronos_uint64_t; 148 | #define KHRONOS_SUPPORT_INT64 1 149 | #define KHRONOS_SUPPORT_FLOAT 1 150 | 151 | #elif defined(__VMS ) || defined(__sgi) 152 | 153 | /* 154 | * Using 155 | */ 156 | #include 157 | typedef int32_t khronos_int32_t; 158 | typedef uint32_t khronos_uint32_t; 159 | typedef int64_t khronos_int64_t; 160 | typedef uint64_t khronos_uint64_t; 161 | #define KHRONOS_SUPPORT_INT64 1 162 | #define KHRONOS_SUPPORT_FLOAT 1 163 | 164 | #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) 165 | 166 | /* 167 | * Win32 168 | */ 169 | typedef __int32 khronos_int32_t; 170 | typedef unsigned __int32 khronos_uint32_t; 171 | typedef __int64 khronos_int64_t; 172 | typedef unsigned __int64 khronos_uint64_t; 173 | #define KHRONOS_SUPPORT_INT64 1 174 | #define KHRONOS_SUPPORT_FLOAT 1 175 | 176 | #elif defined(__sun__) || defined(__digital__) 177 | 178 | /* 179 | * Sun or Digital 180 | */ 181 | typedef int khronos_int32_t; 182 | typedef unsigned int khronos_uint32_t; 183 | #if defined(__arch64__) || defined(_LP64) 184 | typedef long int khronos_int64_t; 185 | typedef unsigned long int khronos_uint64_t; 186 | #else 187 | typedef long long int khronos_int64_t; 188 | typedef unsigned long long int khronos_uint64_t; 189 | #endif /* __arch64__ */ 190 | #define KHRONOS_SUPPORT_INT64 1 191 | #define KHRONOS_SUPPORT_FLOAT 1 192 | 193 | #elif 0 194 | 195 | /* 196 | * Hypothetical platform with no float or int64 support 197 | */ 198 | typedef int khronos_int32_t; 199 | typedef unsigned int khronos_uint32_t; 200 | #define KHRONOS_SUPPORT_INT64 0 201 | #define KHRONOS_SUPPORT_FLOAT 0 202 | 203 | #else 204 | 205 | /* 206 | * Generic fallback 207 | */ 208 | #include 209 | typedef int32_t khronos_int32_t; 210 | typedef uint32_t khronos_uint32_t; 211 | typedef int64_t khronos_int64_t; 212 | typedef uint64_t khronos_uint64_t; 213 | #define KHRONOS_SUPPORT_INT64 1 214 | #define KHRONOS_SUPPORT_FLOAT 1 215 | 216 | #endif 217 | 218 | 219 | /* 220 | * Types that are (so far) the same on all platforms 221 | */ 222 | typedef signed char khronos_int8_t; 223 | typedef unsigned char khronos_uint8_t; 224 | typedef signed short int khronos_int16_t; 225 | typedef unsigned short int khronos_uint16_t; 226 | 227 | /* 228 | * Types that differ between LLP64 and LP64 architectures - in LLP64, 229 | * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears 230 | * to be the only LLP64 architecture in current use. 231 | */ 232 | #ifdef _WIN64 233 | typedef signed long long int khronos_intptr_t; 234 | typedef unsigned long long int khronos_uintptr_t; 235 | typedef signed long long int khronos_ssize_t; 236 | typedef unsigned long long int khronos_usize_t; 237 | #else 238 | typedef signed long int khronos_intptr_t; 239 | typedef unsigned long int khronos_uintptr_t; 240 | typedef signed long int khronos_ssize_t; 241 | typedef unsigned long int khronos_usize_t; 242 | #endif 243 | 244 | #if KHRONOS_SUPPORT_FLOAT 245 | /* 246 | * Float type 247 | */ 248 | typedef float khronos_float_t; 249 | #endif 250 | 251 | #if KHRONOS_SUPPORT_INT64 252 | /* Time types 253 | * 254 | * These types can be used to represent a time interval in nanoseconds or 255 | * an absolute Unadjusted System Time. Unadjusted System Time is the number 256 | * of nanoseconds since some arbitrary system event (e.g. since the last 257 | * time the system booted). The Unadjusted System Time is an unsigned 258 | * 64 bit value that wraps back to 0 every 584 years. Time intervals 259 | * may be either signed or unsigned. 260 | */ 261 | typedef khronos_uint64_t khronos_utime_nanoseconds_t; 262 | typedef khronos_int64_t khronos_stime_nanoseconds_t; 263 | #endif 264 | 265 | /* 266 | * Dummy value used to pad enum types to 32 bits. 267 | */ 268 | #ifndef KHRONOS_MAX_ENUM 269 | #define KHRONOS_MAX_ENUM 0x7FFFFFFF 270 | #endif 271 | 272 | /* 273 | * Enumerated boolean type 274 | * 275 | * Values other than zero should be considered to be true. Therefore 276 | * comparisons should not be made against KHRONOS_TRUE. 277 | */ 278 | typedef enum { 279 | KHRONOS_FALSE = 0, 280 | KHRONOS_TRUE = 1, 281 | KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM 282 | } khronos_boolean_enum_t; 283 | 284 | #endif /* __khrplatform_h_ */ 285 | -------------------------------------------------------------------------------- /include/projects/base_app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GLFW_INCLUDE_NONE 4 | #include "GLFW/glfw3.h" 5 | 6 | class Application 7 | { 8 | public: 9 | virtual void run() final; 10 | 11 | protected: 12 | struct AppInfo 13 | { 14 | const char* title; 15 | int window_width; 16 | int window_height; 17 | int major_version; 18 | int minor_version; 19 | int samples; 20 | int cursor; 21 | bool resizeable; 22 | }; 23 | 24 | AppInfo m_info{}; 25 | GLFWwindow* m_window{}; 26 | 27 | virtual void set_info(); 28 | virtual void init() final; 29 | virtual void setup() {} 30 | virtual void draw() final; 31 | virtual void render(double /* current_time */) {} 32 | virtual void shutdown() {} 33 | virtual void on_key(int key, int action); 34 | virtual void on_resize(int width, int height) {} 35 | virtual void on_mouse_button(int button, int action) {} 36 | virtual void on_mouse_move(double x_pos, double y_pos) {} 37 | virtual void on_mouse_wheel(double x_offset, double y_offset) {} 38 | static void _check_gl_error(const char* file, int line); 39 | #define check_gl_error() _check_gl_error(__FILE__,__LINE__) 40 | }; 41 | -------------------------------------------------------------------------------- /include/projects/camera.h: -------------------------------------------------------------------------------- 1 | // Much taken from https://learnopengl.com 2 | 3 | #pragma once 4 | 5 | #include "glm/glm/glm.hpp" 6 | 7 | // This enum class is used as an abstraction of window-system specific input methods 8 | enum class Camera_Movement{ 9 | FORWARD, 10 | BACKWARD, 11 | LEFT, 12 | RIGHT 13 | }; 14 | 15 | // These are default arguments for the constructors 16 | namespace { 17 | float default_yaw{ -90.0f }; 18 | float default_pitch{ 0.0f }; 19 | float default_movement_speed{ 2.5f }; 20 | float default_mouse_sensitivity{ 0.1f }; 21 | float default_maximum_zoom{ 45.0f }; 22 | glm::vec3 default_front{ 0.0f, 0.0f, -1.0f }; 23 | glm::vec3 default_up{ 0.0f, 1.0f, 0.0f }; 24 | glm::vec3 default_position{ 0.0f }; 25 | 26 | // These are used in the implementation 27 | const float default_minimum_pitch{ -89.0f }; 28 | const float default_maximum_pitch{ 89.0f }; 29 | glm::vec3 default_right{ 1.0f, 0.0f, 0.0f }; 30 | glm::vec3 default_world_up{ 0.0f, 1.0f, 0.0f }; 31 | float default_near_plane{ 0.1f }; 32 | float default_far_plane{ 1000.0f }; 33 | } 34 | 35 | // An abstract camera class that processes input and calculates the corresponding Euler angles, vectors and matrices 36 | class Camera 37 | { 38 | public: 39 | // Input handlers 40 | void process_keyboard(Camera_Movement direction, float delta_time); 41 | void process_mouse_movement(float x_offset, float y_offset, bool constrain_pitch = true); 42 | void process_mouse_scroll(float y_offset); 43 | 44 | // Setters 45 | void set_position(float x, float y, float z); 46 | void set_position(glm::vec3 position); 47 | 48 | // Getters 49 | glm::mat4 get_view_matrix() const; 50 | glm::mat4 get_proj_matrix() const; 51 | glm::vec3 get_pos() const; 52 | glm::vec3 get_front() const; 53 | 54 | // Constructors 55 | explicit Camera(const glm::vec3& position = default_position, const glm::vec3& front = default_front, const glm::vec3& up = default_up, float yaw = default_yaw, float pitch = default_pitch, float speed = default_movement_speed, float zoom = default_maximum_zoom, float sensitivity = default_mouse_sensitivity); 56 | Camera(float pos_x, float pos_y, float pos_z, float up_x = default_up.x, float up_y = default_up.y, float up_z = default_up.z, float yaw = default_yaw, float pitch = default_pitch); 57 | 58 | private: 59 | const float m_minimum_pitch; 60 | const float m_maximum_pitch; 61 | float m_yaw; 62 | float m_pitch; 63 | float m_near_plane; 64 | float m_far_plane; 65 | float m_movement_speed; 66 | float m_zoom; 67 | float m_mouse_sensitivity; 68 | float m_maximum_zoom; 69 | float m_minimum_zoom; 70 | glm::vec3 m_front; 71 | glm::vec3 m_up; 72 | glm::vec3 m_right; 73 | glm::vec3 m_world_up; 74 | glm::vec3 m_position; 75 | 76 | void update_camera_vectors(); 77 | }; 78 | -------------------------------------------------------------------------------- /include/projects/glsl_program.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "glad/glad.h" 6 | #include "glm/glm/glm.hpp" 7 | 8 | class GlslProgram 9 | { 10 | public: 11 | struct Format { 12 | public: 13 | std::string m_vertex_shader; 14 | std::string m_tess_control_shader; 15 | std::string m_tess_eval_shader; 16 | std::string m_geometry_shader; 17 | std::string m_fragment_shader; 18 | std::string m_compute_shader; 19 | 20 | Format& vertex(const std::string& shader_path); 21 | Format& tess_control(const std::string& shader_path); 22 | Format& tess_eval(const std::string& shader_path); 23 | Format& geometry(const std::string& shader_path); 24 | Format& fragment(const std::string& shader_path); 25 | Format& compute(const std::string& shader_path); 26 | 27 | private: 28 | std::string load_shader(const std::string& shader_path); 29 | }; 30 | 31 | void use() const; 32 | void introspect() const; 33 | void uniform(const std::string& name, GLboolean value) const; 34 | void uniform(const std::string& name, GLuint value) const; 35 | void uniform(const std::string& name, GLfloat value) const; 36 | void uniform(const std::string& name, GLdouble value) const; 37 | void uniform(const std::string& name, const glm::vec2& value) const; 38 | void uniform(const std::string& name, const glm::vec3& value) const; 39 | void uniform(const std::string& name, const glm::vec4& value) const; 40 | void uniform(const std::string& name, const glm::mat3& value) const; 41 | void uniform(const std::string& name, const glm::mat4& value) const; 42 | 43 | explicit GlslProgram(const Format& format, bool separable = false); 44 | 45 | private: 46 | GLuint m_handle; 47 | GLuint compile_shader(const std::string& shader_string, GLenum shader_type) const; 48 | void check_compile_errors(GLuint program_or_shader, GLenum program_or_shader_type) const; 49 | }; -------------------------------------------------------------------------------- /src/projects/base_app/base_app.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "glad/glad.h" 4 | 5 | #include "base_app.h" 6 | 7 | // Set window and context parameters 8 | void Application::set_info() 9 | { 10 | m_info.title = "OpenGL Example"; 11 | m_info.window_width = 800; 12 | m_info.window_height = 800; 13 | m_info.major_version = 4; 14 | m_info.minor_version = 4; 15 | m_info.samples = 0; 16 | m_info.resizeable = false; 17 | m_info.cursor = GLFW_CURSOR_NORMAL; 18 | } 19 | 20 | // Initialize GLFW, a window and GLAD 21 | void Application::init() 22 | { 23 | if (!glfwInit()) 24 | { 25 | std::cerr << "Failed to initialize GLFW" << std::endl; 26 | glfwTerminate(); 27 | throw; 28 | } 29 | 30 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, m_info.major_version); 31 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, m_info.minor_version); 32 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 33 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 34 | glfwWindowHint(GLFW_RESIZABLE, m_info.resizeable); 35 | glfwWindowHint(GLFW_SAMPLES, m_info.samples); 36 | 37 | // Create window 38 | m_window = glfwCreateWindow(m_info.window_width, m_info.window_height, m_info.title, nullptr, nullptr); 39 | if (!m_window) 40 | { 41 | std::cerr << "Failed to open window" << std::endl; 42 | throw; 43 | } 44 | glfwMakeContextCurrent(m_window); 45 | glfwSetWindowUserPointer(m_window, this); 46 | glfwSetInputMode(m_window, GLFW_CURSOR, m_info.cursor); 47 | 48 | // Define GLFW interaction callbacks 49 | auto glfw_on_resize = [](GLFWwindow* w, int width, int height) 50 | { 51 | static_cast(glfwGetWindowUserPointer(w))->on_resize(width, height); 52 | }; 53 | 54 | auto glfw_on_key = [](GLFWwindow* w, int key, int scancode, int action, int mods) 55 | { 56 | static_cast(glfwGetWindowUserPointer(w))->on_key(key, action); 57 | }; 58 | 59 | auto glfw_on_mouse_button = [](GLFWwindow* w, int button, int action, int mods) 60 | { 61 | static_cast(glfwGetWindowUserPointer(w))->on_mouse_button(button, action); 62 | }; 63 | 64 | auto glfw_on_mouse_move = [](GLFWwindow* w, double x_pos, double y_pos) 65 | { 66 | static_cast(glfwGetWindowUserPointer(w))->on_mouse_move(x_pos, y_pos); 67 | }; 68 | 69 | auto glfw_on_mouse_wheel = [](GLFWwindow* w, double x_offset, double y_offset) 70 | { 71 | static_cast(glfwGetWindowUserPointer(w))->on_mouse_wheel(x_offset, y_offset); 72 | }; 73 | 74 | // Set GLFW callbacks 75 | glfwSetWindowSizeCallback(m_window, glfw_on_resize); 76 | glfwSetKeyCallback(m_window, glfw_on_key); 77 | glfwSetMouseButtonCallback(m_window, glfw_on_mouse_button); 78 | glfwSetCursorPosCallback(m_window, glfw_on_mouse_move); 79 | glfwSetScrollCallback(m_window, glfw_on_mouse_wheel); 80 | 81 | // Initialize GLAD 82 | if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) 83 | { 84 | std::cout << "Failed to initialize GLAD" << std::endl; 85 | throw; 86 | } 87 | } 88 | 89 | // Render loop 90 | void Application::draw() 91 | { 92 | do 93 | { 94 | render(glfwGetTime()); 95 | glfwSwapBuffers(m_window); 96 | glfwPollEvents(); 97 | } while (!(glfwWindowShouldClose(m_window))); 98 | } 99 | 100 | // Start the app 101 | void Application::run() 102 | { 103 | set_info(); 104 | init(); 105 | setup(); 106 | draw(); 107 | shutdown(); 108 | glfwDestroyWindow(m_window); 109 | glfwTerminate(); 110 | } 111 | 112 | // Close window when escape is pressed 113 | void Application::on_key(int key, int action) 114 | { 115 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) 116 | { 117 | glfwSetWindowShouldClose(m_window, true); 118 | } 119 | } 120 | 121 | // Function to check OpenGL errors 122 | void Application::_check_gl_error(const char* file, int line) 123 | { 124 | GLenum err; 125 | while((err = glGetError()) != GL_NO_ERROR) { 126 | const char* error; 127 | switch (err) { 128 | case GL_INVALID_OPERATION: 129 | error = "GL_INVALID_OPERATION"; 130 | break; 131 | case GL_INVALID_ENUM: 132 | error = "GL_INVALID_ENUM"; 133 | break; 134 | case GL_INVALID_VALUE: 135 | error = "GL_INVALID_VALUE"; 136 | break; 137 | case GL_OUT_OF_MEMORY: 138 | error = "GL_OUT_OF_MEMORY"; 139 | break; 140 | case GL_INVALID_FRAMEBUFFER_OPERATION: 141 | error = "GL_INVALID_FRAMEBUFFER_OPERATION"; 142 | break; 143 | default: 144 | error = "Unknown OpenGL error"; 145 | } 146 | 147 | std::cerr << "Error: " << err << " " << error << " - " << file << ":" << line << std::endl; 148 | } 149 | } -------------------------------------------------------------------------------- /src/projects/base_app/camera.cpp: -------------------------------------------------------------------------------- 1 | #include "glm/glm/gtc/matrix_transform.hpp" 2 | 3 | #include "camera.h" 4 | 5 | // Camera public 6 | void Camera::set_position(float x, float y, float z) 7 | { 8 | m_position.x = x; 9 | m_position.y = y; 10 | m_position.z = z; 11 | 12 | update_camera_vectors(); 13 | } 14 | 15 | void Camera::set_position(glm::vec3 position) 16 | { 17 | m_position.x = position.x; 18 | m_position.y = position.y; 19 | m_position.z = position.z; 20 | 21 | update_camera_vectors(); 22 | } 23 | 24 | void Camera::process_keyboard(Camera_Movement direction, float delta_time) 25 | { 26 | float velocity = m_movement_speed * delta_time; 27 | 28 | switch (direction) { 29 | case Camera_Movement::FORWARD: 30 | m_position += m_front * velocity; 31 | break; 32 | 33 | case Camera_Movement::BACKWARD: 34 | m_position -= m_front * velocity; 35 | break; 36 | 37 | case Camera_Movement::LEFT: 38 | m_position -= m_right * velocity; 39 | break; 40 | 41 | case Camera_Movement::RIGHT: 42 | m_position += m_right * velocity; 43 | break; 44 | } 45 | } 46 | 47 | void Camera::process_mouse_movement(float x_offset, float y_offset, bool constrain_pitch) 48 | { 49 | m_yaw += x_offset * m_mouse_sensitivity; 50 | m_pitch += y_offset * m_mouse_sensitivity; 51 | 52 | if (constrain_pitch) 53 | { 54 | if (m_pitch > m_maximum_pitch) 55 | { 56 | m_pitch = m_maximum_pitch; 57 | } 58 | 59 | else if (m_pitch < m_minimum_pitch) 60 | { 61 | 62 | m_pitch = m_minimum_pitch; 63 | } 64 | } 65 | 66 | update_camera_vectors(); 67 | } 68 | 69 | void Camera::process_mouse_scroll(float y_offset) 70 | { 71 | if (m_zoom >= m_minimum_zoom && m_zoom <= m_maximum_zoom) 72 | { 73 | m_zoom -= y_offset; 74 | } 75 | 76 | else if (m_zoom <= m_minimum_zoom) 77 | { 78 | m_zoom = m_minimum_zoom; 79 | } 80 | 81 | else if (m_zoom >= m_minimum_zoom) 82 | { 83 | m_zoom = m_minimum_zoom; 84 | } 85 | } 86 | 87 | Camera::Camera(const glm::vec3& position, const glm::vec3& front, const glm::vec3& up, float yaw, float pitch, float speed, float zoom, float sensitivity) : 88 | m_position{ position }, m_front{ front }, m_world_up{ up }, 89 | m_yaw{ yaw }, m_pitch{ pitch }, m_movement_speed{ speed }, 90 | m_zoom{ zoom }, m_mouse_sensitivity{ sensitivity }, 91 | m_far_plane{default_far_plane}, m_near_plane{default_near_plane}, 92 | m_minimum_pitch{default_minimum_pitch}, m_maximum_pitch{default_maximum_pitch} 93 | { 94 | update_camera_vectors(); 95 | } 96 | 97 | Camera::Camera(float pos_x, float pos_y, float pos_z, float up_x, float up_y, float up_z, float yaw, float pitch) : 98 | m_position{ glm::vec3{pos_x, pos_y, pos_z} }, 99 | m_world_up{ glm::vec3{up_x, up_y, up_z} }, 100 | m_yaw{ yaw }, m_pitch{ pitch }, 101 | m_far_plane{default_far_plane}, m_near_plane{default_near_plane}, 102 | m_minimum_pitch{default_minimum_pitch}, m_maximum_pitch{default_maximum_pitch} 103 | { 104 | update_camera_vectors(); 105 | } 106 | 107 | glm::mat4 Camera::get_view_matrix() const 108 | { 109 | return glm::lookAt(m_position, m_position + m_front, m_world_up); 110 | } 111 | 112 | glm::mat4 Camera::get_proj_matrix() const 113 | { 114 | return glm::perspective(glm::radians(m_zoom), 800.0f / 800.0f, m_near_plane, m_far_plane); 115 | } 116 | 117 | glm::vec3 Camera::get_pos() const 118 | { 119 | return glm::vec3(m_position); 120 | } 121 | 122 | glm::vec3 Camera::get_front() const 123 | { 124 | return glm::vec3(m_front); 125 | } 126 | 127 | void Camera::update_camera_vectors() 128 | { 129 | glm::vec3 front_vector{ 1.0f }; 130 | front_vector.x = static_cast(cos(glm::radians(m_yaw)) * cos(glm::radians(m_pitch))); 131 | front_vector.y = static_cast(sin(glm::radians(m_pitch))); 132 | front_vector.z = static_cast(sin(glm::radians(m_yaw)) * cos(glm::radians(m_pitch))); 133 | 134 | // Normalize the vectors because their length gets closer to 0 the more you look up or down which results in slower movement 135 | m_front = glm::normalize(front_vector); 136 | m_right = glm::normalize(glm::cross(m_front, m_world_up)); 137 | m_up = glm::normalize(glm::cross(m_right, m_front)); 138 | } 139 | -------------------------------------------------------------------------------- /src/projects/base_app/glsl_program.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "glm/glm/gtc/type_ptr.hpp" 7 | 8 | #include "glsl_program.h" 9 | 10 | // GlslProgram::Format 11 | GlslProgram::Format& GlslProgram::Format::vertex(const std::string& shader_path) 12 | { 13 | m_vertex_shader = load_shader(shader_path); 14 | return *this; 15 | } 16 | 17 | GlslProgram::Format& GlslProgram::Format::tess_control(const std::string& shader_path) 18 | { 19 | m_tess_control_shader = load_shader(shader_path); 20 | return *this; 21 | } 22 | 23 | GlslProgram::Format& GlslProgram::Format::tess_eval(const std::string& shader_path) 24 | { 25 | m_tess_eval_shader = load_shader(shader_path); 26 | return *this; 27 | } 28 | 29 | GlslProgram::Format& GlslProgram::Format::geometry(const std::string& shader_path) 30 | { 31 | m_geometry_shader = load_shader(shader_path); 32 | return *this; 33 | } 34 | 35 | GlslProgram::Format& GlslProgram::Format::fragment(const std::string& shader_path) 36 | { 37 | m_fragment_shader = load_shader(shader_path); 38 | return *this; 39 | } 40 | 41 | GlslProgram::Format& GlslProgram::Format::compute(const std::string& shader_path) 42 | { 43 | m_compute_shader = load_shader(shader_path); 44 | return *this; 45 | } 46 | 47 | std::string GlslProgram::Format::load_shader(const std::string& shader_path) 48 | { 49 | std::ifstream shader_file; 50 | shader_file.exceptions(std::ifstream::failbit | std::ifstream::badbit); 51 | 52 | try 53 | { 54 | // Convert file contents into a std::string 55 | shader_file.open(shader_path); 56 | std::stringstream shader_string_stream; 57 | shader_string_stream << shader_file.rdbuf(); 58 | shader_file.close(); 59 | return shader_string_stream.str(); 60 | } 61 | catch (std::ifstream::failure e) 62 | { 63 | std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ::PATH: " << shader_path << std::endl; 64 | throw; 65 | } 66 | } 67 | 68 | //GlslProgram 69 | void GlslProgram::use() const 70 | { 71 | glUseProgram(m_handle); 72 | } 73 | 74 | void GlslProgram::introspect() const 75 | { 76 | const int max_name_length{ 64 }; 77 | const int num_parameters{ 2 }; 78 | GLint num_outputs{ 0 }; 79 | glGetProgramInterfaceiv(m_handle, GL_PROGRAM_OUTPUT, GL_ACTIVE_RESOURCES, &num_outputs); 80 | 81 | static const GLenum properties[]{ GL_TYPE, GL_LOCATION }; 82 | GLint params[num_parameters]; 83 | GLchar name[max_name_length]; 84 | for (GLuint index{ 0 }; index != num_outputs; ++index) 85 | { 86 | glGetProgramResourceName(m_handle, GL_PROGRAM_OUTPUT, index, sizeof(name), nullptr, name); 87 | glGetProgramResourceiv(m_handle, GL_PROGRAM_OUTPUT, index, num_parameters, properties, num_parameters, nullptr, params); 88 | std::cout << "Index: " << index << " is type: " << params[0] << " is named: " << name << " is at location: " << params[1] << std::endl; 89 | } 90 | } 91 | 92 | void GlslProgram::uniform(const std::string& name, GLboolean value) const 93 | { 94 | glUniform1i(glGetUniformLocation(m_handle, name.c_str()), value); 95 | } 96 | 97 | void GlslProgram::uniform(const std::string& name, GLuint value) const 98 | { 99 | glUniform1i(glGetUniformLocation(m_handle, name.c_str()), value); 100 | } 101 | 102 | void GlslProgram::uniform(const std::string& name, GLfloat value) const 103 | { 104 | glUniform1f(glGetUniformLocation(m_handle, name.c_str()), value); 105 | } 106 | 107 | void GlslProgram::uniform(const std::string& name, GLdouble value) const 108 | { 109 | glUniform1d(glGetUniformLocation(m_handle, name.c_str()), value); 110 | } 111 | 112 | void GlslProgram::uniform(const std::string& name, const glm::vec2& vec2) const 113 | { 114 | glUniform2f(glGetUniformLocation(m_handle, name.c_str()), vec2.x, vec2.y); 115 | } 116 | 117 | void GlslProgram::uniform(const std::string& name, const glm::vec3& vec3) const 118 | { 119 | glUniform3f(glGetUniformLocation(m_handle, name.c_str()), vec3.x, vec3.y, vec3.z); 120 | } 121 | 122 | void GlslProgram::uniform(const std::string& name, const glm::vec4& vec4) const 123 | { 124 | glUniform4f(glGetUniformLocation(m_handle, name.c_str()), vec4.x, vec4.y, vec4.z, vec4.w); 125 | } 126 | 127 | void GlslProgram::uniform(const std::string& name, const glm::mat3& value) const 128 | { 129 | glUniformMatrix3fv(glGetUniformLocation(m_handle, name.c_str()), 1, GL_FALSE, glm::value_ptr(value)); 130 | } 131 | 132 | void GlslProgram::uniform(const std::string& name, const glm::mat4& value) const 133 | { 134 | glUniformMatrix4fv(glGetUniformLocation(m_handle, name.c_str()), 1, GL_FALSE, glm::value_ptr(value)); 135 | } 136 | 137 | GlslProgram::GlslProgram(const Format& format, bool separable) 138 | { 139 | m_handle = glCreateProgram(); 140 | std::vector shader_handles; 141 | shader_handles.push_back(compile_shader(format.m_vertex_shader, GL_VERTEX_SHADER)); 142 | shader_handles.push_back(compile_shader(format.m_tess_control_shader, GL_TESS_CONTROL_SHADER)); 143 | shader_handles.push_back(compile_shader(format.m_tess_eval_shader, GL_TESS_EVALUATION_SHADER)); 144 | shader_handles.push_back(compile_shader(format.m_geometry_shader, GL_GEOMETRY_SHADER)); 145 | shader_handles.push_back(compile_shader(format.m_fragment_shader, GL_FRAGMENT_SHADER)); 146 | shader_handles.push_back(compile_shader(format.m_compute_shader, GL_COMPUTE_SHADER)); 147 | 148 | // Attach all valid shader handles to program 149 | for (GLuint shader_handle : shader_handles) 150 | { 151 | // shader_handle will be 0 if it is not valid 152 | if (shader_handle) 153 | { 154 | glAttachShader(m_handle, shader_handle); 155 | } 156 | } 157 | 158 | if (separable) 159 | { 160 | glProgramParameteri(m_handle, GL_PROGRAM_SEPARABLE, GL_TRUE); 161 | } 162 | 163 | // Link program and check errors 164 | glLinkProgram(m_handle); 165 | check_compile_errors(m_handle, GL_SHADER); 166 | 167 | // Detach and delete all shader handles 168 | for (GLuint shader_handle : shader_handles) 169 | { 170 | if (shader_handle) 171 | { 172 | glDetachShader(m_handle, shader_handle); 173 | glDeleteShader(shader_handle); 174 | } 175 | } 176 | } 177 | 178 | GLuint GlslProgram::compile_shader(const std::string& shader_string, GLenum shader_type) const 179 | { 180 | GLuint shader_handle{ 0 }; 181 | 182 | if (!shader_string.empty()) 183 | { 184 | const char* shader_c_string { shader_string.c_str() }; 185 | shader_handle = glCreateShader(shader_type); 186 | glShaderSource(shader_handle, 1, &shader_c_string, nullptr); 187 | glCompileShader(shader_handle); 188 | GlslProgram::check_compile_errors(shader_handle, shader_type); 189 | } 190 | 191 | return shader_handle; 192 | } 193 | 194 | void GlslProgram::check_compile_errors(const GLuint program_or_shader, const GLenum program_or_shader_type) const 195 | { 196 | GLint success; 197 | const GLuint log_length{ 1024 }; 198 | GLchar info_log[log_length]; 199 | 200 | if (program_or_shader_type == GL_SHADER) 201 | { 202 | glGetProgramiv(program_or_shader, GL_LINK_STATUS, &success); 203 | if (!success) 204 | { 205 | glGetProgramInfoLog(program_or_shader, log_length, nullptr, info_log); 206 | std::cout << "ERROR::PROGRAM_LINKING_ERROR: " << program_or_shader_type << "\n" << info_log << std::endl; 207 | } 208 | } 209 | 210 | else 211 | { 212 | glGetShaderiv(program_or_shader, GL_COMPILE_STATUS, &success); 213 | if (!success) 214 | { 215 | glGetShaderInfoLog(program_or_shader, log_length, nullptr, info_log); 216 | std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << program_or_shader_type << "\n" << info_log << std::endl; 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/projects/basic_cube/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "basic_cube") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/projects/base_app.h 15 | ${INCLUDE_DIR}/projects/glsl_program.h 16 | ${INCLUDE_DIR}/projects/camera.h 17 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 18 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 20 | ) 21 | 22 | # Add executable to be built 23 | add_executable(${PROJECT_NAME} ${SOURCES}) 24 | 25 | # Specify libraries to be used 26 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 27 | 28 | # Copy resources 29 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 30 | 31 | # Other option 32 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 33 | 34 | # Set correct CWD 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/projects/basic_cube/assets/shaders/cube.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | in vec3 v_position; 4 | out vec4 frag_color; 5 | 6 | void main() 7 | { 8 | frag_color = vec4(v_position, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /src/projects/basic_cube/assets/shaders/cube.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | uniform mat4 u_model_view_matrix; 4 | uniform mat4 u_projection_matrix; 5 | 6 | layout (location = 0) in vec3 a_position; 7 | layout (location = 1) in vec3 a_normal; 8 | 9 | out vec3 v_position; 10 | 11 | void main() 12 | { 13 | v_position = a_position; 14 | gl_Position = u_projection_matrix * u_model_view_matrix * vec4(a_position, 1.0); 15 | } 16 | -------------------------------------------------------------------------------- /src/projects/basic_cube/src/main.cpp: -------------------------------------------------------------------------------- 1 | // A rotating basic cube with model-space positions as colors 2 | 3 | #include 4 | #include 5 | 6 | #include "glm/glm/gtc/matrix_transform.hpp" 7 | 8 | #include "base_app.h" 9 | #include "glsl_program.h" 10 | #include "camera.h" 11 | 12 | static const GLfloat cube_vertices[]{ 13 | // Positions // Normals 14 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 15 | 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 16 | 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 17 | 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 18 | -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 19 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 20 | 21 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 22 | 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 23 | 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 24 | 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 25 | -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 26 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 27 | 28 | -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 29 | -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 30 | -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 31 | -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 32 | -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 33 | -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 34 | 35 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 36 | 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 37 | 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 38 | 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 39 | 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 40 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 41 | 42 | -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 43 | 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 44 | 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 45 | 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 46 | -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 47 | -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 48 | 49 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 50 | 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 51 | 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 52 | 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 53 | -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 54 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f 55 | }; 56 | 57 | class BasicCubeExample : public Application 58 | { 59 | private: 60 | GLuint m_vao{ 0 }; 61 | GLuint m_vbo{ 0 }; 62 | std::unique_ptr m_shader; 63 | Camera m_camera{ glm::vec3{0, 0, 5} }; 64 | const glm::vec3 m_world_up{ glm::vec3{ 0, 1, 0 } }; 65 | const std::vector m_clear_color{ 0.2f, 0.0f, 0.2f, 1.0f }; 66 | const std::string m_cube_mv_matrix_name = "u_model_view_matrix"; 67 | const std::string m_cube_projection_matrix_name = "u_projection_matrix"; 68 | 69 | void set_info() override 70 | { 71 | Application::set_info(); 72 | m_info.title = "Basic cube example"; 73 | } 74 | 75 | void setup() override 76 | { 77 | // Set and use cube shader 78 | m_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/cube.vert").fragment("../assets/shaders/cube.frag")); 79 | m_shader->introspect(); 80 | m_shader->use(); 81 | 82 | // Cube vertex attributes 83 | const GLuint elements_per_face{ 6 }; 84 | 85 | // Position attributes 86 | const GLuint position_index{ 0 }; 87 | const GLuint position_size{ 3 }; 88 | const GLenum position_type{ GL_FLOAT }; 89 | const GLboolean position_normalize{ GL_FALSE }; 90 | const GLuint position_offset_in_buffer{ 0 }; 91 | 92 | // Normal attributes 93 | const GLuint normal_index{ 1 }; 94 | const GLuint normal_size{ 3 }; 95 | const GLenum normal_type{ GL_FLOAT }; 96 | const GLboolean normal_normalize{ GL_FALSE }; 97 | const GLuint normal_offset_in_buffer{ sizeof(GLfloat) * position_size }; 98 | 99 | // Cube VBO attributes 100 | const GLuint binding_index{ 0 }; 101 | const GLuint first_element_offset{ 0 }; 102 | const GLuint element_stride{ sizeof(GLfloat) * elements_per_face }; 103 | 104 | // Set up the cube VBO 105 | const GLuint flags{ 0 }; 106 | glCreateBuffers(1, &m_vbo); 107 | glNamedBufferStorage(m_vbo, sizeof(cube_vertices), cube_vertices, flags); 108 | 109 | // Create and bind cube VAO 110 | glCreateVertexArrays(1, &m_vao); 111 | glBindVertexArray(m_vao); 112 | 113 | // Set up position attribute in VAO 114 | glEnableVertexArrayAttrib(m_vao, position_index); 115 | glVertexArrayAttribFormat(m_vao, position_index, position_size, position_type, position_normalize, position_offset_in_buffer); 116 | glVertexArrayAttribBinding(m_vao, position_index, binding_index); 117 | 118 | // Set up normal attribute in VAO 119 | glEnableVertexArrayAttrib(m_vao, normal_index); 120 | glVertexArrayAttribFormat(m_vao, normal_index, normal_size, normal_type, normal_normalize, normal_offset_in_buffer); 121 | glVertexArrayAttribBinding(m_vao, normal_index, binding_index); 122 | 123 | // Set buffer that backs the VAO 124 | glVertexArrayVertexBuffer(m_vao, binding_index, m_vbo, first_element_offset, element_stride); 125 | 126 | // Set OpenGL State 127 | glEnable(GL_DEPTH_TEST); 128 | glDepthFunc(GL_LEQUAL); 129 | } 130 | 131 | void render(double current_time) override 132 | { 133 | // Set default framebuffer parameters 134 | glViewport(0, 0, m_info.window_width, m_info.window_height); 135 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 136 | glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0, 0); 137 | 138 | // Set uniforms and draw cube 139 | glm::mat4 model_matrix{ glm::mat4{ 1.0 } }; 140 | model_matrix = glm::rotate(model_matrix, static_cast(current_time), m_world_up); 141 | m_shader->uniform(m_cube_mv_matrix_name, m_camera.get_view_matrix() * model_matrix); 142 | m_shader->uniform(m_cube_projection_matrix_name, m_camera.get_proj_matrix()); 143 | glDrawArrays(GL_TRIANGLES, 0, sizeof(cube_vertices) / sizeof(*cube_vertices)); 144 | }; 145 | }; 146 | 147 | int main(int argc, char* argv[]) 148 | { 149 | std::unique_ptr app{ new BasicCubeExample }; 150 | app->run(); 151 | } 152 | -------------------------------------------------------------------------------- /src/projects/compute_shader/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "compute_shader") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/projects/base_app.h 15 | ${INCLUDE_DIR}/projects/glsl_program.h 16 | ${INCLUDE_DIR}/projects/camera.h 17 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 18 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 20 | ) 21 | 22 | # Add executable to be built 23 | add_executable(${PROJECT_NAME} ${SOURCES}) 24 | 25 | # Specify libraries to be used 26 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 27 | 28 | # Copy resources 29 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 30 | 31 | # Other option 32 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 33 | 34 | # Set correct CWD 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/projects/compute_shader/assets/shaders/cube.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0) in vec3 v_position; 4 | 5 | layout (location = 0) out vec4 frag_color; 6 | 7 | void main() 8 | { 9 | frag_color = vec4(v_position, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /src/projects/compute_shader/assets/shaders/cube.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | uniform mat4 u_model_view_matrix; 4 | uniform mat4 u_projection_matrix; 5 | 6 | layout (location = 0) in vec3 a_position; 7 | layout (location = 1) in vec3 a_normal; 8 | 9 | out vec3 v_position; 10 | 11 | void main() 12 | { 13 | v_position = a_position; 14 | gl_Position = u_projection_matrix * u_model_view_matrix * vec4(a_position, 1.0); 15 | } 16 | -------------------------------------------------------------------------------- /src/projects/compute_shader/assets/shaders/full_screen_quad.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (binding = 0) uniform sampler2D input_image; 4 | 5 | layout (location = 0) out vec4 frag_color; 6 | 7 | void main() 8 | { 9 | frag_color = texelFetch(input_image, ivec2(gl_FragCoord.xy), 0).rgba; 10 | } 11 | -------------------------------------------------------------------------------- /src/projects/compute_shader/assets/shaders/full_screen_quad.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | void main() 4 | { 5 | const vec4 vertices[] = { 6 | {-1.0, -1.0, 0.0, 1.0}, 7 | {1.0, -1.0, 0.0, 1.0}, 8 | {-1.0, 1.0, 0.0, 1.0}, 9 | {1.0, 1.0, 0.0, 1.0} 10 | }; 11 | 12 | gl_Position = vertices[gl_VertexID]; 13 | } 14 | -------------------------------------------------------------------------------- /src/projects/compute_shader/assets/shaders/shader.comp: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (local_size_x = 32, local_size_y = 32) in; 4 | 5 | layout (binding = 0, rgba32f) readonly uniform image2D input_image; 6 | layout (binding = 1) writeonly uniform image2D output_image; 7 | 8 | void main() 9 | { 10 | ivec2 pixel = ivec2(gl_GlobalInvocationID.xy); 11 | vec4 texel = imageLoad(input_image, pixel); 12 | 13 | // Invert the pixel color 14 | texel = vec4(1.0) - texel; 15 | imageStore(output_image, pixel, texel); 16 | } 17 | -------------------------------------------------------------------------------- /src/projects/compute_shader/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This renders a cube to an FBO then uses a compute shader to invert that image 2 | // It uses Image load/store to write the image 3 | 4 | #include 5 | #include 6 | 7 | #include "glm/glm/gtc/matrix_transform.hpp" 8 | 9 | #include "base_app.h" 10 | #include "glsl_program.h" 11 | #include "camera.h" 12 | 13 | // Cube vertices 14 | static const GLfloat cube_vertices[]{ 15 | // Positions // Normals 16 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 17 | 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 18 | 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 19 | 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 20 | -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 21 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 22 | 23 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 24 | 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 25 | 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 26 | 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 27 | -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 28 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 29 | 30 | -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 31 | -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 32 | -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 33 | -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 34 | -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 35 | -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 36 | 37 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 38 | 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 39 | 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 40 | 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 41 | 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 42 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 43 | 44 | -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 45 | 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 46 | 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 47 | 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 48 | -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 49 | -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 50 | 51 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 52 | 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 53 | 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 54 | 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 55 | -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 56 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f 57 | }; 58 | 59 | class ComputeShaderExample : public Application 60 | { 61 | private: 62 | GLuint m_cube_vao{ 0 }; 63 | GLuint m_full_screen_quad_vao{ 0 }; 64 | GLuint m_cube_vbo{ 0 }; 65 | GLuint m_src_fbo{ 0 }; 66 | GLuint m_color_texture{ 0 }; 67 | GLuint m_depth_texture{ 0 }; 68 | GLuint m_second_color_texture{ 0 }; 69 | Camera m_camera{ glm::vec3{ 0, 0, 5 } }; 70 | const GLuint m_vertices_per_cube{ sizeof(cube_vertices) / sizeof(*cube_vertices) }; 71 | const int m_number_cubes{ 9 }; 72 | const glm::vec3 m_world_up{ glm::vec3{ 0, 1, 0 } }; 73 | const std::vector m_clear_color{ 0.2f, 0.0f, 0.2f, 1.0f }; 74 | const GLfloat m_depth_reset_val{ 1.0f }; 75 | std::unique_ptr m_cube_shader; 76 | std::unique_ptr m_full_screen_quad_shader; 77 | std::unique_ptr m_compute_shader; 78 | const int m_workgroup_divisor { 32 }; 79 | 80 | void set_info() override 81 | { 82 | Application::set_info(); 83 | m_info.title = "Compute shader example"; 84 | } 85 | 86 | void load_shaders() 87 | { 88 | m_cube_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/cube.vert").fragment("../assets/shaders/cube.frag")); 89 | m_full_screen_quad_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/full_screen_quad.vert").fragment("../assets/shaders/full_screen_quad.frag")); 90 | m_compute_shader = std::make_unique(GlslProgram::Format().compute("../assets/shaders/shader.comp")); 91 | } 92 | 93 | void setup_cube() 94 | { 95 | // Vertex attribute parameters 96 | const GLuint elements_per_face{ 6 }; 97 | 98 | // Positions 99 | const GLuint position_index{ 0 }; 100 | const GLenum position_type{ GL_FLOAT }; 101 | const GLuint position_size{ 3 }; 102 | const GLboolean position_normalize{ GL_FALSE }; 103 | const GLuint position_offset_in_buffer{ 0 }; 104 | 105 | // Normals 106 | const GLuint normal_index{ 1 }; 107 | const GLuint normal_size{ 3 }; 108 | const GLenum normal_type{ GL_FLOAT }; 109 | const GLboolean normal_normalize{ GL_FALSE }; 110 | const GLuint normal_offset_in_buffer{ sizeof(GLfloat) * position_size }; 111 | 112 | // Vertex buffer attributes 113 | const GLuint binding_index{ 0 }; 114 | const GLuint offset{ 0 }; 115 | const GLuint element_stride{ sizeof(GLfloat) * elements_per_face }; 116 | 117 | // Set up VBO and its data store 118 | const GLuint flags{ 0 }; 119 | glCreateBuffers(1, &m_cube_vbo); 120 | glNamedBufferStorage(m_cube_vbo, sizeof(cube_vertices), cube_vertices, flags); 121 | 122 | // Set up cube VAO 123 | glCreateVertexArrays(1, &m_cube_vao); 124 | 125 | glEnableVertexArrayAttrib(m_cube_vao, position_index); 126 | glVertexArrayAttribFormat(m_cube_vao, position_index, position_size, position_type, position_normalize, position_offset_in_buffer); 127 | glVertexArrayAttribBinding(m_cube_vao, position_index, binding_index); 128 | 129 | glEnableVertexArrayAttrib(m_cube_vao, normal_index); 130 | glVertexArrayAttribFormat(m_cube_vao, normal_index, normal_size, normal_type, normal_normalize, normal_offset_in_buffer); 131 | glVertexArrayAttribBinding(m_cube_vao, normal_index, binding_index); 132 | 133 | glVertexArrayVertexBuffer(m_cube_vao, binding_index, m_cube_vbo, offset, element_stride); 134 | } 135 | 136 | void setup_textures_and_buffers() 137 | { 138 | // Create a framebuffer 139 | const std::vector draw_buffers{ GL_COLOR_ATTACHMENT0 }; 140 | glCreateFramebuffers(1, &m_src_fbo); 141 | glNamedFramebufferDrawBuffers(m_src_fbo, 1, draw_buffers.data()); 142 | 143 | // Create depth texture 144 | glCreateTextures(GL_TEXTURE_2D, 1, &m_depth_texture); 145 | glTextureStorage2D(m_depth_texture, 1, GL_DEPTH_COMPONENT32F, m_info.window_width, m_info.window_height); 146 | glTextureParameteri(m_depth_texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 147 | glTextureParameteri(m_depth_texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 148 | 149 | // Create the color texture 150 | glCreateTextures(GL_TEXTURE_2D, 1, &m_color_texture); 151 | glTextureStorage2D(m_color_texture, 1, GL_RGBA32F, m_info.window_width, m_info.window_height); 152 | glTextureParameteri(m_color_texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 153 | glTextureParameteri(m_color_texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 154 | 155 | // Create the second color texture 156 | glCreateTextures(GL_TEXTURE_2D, 1, &m_second_color_texture); 157 | glTextureStorage2D(m_second_color_texture, 1, GL_RGBA32F, m_info.window_width, m_info.window_height); 158 | glTextureParameteri(m_second_color_texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 159 | glTextureParameteri(m_second_color_texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 160 | 161 | // Attach textures 162 | glNamedFramebufferTexture(m_src_fbo, GL_DEPTH_ATTACHMENT, m_depth_texture, 0); 163 | glNamedFramebufferTexture(m_src_fbo, GL_COLOR_ATTACHMENT0, m_color_texture, 0); 164 | 165 | // Set up full screen quad VAO 166 | glCreateVertexArrays(1, &m_full_screen_quad_vao); 167 | } 168 | 169 | void setup() override 170 | { 171 | load_shaders(); 172 | setup_cube(); 173 | setup_textures_and_buffers(); 174 | } 175 | 176 | void render(double current_time) override 177 | { 178 | // Draw scene into the src FBO 179 | m_cube_shader->use(); 180 | glBindVertexArray(m_cube_vao); 181 | glBindFramebuffer(GL_FRAMEBUFFER, m_src_fbo); 182 | glViewport(0, 0, m_info.window_width, m_info.window_height); 183 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 184 | glClearBufferfv(GL_DEPTH, 0, &m_depth_reset_val); 185 | glEnable(GL_DEPTH_TEST); 186 | 187 | for (int index{ 0 }; index != m_number_cubes; ++index) 188 | { 189 | glm::mat4 model_matrix{ glm::mat4{ 1.0 } }; 190 | // Numbers here are just to offset each cube 191 | model_matrix = glm::translate(model_matrix, glm::vec3{ -1.5, 0, 0 }); 192 | model_matrix = glm::translate(model_matrix, glm::vec3{index, static_cast(index) / 5, index * -2 }); 193 | model_matrix = glm::rotate(model_matrix, static_cast(current_time), m_world_up); 194 | m_cube_shader->uniform("u_model_view_matrix", m_camera.get_view_matrix() * model_matrix); 195 | m_cube_shader->uniform("u_projection_matrix", m_camera.get_proj_matrix()); 196 | glDrawArrays(GL_TRIANGLES, 0, m_vertices_per_cube); 197 | } 198 | 199 | // Compute shader 200 | m_compute_shader->use(); 201 | glBindImageTexture(0, m_color_texture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); 202 | glBindImageTexture(1, m_second_color_texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); 203 | glDispatchCompute(static_cast(m_info.window_width / m_workgroup_divisor), static_cast(m_info.window_height / m_workgroup_divisor), 1); 204 | 205 | // Draw full screen quad 206 | m_full_screen_quad_shader->use(); 207 | glBindVertexArray(m_full_screen_quad_vao); 208 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 209 | glViewport(0, 0, m_info.window_width, m_info.window_height); 210 | glBindTextureUnit(0, m_second_color_texture); 211 | glDisable(GL_DEPTH_TEST); 212 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 213 | }; 214 | }; 215 | 216 | int main(int argc, char* argv[]) 217 | { 218 | std::unique_ptr app{ new ComputeShaderExample }; 219 | app->run(); 220 | } 221 | -------------------------------------------------------------------------------- /src/projects/framebuffers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "framebuffers") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/projects/base_app.h 15 | ${INCLUDE_DIR}/projects/glsl_program.h 16 | ${INCLUDE_DIR}/projects/camera.h 17 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 18 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 20 | ) 21 | 22 | # Add executable to be built 23 | add_executable(${PROJECT_NAME} ${SOURCES}) 24 | 25 | # Specify libraries to be used 26 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 27 | 28 | # Copy resources 29 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 30 | 31 | # Other option 32 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 33 | 34 | # Set correct CWD 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/projects/framebuffers/assets/shaders/cube.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0) in vec3 v_position; 4 | 5 | layout (location = 0) out vec4 frag_color; 6 | 7 | void main() 8 | { 9 | frag_color = vec4(v_position, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /src/projects/framebuffers/assets/shaders/cube.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | uniform mat4 u_model_view_matrix; 4 | uniform mat4 u_projection_matrix; 5 | 6 | layout (location = 0) in vec3 a_position; 7 | 8 | layout (location = 0) out vec3 v_position; 9 | 10 | void main() 11 | { 12 | v_position = a_position; 13 | gl_Position = u_projection_matrix * u_model_view_matrix * vec4(a_position, 1.0); 14 | } 15 | -------------------------------------------------------------------------------- /src/projects/framebuffers/assets/shaders/cube2.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (binding = 0) uniform sampler2D fbo_texture; 4 | 5 | layout (location = 0) in vec2 v_uv; 6 | 7 | layout (location = 0) out vec4 frag_color; 8 | 9 | void main() 10 | { 11 | 12 | frag_color = texture(fbo_texture, v_uv) + vec4(0.5); 13 | } 14 | -------------------------------------------------------------------------------- /src/projects/framebuffers/assets/shaders/cube2.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | uniform mat4 u_model_view_matrix; 4 | uniform mat4 u_projection_matrix; 5 | 6 | layout (location = 0) in vec3 a_position; 7 | layout (location = 1) in vec2 a_uv; 8 | 9 | layout (location = 0) out vec2 v_uv; 10 | 11 | void main() 12 | { 13 | v_uv = a_uv; 14 | gl_Position = u_projection_matrix * u_model_view_matrix * vec4(a_position, 1.0); 15 | } 16 | -------------------------------------------------------------------------------- /src/projects/framebuffers/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This renders to a texture using an FBO 2 | 3 | #include 4 | #include 5 | 6 | #include "glm/glm/gtc/matrix_transform.hpp" 7 | 8 | #include "base_app.h" 9 | #include "glsl_program.h" 10 | #include "camera.h" 11 | 12 | // Cube 13 | const GLfloat cube_vertices[]{ 14 | // Positions // UVs 15 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 16 | 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 17 | 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 18 | 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 19 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 20 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 21 | 22 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 23 | 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 24 | 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 25 | 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 26 | -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 27 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 28 | 29 | -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 30 | -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 31 | -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 32 | -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 33 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 34 | -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 35 | 36 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 37 | 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 38 | 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 39 | 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 40 | 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 41 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 42 | 43 | -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 44 | 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 45 | 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 46 | 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 47 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 48 | -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 49 | 50 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 51 | 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 52 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 53 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 54 | -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 55 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f 56 | }; 57 | 58 | class FboExample : public Application 59 | { 60 | private: 61 | GLuint m_vao { 0 }; 62 | GLuint m_vbo{ 0 }; 63 | GLuint m_fbo{ 0 }; 64 | GLuint m_color_buffer_texture{ 0 }; 65 | GLuint m_depth_buffer_texture{ 0 }; 66 | const float m_time_divisor { 2.0 }; 67 | const GLuint m_fbo_width_height{ 800 }; 68 | const GLuint m_num_vertices{ 36 }; 69 | const GLfloat m_depth_reset_val{ 1.0f }; 70 | const glm::vec3 m_world_up{ glm::vec3{ 0, 1, 0 } }; 71 | const std::vector m_clear_color{ 0.2f, 0.0f, 0.2f, 1.0f }; 72 | Camera m_camera{ glm::vec3{0, 0, 5} }; 73 | std::unique_ptr m_shader; 74 | std::unique_ptr m_shader2; 75 | 76 | void setup() override 77 | { 78 | // Create shaders 79 | m_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/cube.vert").fragment("../assets/shaders/cube.frag")); 80 | m_shader2 = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/cube2.vert").fragment("../assets/shaders/cube2.frag")); 81 | 82 | // Cube vertex attribute parameters 83 | const GLuint elements_per_face{ 5 }; 84 | 85 | // Positions 86 | const GLuint position_index{ 0 }; 87 | const GLuint position_size{ 3 }; 88 | const GLenum position_type{ GL_FLOAT }; 89 | const GLboolean position_normalize{ GL_FALSE }; 90 | const GLuint position_offset_in_buffer{ 0 }; 91 | 92 | // UVs 93 | const GLuint uv_index{ 1 }; 94 | const GLuint uv_size{ 2 }; 95 | const GLenum uv_type{ GL_FLOAT }; 96 | const GLboolean uv_normalize{ GL_FALSE }; 97 | const GLuint uv_offset_in_buffer{ sizeof(GLfloat) * position_size }; 98 | 99 | // Cube vertex buffer attributes 100 | const GLuint binding_index{ 0 }; 101 | const GLuint offset{ 0 }; 102 | const GLuint element_stride{ sizeof(GLfloat) * elements_per_face }; 103 | 104 | // Setup the cube VBO and its data store 105 | const GLuint flags{ 0 }; 106 | glCreateBuffers(1, &m_vbo ); 107 | glNamedBufferStorage(m_vbo, sizeof(cube_vertices), cube_vertices, flags); 108 | 109 | // Setup and bind a VAO 110 | glCreateVertexArrays(1, &m_vao); 111 | glBindVertexArray(m_vao); 112 | 113 | // Set attributes in the VAO 114 | glEnableVertexArrayAttrib(m_vao, position_index); 115 | glEnableVertexArrayAttrib(m_vao, uv_index); 116 | 117 | glVertexArrayAttribFormat(m_vao, position_index, position_size, position_type, position_normalize, position_offset_in_buffer); 118 | glVertexArrayAttribFormat(m_vao, uv_index, uv_size, uv_type, uv_normalize, uv_offset_in_buffer); 119 | 120 | glVertexArrayAttribBinding(m_vao, position_index, binding_index); 121 | glVertexArrayAttribBinding(m_vao, uv_index, binding_index); 122 | 123 | glVertexArrayVertexBuffer(m_vao, binding_index, m_vbo, offset, element_stride); 124 | 125 | // Create an FBO 126 | glCreateFramebuffers(1, &m_fbo); 127 | 128 | // Create color buffer texture 129 | glCreateTextures(GL_TEXTURE_2D, 1, &m_color_buffer_texture); 130 | glTextureStorage2D(m_color_buffer_texture, 1, GL_RGB8, m_fbo_width_height, m_fbo_width_height); 131 | glTextureParameteri(m_color_buffer_texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 132 | glTextureParameteri(m_color_buffer_texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 133 | 134 | // Create depth buffer texture 135 | glCreateTextures(GL_TEXTURE_2D, 1, &m_depth_buffer_texture); 136 | glTextureStorage2D(m_depth_buffer_texture, 1, GL_DEPTH_COMPONENT32F, m_fbo_width_height, m_fbo_width_height); 137 | 138 | // Attach buffers 139 | glNamedFramebufferTexture(m_fbo, GL_COLOR_ATTACHMENT0, m_color_buffer_texture, 0); 140 | glNamedFramebufferTexture(m_fbo, GL_DEPTH_ATTACHMENT, m_depth_buffer_texture, 0); 141 | 142 | // This the default (unnecessary here because we only have one output in the FBO frag shader) 143 | static const GLenum draw_buffers[]{ GL_COLOR_ATTACHMENT0 }; 144 | glNamedFramebufferDrawBuffers(m_fbo, 1, draw_buffers); 145 | 146 | glEnable(GL_DEPTH_TEST); 147 | glDepthFunc(GL_LEQUAL); 148 | } 149 | 150 | void render(double current_time) override 151 | { 152 | // Bind the member FBO 153 | glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); 154 | glViewport(0, 0, m_fbo_width_height, m_fbo_width_height); 155 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 156 | glClearBufferfv(GL_DEPTH, 0, &m_depth_reset_val); 157 | 158 | // Calculate and set cube uniforms 159 | glm::mat4 model_matrix{ glm::mat4{ 1.0 } }; 160 | model_matrix = glm::rotate(model_matrix, static_cast(current_time), m_world_up); 161 | m_shader->use(); 162 | m_shader->uniform("u_model_view_matrix", m_camera.get_view_matrix() * model_matrix); 163 | m_shader->uniform("u_projection_matrix", m_camera.get_proj_matrix()); 164 | glDrawArrays(GL_TRIANGLES, 0, m_num_vertices); 165 | 166 | // Return to default framebuffer 167 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 168 | glBindTextureUnit(0, m_color_buffer_texture); 169 | glViewport(0, 0, m_info.window_width, m_info.window_height); 170 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 171 | glClearBufferfv(GL_DEPTH, 0, &m_depth_reset_val); 172 | 173 | // Set uniforms and render textured cube 174 | m_shader2->use(); 175 | model_matrix = glm::mat4{ 1.0f }; 176 | model_matrix = glm::rotate(model_matrix, static_cast(current_time / m_time_divisor), m_world_up); 177 | m_shader2->uniform("u_model_view_matrix", m_camera.get_view_matrix() * model_matrix); 178 | m_shader2->uniform("u_projection_matrix", m_camera.get_proj_matrix()); 179 | glDrawArrays(GL_TRIANGLES, 0, m_num_vertices); 180 | }; 181 | }; 182 | 183 | int main(int argc, char* argv[]) 184 | { 185 | std::unique_ptr app{ new FboExample }; 186 | app->run(); 187 | } -------------------------------------------------------------------------------- /src/projects/fullscreen_quad/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "fullscreen_quad") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/projects/base_app.h 15 | ${INCLUDE_DIR}/projects/glsl_program.h 16 | ${INCLUDE_DIR}/projects/camera.h 17 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 18 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 20 | ) 21 | 22 | # Add executable to be built 23 | add_executable(${PROJECT_NAME} ${SOURCES}) 24 | 25 | # Specify libraries to be used 26 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 27 | 28 | # Copy resources 29 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 30 | 31 | # Other option 32 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 33 | 34 | # Set correct CWD 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/projects/fullscreen_quad/assets/shaders/quad.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0) out vec4 frag_color; 4 | 5 | uniform float time; 6 | 7 | void main() 8 | { 9 | float red_amount = (sin(time * 4.0) + 1.0) / 2.0; 10 | frag_color = vec4(red_amount, 0.0, 0.0, 1.0); 11 | } 12 | -------------------------------------------------------------------------------- /src/projects/fullscreen_quad/assets/shaders/quad.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | const vec2[4] positions = { 4 | {-1.0, -1.0}, 5 | {1.0, -1.0}, 6 | {-1.0, 1.0}, 7 | {1.0, 1.0} 8 | }; 9 | 10 | void main() 11 | { 12 | gl_Position = vec4(positions[gl_VertexID].xy, 0.0, 1.0); 13 | } 14 | -------------------------------------------------------------------------------- /src/projects/fullscreen_quad/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This renders a full screen quad 2 | #include 3 | #include 4 | 5 | #include "base_app.h" 6 | #include "glsl_program.h" 7 | 8 | class FullscreenQuadExample : public Application 9 | { 10 | private: 11 | GLuint m_vao { 0 }; 12 | const GLuint m_num_vertices{ 4 }; 13 | std::unique_ptr m_shader; 14 | 15 | void setup() override 16 | { 17 | // Create shader 18 | m_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/quad.vert").fragment("../assets/shaders/quad.frag")); 19 | m_shader->use(); 20 | m_shader->introspect(); 21 | 22 | // Setup and bind a VAO 23 | glCreateVertexArrays(1, &m_vao); 24 | glBindVertexArray(m_vao); 25 | } 26 | 27 | void render(double current_time) override 28 | { 29 | m_shader->uniform("time", static_cast(current_time)); 30 | glDrawArrays(GL_TRIANGLE_STRIP, 0, m_num_vertices); 31 | }; 32 | }; 33 | 34 | int main(int argc, char* argv[]) 35 | { 36 | std::unique_ptr app{ new FullscreenQuadExample }; 37 | app->run(); 38 | } -------------------------------------------------------------------------------- /src/projects/geometry_shader/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "geometry_shader") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/projects/base_app.h 15 | ${INCLUDE_DIR}/projects/glsl_program.h 16 | ${INCLUDE_DIR}/projects/camera.h 17 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 18 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 20 | ) 21 | 22 | # Add executable to be built 23 | add_executable(${PROJECT_NAME} ${SOURCES}) 24 | 25 | # Specify libraries to be used 26 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 27 | 28 | # Copy resources 29 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 30 | 31 | # Other option 32 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 33 | 34 | # Set correct CWD 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/projects/geometry_shader/assets/shaders/shader.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0)in vec4 g_position; 4 | 5 | layout (location = 0) out vec4 frag_color; 6 | 7 | void main() 8 | { 9 | frag_color = g_position; 10 | } 11 | -------------------------------------------------------------------------------- /src/projects/geometry_shader/assets/shaders/shader.geom: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (triangles) in; 4 | layout (triangle_strip, max_vertices = 3) out; 5 | 6 | layout (location = 0) in vec4 v_position[]; 7 | 8 | layout (location = 0) out vec4 g_position; 9 | 10 | void main() 11 | { 12 | for (int i = 0; i < gl_in.length(); i++) 13 | { 14 | gl_Position = gl_in[i].gl_Position; 15 | g_position = v_position[i]; 16 | EmitVertex(); 17 | } 18 | EndPrimitive(); 19 | } 20 | -------------------------------------------------------------------------------- /src/projects/geometry_shader/assets/shaders/shader.tesc: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (vertices = 16) out; 4 | 5 | const float tess_level = 16.0; 6 | 7 | void main() 8 | { 9 | if (gl_InvocationID == 0) 10 | { 11 | gl_TessLevelInner[0] = tess_level; 12 | gl_TessLevelInner[1] = tess_level; 13 | 14 | gl_TessLevelOuter[0] = tess_level; 15 | gl_TessLevelOuter[1] = tess_level; 16 | gl_TessLevelOuter[2] = tess_level; 17 | gl_TessLevelOuter[3] = tess_level; 18 | } 19 | 20 | gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; 21 | } 22 | -------------------------------------------------------------------------------- /src/projects/geometry_shader/assets/shaders/shader.tese: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | const float epsilon = 0.001; 4 | 5 | uniform mat4 u_model_view_matrix; 6 | uniform mat4 u_projection_matrix; 7 | 8 | layout (quads, equal_spacing, cw) in; 9 | 10 | out TES_OUT 11 | { 12 | vec3 normal; 13 | } tes_out; 14 | 15 | vec4 quadratic_bezier(vec4 A, vec4 B, vec4 C, float t) 16 | { 17 | vec4 D = mix(A, B, t); 18 | vec4 E = mix(B, C, t); 19 | 20 | return mix(D, E, t); 21 | } 22 | 23 | vec4 cubic_bezier(vec4 A, vec4 B, vec4 C, vec4 D, float t) 24 | { 25 | vec4 E = mix(A, B, t); 26 | vec4 F = mix(B, C, t); 27 | vec4 G = mix(C, D, t); 28 | 29 | return quadratic_bezier(E, F, G, t); 30 | } 31 | 32 | vec4 evaluate_patch(vec2 at) 33 | { 34 | vec4 point_coords[4]; 35 | 36 | for (int i = 0; i < point_coords.length; i++) 37 | { 38 | point_coords[i] = cubic_bezier( 39 | gl_in[i + 0].gl_Position, 40 | gl_in[i + 4].gl_Position, 41 | gl_in[i + 8].gl_Position, 42 | gl_in[i + 12].gl_Position, 43 | at.y 44 | ); 45 | } 46 | 47 | return cubic_bezier(point_coords[0], point_coords[1], point_coords[2], point_coords[3], at.x); 48 | } 49 | 50 | void main() 51 | { 52 | vec4 position = evaluate_patch(gl_TessCoord.xy); 53 | vec4 position_y_offset = evaluate_patch(gl_TessCoord.xy + vec2(0.0, epsilon)); 54 | vec4 position_x_offset = evaluate_patch(gl_TessCoord.xy + vec2(epsilon, 0.0)); 55 | 56 | vec3 v1 = normalize(position_y_offset.xyz - position.xyz); 57 | vec3 v2 = normalize(position_x_offset.xyz - position.xyz); 58 | 59 | tes_out.normal = cross(v1, v2); 60 | 61 | gl_Position = position; 62 | } 63 | -------------------------------------------------------------------------------- /src/projects/geometry_shader/assets/shaders/shader.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | uniform mat4 u_model_view_matrix; 4 | 5 | layout (location = 0) in vec4 a_position; 6 | 7 | layout (location = 0) out vec4 v_position; 8 | 9 | void main() 10 | { 11 | gl_Position = u_model_view_matrix * a_position; 12 | v_position = gl_Position; 13 | } 14 | -------------------------------------------------------------------------------- /src/projects/geometry_shader/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This is a pass through geometry shader 2 | 3 | #include 4 | #include 5 | 6 | #include "base_app.h" 7 | #include "glsl_program.h" 8 | #include "camera.h" 9 | 10 | static const GLfloat vertices[]{ 11 | -0.5f, -0.5f, 0.0f, 1.0f, 12 | 0.5f, -0.5f, 0.0f, 1.0f, 13 | -0.5f, 0.5, 0.0f, 1.0f, 14 | 0.5f, 0.5, 0.0f, 1.0f 15 | }; 16 | 17 | class GeometryShaderExample : public Application 18 | { 19 | private: 20 | 21 | bool m_show_wireframe{ false }; 22 | const GLuint m_vertices_per_patch{ 4 }; 23 | const std::vector m_clear_color{ 0.2f, 0.0f, 0.2f, 1.0f }; 24 | glm::mat4 m_model_matrix{ glm::mat4{ 1.0f } }; 25 | glm::mat4 m_view_matrix{ 1.0 }; 26 | glm::mat4 m_projection_matrix{ 1.0 }; 27 | GLuint m_vao{ 0 }; 28 | GLuint m_vbo{ 0 }; 29 | Camera m_camera; 30 | std::unique_ptr m_shader; 31 | 32 | void set_info() override 33 | { 34 | Application::set_info(); 35 | m_info.title = "Geometry shader example"; 36 | } 37 | 38 | void on_key(int key, int action) override 39 | { 40 | Application::on_key(key, action); 41 | if (key == GLFW_KEY_W && action == GLFW_PRESS) 42 | { 43 | m_show_wireframe = !m_show_wireframe; 44 | } 45 | } 46 | 47 | void setup() override 48 | { 49 | // Create and enable shader 50 | m_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/shader.vert").fragment("../assets/shaders/shader.frag").geometry("../assets/shaders/shader.geom")); 51 | m_shader->use(); 52 | 53 | // Vertex attribute parameters 54 | const GLuint position_index{ 0 }; 55 | const GLuint size{ 4 }; 56 | const GLenum type{ GL_FLOAT }; 57 | const GLboolean normalized{ GL_FALSE }; 58 | const GLuint stride{ sizeof(GLfloat) * size }; 59 | 60 | // Set up VBO 61 | const GLuint flags{ 0 }; 62 | glCreateBuffers(1, &m_vbo); 63 | glNamedBufferStorage(m_vbo, sizeof(vertices), vertices, flags); 64 | 65 | // Buffer attributes 66 | const GLuint relative_offset{ 0 }; 67 | const GLuint binding_index{ 0 }; 68 | const GLuint offset{ 0 }; 69 | 70 | // Setup and bind a VAO 71 | glCreateVertexArrays(1, &m_vao); 72 | glBindVertexArray(m_vao); 73 | glEnableVertexArrayAttrib(m_vao, position_index); 74 | glVertexArrayAttribFormat(m_vao, position_index, size, type, normalized, relative_offset); 75 | glVertexArrayVertexBuffer(m_vao, binding_index, m_vbo, offset, stride); 76 | glVertexArrayAttribBinding(m_vao, position_index, binding_index); 77 | } 78 | 79 | void render(double current_time) override 80 | { 81 | // Set OpenGL state 82 | glViewport(0, 0, m_info.window_width, m_info.window_height); 83 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 84 | glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0); 85 | glEnable(GL_DEPTH_TEST); 86 | glDepthFunc(GL_LEQUAL); 87 | glPatchParameteri(GL_PATCH_VERTICES, m_vertices_per_patch); 88 | 89 | // Show or hide wireframe 90 | if (m_show_wireframe) 91 | { 92 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 93 | } 94 | else 95 | { 96 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 97 | } 98 | 99 | // Set uniforms 100 | m_view_matrix = m_camera.get_view_matrix(); 101 | m_projection_matrix = m_camera.get_proj_matrix(); 102 | m_shader->uniform("u_model_view_matrix", m_view_matrix * m_model_matrix); 103 | m_shader->uniform("u_projection_matrix", m_projection_matrix * m_model_matrix); 104 | 105 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 106 | }; 107 | }; 108 | 109 | int main(int argc, char* argv[]) 110 | { 111 | std::unique_ptr app{ new GeometryShaderExample}; 112 | app->run(); 113 | } -------------------------------------------------------------------------------- /src/projects/geometry_shader_normal_viewer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "geometry_shader_normal_viewer") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/projects/base_app.h 15 | ${INCLUDE_DIR}/projects/glsl_program.h 16 | ${INCLUDE_DIR}/projects/camera.h 17 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 18 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 20 | ) 21 | 22 | # Add executable to be built 23 | add_executable(${PROJECT_NAME} ${SOURCES}) 24 | 25 | # Specify libraries to be used 26 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 27 | 28 | # Copy resources 29 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 30 | 31 | # Other option 32 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 33 | 34 | # Set correct CWD 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/projects/geometry_shader_normal_viewer/assets/shaders/cube.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0) in vec3 v_position; 4 | layout (location = 0) out vec4 frag_color; 5 | 6 | void main() 7 | { 8 | frag_color = vec4(v_position, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /src/projects/geometry_shader_normal_viewer/assets/shaders/cube.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | uniform mat4 u_model_view_matrix; 4 | uniform mat4 u_projection_matrix; 5 | 6 | layout (location = 0) in vec3 a_position; 7 | layout (location = 1) in vec3 a_normal; 8 | 9 | layout (location = 0) out vec3 v_position; 10 | 11 | void main() 12 | { 13 | v_position = a_position; 14 | gl_Position = u_projection_matrix * u_model_view_matrix * vec4(a_position, 1.0); 15 | } 16 | -------------------------------------------------------------------------------- /src/projects/geometry_shader_normal_viewer/assets/shaders/normal_viewer.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0) out vec4 frag_color; 4 | 5 | void main() 6 | { 7 | frag_color = vec4(1.0); 8 | } 9 | -------------------------------------------------------------------------------- /src/projects/geometry_shader_normal_viewer/assets/shaders/normal_viewer.geom: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (triangles) in; 4 | layout (line_strip, max_vertices = 8) out; 5 | 6 | uniform mat4 u_model_view_matrix; 7 | uniform mat4 u_projection_matrix; 8 | uniform float u_normal_length; 9 | 10 | in VS_OUT 11 | { 12 | vec3 normal; 13 | } gsIn[]; 14 | 15 | 16 | void main() 17 | { 18 | vec3 ab = gl_in[1].gl_Position.xyz - gl_in[0].gl_Position.xyz; 19 | vec3 ac = gl_in[2].gl_Position.xyz - gl_in[0].gl_Position.xyz; 20 | vec3 face_normal = normalize(cross(ab, ac)); 21 | 22 | vec4 tri_centroid = (gl_in[0].gl_Position + gl_in[1].gl_Position + gl_in[2].gl_Position) / 3.0; 23 | 24 | 25 | gl_Position = u_projection_matrix * u_model_view_matrix * tri_centroid; 26 | EmitVertex(); 27 | 28 | gl_Position = u_projection_matrix * u_model_view_matrix * (tri_centroid + vec4(face_normal * u_normal_length, 0.0)); 29 | EmitVertex(); 30 | EndPrimitive(); 31 | 32 | 33 | // Emit a line strip at each normal 34 | for (int i = 0; i < gl_in.length(); i++) 35 | { 36 | gl_Position = u_projection_matrix * u_model_view_matrix * gl_in[i].gl_Position; 37 | EmitVertex(); 38 | 39 | gl_Position = u_projection_matrix * u_model_view_matrix * (gl_in[i].gl_Position + vec4(gsIn[i].normal * u_normal_length, 0.0)); 40 | EmitVertex(); 41 | EndPrimitive(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/projects/geometry_shader_normal_viewer/assets/shaders/normal_viewer.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0) in vec3 a_position; 4 | layout (location = 1) in vec3 a_normal; 5 | 6 | out VS_OUT 7 | { 8 | vec3 normal; 9 | } vsOut; 10 | 11 | void main() 12 | { 13 | vsOut.normal = a_normal; 14 | gl_Position = vec4(a_position, 1.0); 15 | } 16 | -------------------------------------------------------------------------------- /src/projects/geometry_shader_normal_viewer/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This is a cube geometry shader which converts faces to lines to show normals 2 | 3 | #include 4 | #include 5 | 6 | #include "glm/glm/gtc/matrix_transform.hpp" 7 | 8 | #include "base_app.h" 9 | #include "glsl_program.h" 10 | #include "camera.h" 11 | 12 | // Cube 13 | const GLfloat cube_vertices[] { 14 | // Positions // Normals 15 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 16 | 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 17 | 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 18 | 19 | 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 20 | -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 21 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 22 | 23 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 24 | 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 25 | 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 26 | 27 | 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 28 | -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 29 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 30 | 31 | -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 32 | -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 33 | -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 34 | 35 | -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 36 | -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 37 | -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 38 | 39 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 40 | 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 41 | 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 42 | 43 | 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 44 | 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 45 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 46 | 47 | -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 48 | 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 49 | 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 50 | 51 | 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 52 | -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 53 | -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 54 | 55 | 56 | 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 57 | 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 58 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 59 | 60 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 61 | -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 62 | 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f 63 | }; 64 | 65 | class GeometryShaderExample : public Application 66 | { 67 | private: 68 | GLuint m_vao{ 0 }; 69 | GLuint m_vbo{ 0 }; 70 | glm::mat4 m_model_matrix{ glm::mat4{1.0f } }; 71 | Camera m_camera{ glm::vec3{0, 0, 5} }; 72 | const GLuint m_num_vertices{ 36 }; 73 | const GLfloat m_normal_length{ 0.5f}; 74 | const glm::vec3 m_world_up{ glm::vec3{ 0, 1, 0 } }; 75 | const GLfloat m_rotation_rate{ 0.001f }; 76 | const std::vector m_clear_color{ 0.2f, 0.0f, 0.2f, 1.0f }; 77 | std::unique_ptr m_shader; 78 | std::unique_ptr m_normal_shader; 79 | 80 | void set_info() override 81 | { 82 | Application::set_info(); 83 | m_info.title = "Geometry shader example"; 84 | } 85 | 86 | void setup() override 87 | { 88 | // Create shaders 89 | m_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/cube.vert").fragment("../assets/shaders/cube.frag")); 90 | m_normal_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/normal_viewer.vert").fragment("../assets/shaders/normal_viewer.frag").geometry("../assets/shaders/normal_viewer.geom")); 91 | 92 | // Cube position vertex attribute parameters 93 | const GLuint elements_per_face{ 6 }; 94 | const GLuint position_index{ 0 }; 95 | const GLuint position_size{ 3 }; 96 | const GLenum position_type{ GL_FLOAT }; 97 | const GLboolean position_normalize{ GL_FALSE }; 98 | const GLuint position_offset_in_buffer{ 0 }; 99 | 100 | // Normal position vertex attribute parameters 101 | const GLuint normal_index{ 1 }; 102 | const GLuint normal_size{ 3 }; 103 | const GLenum normal_type{ GL_FLOAT }; 104 | const GLboolean normal_normalize{ GL_FALSE }; 105 | const GLuint normal_offset_in_buffer{ sizeof(GLfloat) * position_size }; 106 | 107 | // Cube vertex buffer attributes 108 | const GLuint binding_index{ 0 }; 109 | const GLuint offset{ 0 }; 110 | const GLuint element_stride{ sizeof(GLfloat) * elements_per_face }; 111 | 112 | // Setup the cube VBO and its data store 113 | const GLuint flags{ 0 }; 114 | glCreateBuffers(1, &m_vbo); 115 | glNamedBufferStorage(m_vbo, sizeof(cube_vertices), cube_vertices, flags); 116 | 117 | // Setup and bind a VAO 118 | glCreateVertexArrays(1, &m_vao); 119 | glBindVertexArray(m_vao); 120 | 121 | // Set attributes in the VAO 122 | glEnableVertexArrayAttrib(m_vao, position_index); 123 | glEnableVertexArrayAttrib(m_vao, normal_index); 124 | 125 | glVertexArrayAttribFormat(m_vao, position_index, position_size, position_type, position_normalize, position_offset_in_buffer); 126 | glVertexArrayAttribFormat(m_vao, normal_index, normal_size, normal_type, normal_normalize, normal_offset_in_buffer); 127 | 128 | glVertexArrayAttribBinding(m_vao, position_index, binding_index); 129 | glVertexArrayAttribBinding(m_vao, normal_index, binding_index); 130 | 131 | glVertexArrayVertexBuffer(m_vao, binding_index, m_vbo, offset, element_stride); 132 | 133 | // Turn on depth test 134 | glEnable(GL_DEPTH_TEST); 135 | glDepthFunc(GL_LEQUAL); 136 | } 137 | 138 | void render(double current_time) override 139 | { 140 | // Set OpenGL state 141 | glViewport(0, 0, m_info.window_width, m_info.window_height); 142 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 143 | glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0); 144 | 145 | // Update uniforms 146 | m_model_matrix = glm::rotate(m_model_matrix, m_rotation_rate, m_world_up); 147 | 148 | // Set uniforms for normals 149 | m_normal_shader->use(); 150 | m_normal_shader->uniform("u_model_view_matrix", m_camera.get_view_matrix() * m_model_matrix); 151 | m_normal_shader->uniform("u_projection_matrix", m_camera.get_proj_matrix()); 152 | m_normal_shader->uniform("u_normal_length", m_normal_length); 153 | glDrawArrays(GL_TRIANGLES, 0, m_num_vertices); 154 | 155 | // Set uniforms for faces 156 | m_shader->use(); 157 | m_shader->uniform("u_model_view_matrix", m_camera.get_view_matrix() * m_model_matrix); 158 | m_shader->uniform("u_projection_matrix", m_camera.get_proj_matrix()); 159 | glDrawArrays(GL_TRIANGLES, 0, m_num_vertices); 160 | }; 161 | }; 162 | 163 | int main(int argc, char* argv[]) 164 | { 165 | std::unique_ptr app{ new GeometryShaderExample }; 166 | app->run(); 167 | } -------------------------------------------------------------------------------- /src/projects/multiple_attributes_and_buffers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "multiple_attributes_and_buffers") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/projects/base_app.h 15 | ${INCLUDE_DIR}/projects/glsl_program.h 16 | ${INCLUDE_DIR}/projects/camera.h 17 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 18 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 20 | ) 21 | 22 | # Add executable to be built 23 | add_executable(${PROJECT_NAME} ${SOURCES}) 24 | 25 | # Specify libraries to be used 26 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 27 | 28 | # Copy resources 29 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 30 | 31 | # Other option 32 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 33 | 34 | # Set correct CWD 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/projects/multiple_attributes_and_buffers/assets/shaders/shader.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | in VS_OUT 4 | { 5 | vec3 color; 6 | } fs_in; 7 | 8 | out vec4 frag_color; 9 | 10 | void main() 11 | { 12 | frag_color = vec4(fs_in.color, 1.0); 13 | } 14 | -------------------------------------------------------------------------------- /src/projects/multiple_attributes_and_buffers/assets/shaders/shader.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0) in vec3 a_position; 4 | layout (location = 1) in vec3 a_color; 5 | 6 | out VS_OUT 7 | { 8 | vec3 color; 9 | } vs_out; 10 | 11 | void main() 12 | { 13 | gl_Position = vec4(a_position, 1.0); 14 | vs_out.color = a_color; 15 | } 16 | -------------------------------------------------------------------------------- /src/projects/multiple_attributes_and_buffers/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This uses mapped buffers to upload data 2 | // The buffer backing the square's VAO is switched to change vertex data quickly 3 | // The attribute binding could also be switched to change the vertex data quickly 4 | // https://stackoverflow.com/a/21652955/3291506 explains buffer binding indices 5 | // Interactivity: Spacebar toggles which buffer back the active VAO 6 | 7 | #include 8 | #include 9 | 10 | #include "base_app.h" 11 | #include "glsl_program.h" 12 | 13 | static const GLfloat vertices[]{ 14 | // Positions // Colors 15 | -0.5f, -0.5f, 0.0f, 1.0, 0.0f, 0.0f, 16 | 0.5f, -0.5f, 0.0f, 0.0f, 1.0, 0.0f, 17 | -0.5f, 0.5, 0.0f, 0.0f, 0.0f, 1.0f, 18 | 19 | -0.5f, 0.5, 0.0f, 0.0f, 0.0f, 1.0f, 20 | 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 21 | 0.5f, 0.5, 0.0f, 1.0f, 0.0f, 1.0f 22 | }; 23 | 24 | static const GLfloat vertices2[]{ 25 | // Positions // Colors 26 | -0.5f, -0.5f, 0.0f, 1.0, 0.0f, 0.0f, 27 | 0.5, -0.5f, 0.0f, 0.0f, 1.0, 0.0f, 28 | -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 29 | 30 | -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 31 | 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 32 | 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f 33 | }; 34 | 35 | class MultipleAttributeExample : public Application 36 | { 37 | private: 38 | GLuint m_vao { 0 }; 39 | const GLuint m_attrib_stride{ sizeof(GLfloat) * 6 }; 40 | const GLuint m_attrib_offset{ 0 }; 41 | const GLuint m_attrib_binding_index{ 0 }; 42 | bool m_buffer_zero{ false }; 43 | const std::vector m_clear_color { 0.2f, 0.0f, 0.2f, 1.0f }; 44 | std::vector m_buffers{ 0, 0 }; 45 | std::unique_ptr m_shader; 46 | 47 | void on_key(int key, int action) override 48 | { 49 | Application::on_key(key, action); 50 | 51 | // Toggle which buffer backs the VAO 52 | if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) 53 | { 54 | if (m_buffer_zero) 55 | { 56 | glVertexArrayVertexBuffer(m_vao, m_attrib_binding_index, m_buffers[0], m_attrib_offset, m_attrib_stride); 57 | } 58 | else 59 | { 60 | glVertexArrayVertexBuffer(m_vao, m_attrib_binding_index, m_buffers[1], m_attrib_offset, m_attrib_stride); 61 | 62 | } 63 | m_buffer_zero = !m_buffer_zero; 64 | } 65 | } 66 | 67 | void setup() override 68 | { 69 | // Create shader 70 | m_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/shader.vert").fragment("../assets/shaders/shader.frag")); 71 | m_shader->use(); 72 | 73 | // Create buffers, map them, upload data, unmap them 74 | const GLuint flags{ 0 }; 75 | const GLuint buffer_offset{ 0 }; 76 | 77 | glCreateBuffers(static_cast(m_buffers.size()), m_buffers.data()); 78 | 79 | // Map buffers and upload data 80 | const GLsizeiptr buffer_size { sizeof(vertices) }; 81 | for (int i { 0 }; i < m_buffers.size(); ++i) 82 | { 83 | glNamedBufferStorage(m_buffers[i], buffer_size, nullptr, GL_MAP_WRITE_BIT); 84 | void* ptr = glMapNamedBufferRange(m_buffers[i], buffer_offset, buffer_size, GL_MAP_WRITE_BIT); 85 | 86 | if (!i) 87 | { 88 | memcpy(ptr, vertices, static_cast(buffer_size)); 89 | } 90 | else 91 | { 92 | memcpy(ptr, vertices2, static_cast(buffer_size)); 93 | } 94 | glUnmapNamedBuffer(m_buffers[i]); 95 | } 96 | 97 | // Vertex attribute parameters 98 | const GLboolean normalize_attrib{ GL_FALSE }; 99 | 100 | // Positions 101 | const GLuint position_index{ 0 }; 102 | const GLuint position_size{ 3 }; 103 | const GLenum position_type{ GL_FLOAT }; 104 | const GLuint position_offset{ 0 }; 105 | 106 | // Colors 107 | const GLuint color_index{ 1 }; 108 | const GLuint color_size{ 3 }; 109 | const GLenum color_type{ GL_FLOAT }; 110 | const GLuint color_offset{ 12 }; 111 | 112 | // Setup the VAO 113 | glCreateVertexArrays(1, &m_vao); 114 | glEnableVertexArrayAttrib(m_vao, position_index); 115 | glEnableVertexArrayAttrib(m_vao, color_index); 116 | glVertexArrayAttribFormat(m_vao, position_index, position_size, position_type, normalize_attrib, position_offset); 117 | glVertexArrayAttribFormat(m_vao, color_index, color_size, color_type, normalize_attrib, color_offset); 118 | 119 | // This binding index could also be switched for quick changing of data 120 | glVertexArrayAttribBinding(m_vao, position_index, m_attrib_binding_index); 121 | glVertexArrayAttribBinding(m_vao, color_index, m_attrib_binding_index); 122 | 123 | // Start with the first VBO backing the VAO. 124 | // This is switched when spacebar is pressed 125 | glVertexArrayVertexBuffer(m_vao, m_attrib_binding_index, m_buffers[0], m_attrib_offset, m_attrib_stride); 126 | } 127 | 128 | void render(double current_time) override 129 | { 130 | // Render code 131 | glViewport(0, 0, m_info.window_width, m_info.window_height); 132 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 133 | glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0); 134 | 135 | glBindVertexArray(m_vao); 136 | glDrawArrays(GL_TRIANGLES, 0, sizeof(vertices) / sizeof(*vertices)); 137 | }; 138 | 139 | 140 | }; 141 | 142 | int main(int argc, char* argv[]) 143 | { 144 | std::unique_ptr app { new MultipleAttributeExample}; 145 | app->run(); 146 | } 147 | -------------------------------------------------------------------------------- /src/projects/phong_lighting/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "phong_lighting") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/glm.hpp 14 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_inverse.hpp 15 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 16 | ${INCLUDE_DIR}/projects/base_app.h 17 | ${INCLUDE_DIR}/projects/glsl_program.h 18 | ${INCLUDE_DIR}/projects/camera.h 19 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 20 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 21 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 22 | ) 23 | 24 | # Add executable to be built 25 | add_executable(${PROJECT_NAME} ${SOURCES}) 26 | 27 | # Specify libraries to be used 28 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 29 | 30 | # Copy resources 31 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 32 | 33 | # Other option 34 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 35 | 36 | # Set correct CWD 37 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 38 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 39 | endif() 40 | -------------------------------------------------------------------------------- /src/projects/phong_lighting/assets/images/container2.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:ca98381f58284329832350eb56799a1332779c39881c2aa4e5d503d05fa201be 3 | size 88065 4 | -------------------------------------------------------------------------------- /src/projects/phong_lighting/assets/images/container2_specular.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9e1bdfdf10a51e6b380d6753ced1edc7a097702fc9b53f16979d520f61884200 3 | size 43262 4 | -------------------------------------------------------------------------------- /src/projects/phong_lighting/assets/shaders/lamp.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0) out vec4 frag_color; 4 | 5 | void main() 6 | { 7 | frag_color = vec4(1.0); 8 | } 9 | -------------------------------------------------------------------------------- /src/projects/phong_lighting/assets/shaders/lamp.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0) in vec3 a_pos; 4 | 5 | uniform mat4 u_model_matrix; 6 | uniform mat4 u_view_matrix; 7 | uniform mat4 u_projection_matrix; 8 | 9 | void main() 10 | { 11 | gl_Position = u_projection_matrix * u_view_matrix * u_model_matrix * vec4(a_pos, 1.0); 12 | } 13 | -------------------------------------------------------------------------------- /src/projects/phong_lighting/assets/shaders/phong.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | // Comments show where you could use the following features: 4 | // shadows, ambient lights, emit and constant colors, light multiplying alpha, fog, "darkness emit" 5 | // and the following maps: 6 | // projection, environment, alpha, color 7 | uniform vec4 u_vertex_color; 8 | uniform vec3 u_camera_position; 9 | 10 | // Light properties 11 | uniform vec3 u_light_color; 12 | uniform vec3 u_light_position; 13 | 14 | // Spotlight properties 15 | uniform vec3 u_light_direction; 16 | uniform float u_light_cutoff; 17 | uniform float u_light_outer_cutoff; 18 | 19 | // Material uniforms 20 | uniform vec4 u_diffuse_color; 21 | uniform vec4 u_ambient_color; 22 | uniform vec3 u_specular_color; 23 | uniform float u_shininess; 24 | uniform float u_shadow_strength; 25 | uniform vec3 u_shadow_color; 26 | uniform sampler2D u_diffuse_texture; 27 | uniform sampler2D u_specular_texture; 28 | 29 | in VS_OUT { 30 | vec3 world_space_position; 31 | vec3 world_space_normal; 32 | vec2 uv; 33 | } v_vert; 34 | 35 | layout(location = 0) out vec4 frag_color; 36 | 37 | const int num_lights = 1; 38 | const float linear_light_attenuation = 0.09; 39 | const float cubic_light_attenuation = 0.032; 40 | 41 | void calculatePhong(inout vec3 diffuse_contrib, inout vec3 specular_contrib, int light_index, vec3 world_space_position, 42 | vec3 normal, float shadow_strength, vec3 shadow_color, vec3 view_vector, float shininess) 43 | { 44 | // Note: Use light_index to index into an array of lights any time u_light_color is used 45 | 46 | // If using a projection map: 47 | // true_light_color = u_light_color * projectionMapColor 48 | // else: 49 | 50 | vec3 true_light_color = u_light_color; 51 | 52 | // If using shadows: 53 | // if (in a shadow) 54 | // light_color_sum = mix(true_light_color, shadow_color, shadow_strength) 55 | // else: 56 | // light_color_sum = true_light_color 57 | // else: 58 | 59 | // Attenuation 60 | vec3 light_color_sum = true_light_color; 61 | float dist = distance(u_light_position, v_vert.world_space_position); 62 | float attenuation_factor = 1.0 / (1.0 + linear_light_attenuation * dist + cubic_light_attenuation * pow(dist, 2.0)); 63 | light_color_sum *= attenuation_factor; 64 | 65 | // Diffuse 66 | vec3 light_dir = normalize(u_light_position - world_space_position); 67 | float diffuse = max(dot(normal, light_dir), 0.0); 68 | diffuse_contrib = diffuse * light_color_sum; 69 | 70 | // Specular 71 | vec3 reflection_direction = reflect(-light_dir, normal); 72 | float specular = pow(max(dot(view_vector, reflection_direction), 0.0), shininess); 73 | specular_contrib = specular * u_specular_color; 74 | 75 | // Note: This treats the light like a spotlight 76 | // There could be a different function for each type of light 77 | vec3 spotlight_dir = normalize(u_light_position - world_space_position); 78 | float theta = dot(spotlight_dir, normalize(-u_light_direction)); 79 | float epsilon = u_light_cutoff - u_light_outer_cutoff; 80 | float intensity = clamp((theta - u_light_outer_cutoff) / epsilon, 0.0, 1.0); 81 | 82 | diffuse_contrib *= intensity; 83 | specular_contrib *= intensity; 84 | } 85 | 86 | vec4 sum_lighting() 87 | { 88 | vec4 out_color = vec4(0.0); 89 | vec3 diffuse_sum = vec3(0.0); 90 | vec3 specular_sum = vec3(0.0); 91 | 92 | vec3 normal = v_vert.world_space_normal; 93 | vec3 view_vector = normalize(u_camera_position - v_vert.world_space_position); 94 | 95 | // Flip the backface normals 96 | if (!gl_FrontFacing) { 97 | normal = -normal; 98 | } 99 | 100 | // Add diffuse and specular contributions for each light 101 | for (int i = 0; i < num_lights; i++) 102 | { 103 | vec3 diffuse_contrib = vec3(0.0); 104 | vec3 specular_contrib = vec3(0.0); 105 | 106 | calculatePhong(diffuse_contrib, 107 | specular_contrib, 108 | i, 109 | v_vert.world_space_position, 110 | normal, 111 | u_shadow_strength, 112 | u_shadow_color, 113 | view_vector, 114 | u_shininess); 115 | 116 | diffuse_sum += diffuse_contrib; 117 | specular_sum += specular_contrib; 118 | } 119 | 120 | // Final diffuse contribution 121 | diffuse_sum *= u_diffuse_color.rgb * u_vertex_color.rgb * vec3(texture(u_diffuse_texture, v_vert.uv)); 122 | out_color.rgb += diffuse_sum; 123 | 124 | // Final specular contribution 125 | specular_sum *= u_specular_color * vec3(texture(u_specular_texture, v_vert.uv)); 126 | out_color.rgb += specular_sum; 127 | 128 | // If using ambient light: 129 | // (for every ambient light) 130 | // out_color += ambientColor * materialAmbientColor * objectColor 131 | 132 | // If using emit color 133 | // out_color += emitColor * emitMap 134 | 135 | // If using constant color 136 | // out_color += constantColor * objectColor 137 | 138 | // If light multiplies alpha: 139 | // calculate lightness (overall value of how much light is affecting the surface) and use in alpha calculation 140 | // vec3 lightness = out_color.r * 0.3 + out_color.g * 0.6 + out_color.b * 0.1; 141 | 142 | // If using color, environment or darkness maps or fog 143 | // out_color *= ColorMap 144 | // out_color += EnvironementMap * EnvironmentMapColor 145 | // out_color = mix(FogColor * FogMapColor, out_color, FogFactor) 146 | // out_color = mix(darknessEmitMapColor * darknessEmitColor, out_color, lightness) 147 | 148 | // Alpha calculation 149 | // If using alpha map or color map with alpha multiply those values here 150 | float alpha = u_vertex_color.a * u_diffuse_color.a; 151 | out_color.rgb *= alpha; 152 | out_color.a = alpha; 153 | 154 | // If using a diffuse map: 155 | // out_color *= diffuseMap 156 | 157 | return out_color; 158 | } 159 | 160 | void main() 161 | { 162 | frag_color = sum_lighting(); 163 | } 164 | -------------------------------------------------------------------------------- /src/projects/phong_lighting/assets/shaders/phong.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | uniform mat4 u_model_matrix; 4 | uniform mat4 u_view_matrix; 5 | uniform mat4 u_projection_matrix; 6 | uniform mat3 u_normal_matrix; 7 | 8 | layout (location = 0) in vec3 a_position; 9 | layout (location = 1) in vec3 a_normal; 10 | layout (location = 2) in vec2 a_uv; 11 | 12 | out VS_OUT { 13 | vec3 world_space_position; 14 | vec3 world_space_normal; 15 | vec2 uv; 16 | } v_vert; 17 | 18 | void main() 19 | { 20 | vec4 world_space_position = u_model_matrix * vec4(a_position, 1.0); 21 | gl_Position = u_projection_matrix * u_view_matrix * world_space_position; 22 | 23 | v_vert.world_space_position = world_space_position.xyz; 24 | v_vert.world_space_normal = normalize(u_normal_matrix * a_normal); 25 | v_vert.uv = a_uv; 26 | } 27 | -------------------------------------------------------------------------------- /src/projects/phong_lighting/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This is a Phong lighting example 2 | // Interactivity: This implements cursor lock to control camera position 3 | // Mouse wheel to move forward / black 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define STB_IMAGE_IMPLEMENTATION 10 | #include "stb/stb_image.h" 11 | 12 | #include "glm/glm/glm.hpp" 13 | #include "glm/glm/gtc/matrix_inverse.hpp" 14 | #include "glm/glm/gtc/matrix_transform.hpp" 15 | 16 | #include "base_app.h" 17 | #include "glsl_program.h" 18 | #include "camera.h" 19 | 20 | static const GLfloat cube_vertices[]{ 21 | // Positions // Normals // UVs 22 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 23 | 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 24 | 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 25 | 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, 26 | -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 27 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 28 | 29 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 30 | 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 31 | 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 32 | 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 33 | -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 34 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 35 | 36 | -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 37 | -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 38 | -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 39 | -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 40 | -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 41 | -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 42 | 43 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 44 | 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 45 | 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 46 | 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 47 | 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 48 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 49 | 50 | -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 51 | 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 52 | 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 53 | 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 54 | -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 55 | -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 56 | 57 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 58 | 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 59 | 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 60 | 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 61 | -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 62 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f 63 | }; 64 | 65 | class LightingExample : public Application 66 | { 67 | private: 68 | std::string m_diffuse_map_path{ "../assets/images/container2.jpg" }; 69 | std::string m_specular_map_path{ "../assets/images/container2_specular.jpg" }; 70 | bool m_first_mouse{ true }; 71 | double m_last_x{ m_info.window_width / 2.0f }; 72 | double m_last_y{ m_info.window_height / 2.0f }; 73 | const float model_scale { 0.2 }; 74 | const int m_num_vertices{ 36 }; 75 | Camera m_camera{ glm::vec3{ 0.0f, 0.0f, 3.0f } }; 76 | const std::vector m_clear_color{ 0.2f, 0.0f, 0.2f, 1.0f }; 77 | const glm::vec4 m_vertex_color{1.0f, 0.5f, 0.31f, 1.0f }; 78 | const glm::vec3 m_light_color{ 0.5f }; 79 | const glm::vec3 m_light_pos{0.0f, 0.25f, 3.0f }; 80 | const GLfloat m_light_cutoff{ glm::cos(glm::radians(12.5f)) }; 81 | const GLfloat m_light_outer_cutoff{ glm::cos(glm::radians(17.5f)) }; 82 | const glm::vec3 m_specular_color{ 1.0f }; 83 | const glm::vec4 m_diffuse_color{ 0.7, 0.7, 0.7, 1.0 }; 84 | const glm::vec4 m_ambient_color{ 0.0, 0.0, 0.0, 1.0 }; 85 | const glm::vec3 m_shadow_color{ 0.0f }; 86 | const GLfloat m_shadow_strength{ 0.0f }; 87 | const GLfloat m_shininess{ 100.0 }; 88 | float m_delta_time { 0.0 }; 89 | glm::mat4 m_model_matrix{ 1.0 }; 90 | glm::mat4 m_view_matrix{ 1.0 }; 91 | glm::mat4 m_projection_matrix{ 1.0 }; 92 | glm::mat3 m_normal_matrix{ 1.0 }; 93 | GLuint m_cube_vao{ 0 }; 94 | GLuint m_lamp_vao{ 0 }; 95 | GLuint m_cube_vbo{ 0 }; 96 | std::unique_ptr m_lamp_shader; 97 | std::unique_ptr m_cube_shader; 98 | 99 | void set_info() override 100 | { 101 | Application::set_info(); 102 | m_info.cursor = GLFW_CURSOR_DISABLED; 103 | m_info.title = "Phong lighting example"; 104 | } 105 | 106 | void on_key(int key, int action) override 107 | { 108 | Application::on_key(key, action); 109 | 110 | if (key == GLFW_KEY_W && action == GLFW_PRESS) 111 | m_camera.process_keyboard(Camera_Movement::FORWARD, m_delta_time); 112 | 113 | else if (key == GLFW_KEY_S && action == GLFW_PRESS) 114 | m_camera.process_keyboard(Camera_Movement::BACKWARD, m_delta_time); 115 | 116 | else if (key == GLFW_KEY_A && action == GLFW_PRESS) 117 | m_camera.process_keyboard(Camera_Movement::LEFT, m_delta_time); 118 | 119 | else if (key == GLFW_KEY_D && action == GLFW_PRESS) 120 | m_camera.process_keyboard(Camera_Movement::RIGHT, m_delta_time); 121 | } 122 | 123 | void on_mouse_move(double x_pos, double y_pos) override 124 | { 125 | // Avoid initial jump movement 126 | if (m_first_mouse) 127 | { 128 | m_last_x = x_pos; 129 | m_last_y = y_pos; 130 | m_first_mouse = false; 131 | } 132 | 133 | float x_delta{ static_cast(x_pos - m_last_x) }; 134 | float y_delta{ static_cast(y_pos - m_last_y) }; 135 | 136 | m_camera.process_mouse_movement(x_delta, y_delta); 137 | m_last_x = x_pos; 138 | m_last_y = y_pos; 139 | } 140 | 141 | void on_mouse_wheel(GLdouble x_offset, GLdouble y_offset) override 142 | { 143 | m_camera.process_mouse_scroll(static_cast(y_offset)); 144 | } 145 | 146 | void setup() override 147 | { 148 | // Create shaders 149 | m_cube_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/phong.vert").fragment("../assets/shaders/phong.frag")); 150 | m_lamp_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/lamp.vert").fragment("../assets/shaders/lamp.frag")); 151 | 152 | // Create the cube shape VBO (this is used for both the cube and lamp) 153 | const GLuint flags{ 0 }; 154 | glCreateBuffers(1, &m_cube_vbo); 155 | glNamedBufferStorage(m_cube_vbo, sizeof(cube_vertices), cube_vertices, flags); 156 | 157 | // Attributes for the cube shape 158 | const GLuint floats_per_face{ 8 }; 159 | const GLuint position_size{ 3 }; 160 | const GLuint normal_size{ 3 }; 161 | const GLuint uv_size{ 2 }; 162 | const GLenum type{ GL_FLOAT }; 163 | const GLboolean normalize{ GL_FALSE }; 164 | const GLuint stride{ sizeof(GLfloat) * floats_per_face }; 165 | const GLuint position_offset{ 0 }; 166 | const GLuint normal_offset{ sizeof(GLfloat) * position_size }; 167 | const GLuint uv_offset{ sizeof(GLfloat) * position_size + sizeof(GLfloat) * normal_size }; 168 | const GLuint binding_index{ 0 }; 169 | const GLuint position_index{ 0 }; 170 | const GLuint normal_index{ 1 }; 171 | const GLuint uv_index{ 2 }; 172 | 173 | // Set attributes in cube VAO 174 | glCreateVertexArrays(1, &m_cube_vao); 175 | 176 | glEnableVertexArrayAttrib(m_cube_vao, position_index); 177 | glVertexArrayAttribFormat(m_cube_vao, position_index, position_size, type, normalize, position_offset); 178 | glVertexArrayAttribBinding(m_cube_vao, position_index, binding_index); 179 | 180 | glEnableVertexArrayAttrib(m_cube_vao, normal_index); 181 | glVertexArrayAttribFormat(m_cube_vao, normal_index, normal_size, type, normalize, normal_offset); 182 | glVertexArrayAttribBinding(m_cube_vao, normal_index, binding_index); 183 | 184 | glEnableVertexArrayAttrib(m_cube_vao, uv_index); 185 | glVertexArrayAttribFormat(m_cube_vao, uv_index, uv_size, type, normalize, uv_offset); 186 | glVertexArrayAttribBinding(m_cube_vao, uv_index, binding_index); 187 | 188 | // Set buffer backing attributes 189 | glVertexArrayVertexBuffer(m_cube_vao, binding_index, m_cube_vbo, position_offset, stride); 190 | 191 | // Set attributes in light VAO (reuse cube positions) 192 | glCreateVertexArrays(1, &m_lamp_vao); 193 | glEnableVertexArrayAttrib(m_lamp_vao, position_index); 194 | glVertexArrayAttribFormat(m_lamp_vao, position_index, position_size, type, normalize, position_offset); 195 | glVertexArrayAttribBinding(m_lamp_vao, position_index, binding_index); 196 | glVertexArrayVertexBuffer(m_lamp_vao, binding_index, m_cube_vbo, position_offset, stride); 197 | 198 | // Create a sampler and bind it to a texture 199 | GLuint sampler_name; 200 | GLuint starting_texture_unit{ 0 }; 201 | glCreateSamplers(1, &sampler_name); 202 | glSamplerParameteri(sampler_name, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 203 | glSamplerParameteri(sampler_name, GL_TEXTURE_WRAP_T, GL_REPEAT); 204 | glSamplerParameteri(sampler_name, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 205 | glSamplerParameteri(sampler_name, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 206 | glBindSampler(starting_texture_unit, sampler_name); 207 | 208 | // Load diffuse and specular maps 209 | GLint diffuse_width; 210 | GLint specular_width; 211 | GLint diffuse_height; 212 | GLint specular_height; 213 | GLint diffuse_nr_channels; 214 | GLint specular_nr_channels; 215 | GLubyte* diffuse_data = stbi_load(m_diffuse_map_path.c_str(), &diffuse_width, &diffuse_height, &diffuse_nr_channels, 0); 216 | if (!diffuse_data) 217 | { 218 | std::cout << "Failed to load texture" << std::endl; 219 | } 220 | GLubyte* specular_data = stbi_load(m_specular_map_path.c_str(), &specular_width, &specular_height, &specular_nr_channels, 0); 221 | if (!specular_data) 222 | { 223 | std::cout << "Failed to load texture" << std::endl; 224 | } 225 | 226 | // Generate OpenGL textures 227 | const int num_box_textures{ 2 }; 228 | GLuint box_textures[num_box_textures]; 229 | const GLuint diffuse_map_index{ 0 }; 230 | const GLuint specular_map_index{ 1 }; 231 | GLint diffuse_map_size; 232 | GLint specular_map_size; 233 | GLsizei levels = 1; 234 | GLint level_to_load = 0; 235 | GLint y_offset = 0; 236 | GLint x_offset = 0; 237 | glCreateTextures(GL_TEXTURE_2D, num_box_textures, box_textures); 238 | glTextureStorage2D(box_textures[diffuse_map_index], levels, GL_RGB8, diffuse_width, diffuse_height); 239 | glTextureStorage2D(box_textures[specular_map_index], levels, GL_RGB8, diffuse_width, diffuse_height); 240 | glTextureSubImage2D(box_textures[diffuse_map_index], level_to_load, x_offset, y_offset, diffuse_width, diffuse_height, GL_RGB, GL_UNSIGNED_BYTE, diffuse_data); 241 | glTextureSubImage2D(box_textures[specular_map_index], level_to_load, x_offset, y_offset, specular_width, specular_height, GL_RGB, GL_UNSIGNED_BYTE, specular_data); 242 | glBindTextureUnit(starting_texture_unit, box_textures[diffuse_map_index]); 243 | glBindTextureUnit(starting_texture_unit + 1, box_textures[specular_map_index]); 244 | stbi_image_free(diffuse_data); 245 | stbi_image_free(specular_data); 246 | 247 | // Global OpenGL state 248 | glEnable(GL_DEPTH_TEST); 249 | } 250 | 251 | void render(double current_time) override 252 | { 253 | glViewport(0, 0, m_info.window_width, m_info.window_height); 254 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 255 | glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0); 256 | 257 | // Draw objects in scene 258 | m_cube_shader->use(); 259 | 260 | // Calculate uniforms 261 | m_model_matrix = glm::mat4{ 1.0f }; 262 | m_view_matrix = m_camera.get_view_matrix(); 263 | m_projection_matrix = m_camera.get_proj_matrix(); 264 | m_normal_matrix = glm::inverseTranspose(glm::mat3(m_model_matrix)); 265 | 266 | // Set lighting uniforms 267 | m_cube_shader->uniform("u_light_color", m_light_color); 268 | m_cube_shader->uniform("u_light_position", m_camera.get_pos()); 269 | m_cube_shader->uniform("u_light_direction", m_camera.get_front()); 270 | m_cube_shader->uniform("u_light_cutoff", m_light_cutoff); 271 | m_cube_shader->uniform("u_light_outer_cutoff", m_light_outer_cutoff); 272 | 273 | // Set material uniforms 274 | m_cube_shader->uniform("u_specular_color", m_specular_color); 275 | m_cube_shader->uniform("u_diffuse_color", m_diffuse_color); 276 | m_cube_shader->uniform("u_ambient_color", m_ambient_color); 277 | m_cube_shader->uniform("u_shadow_color", m_shadow_color); 278 | m_cube_shader->uniform("u_shadow_strength", m_shadow_strength); 279 | m_cube_shader->uniform("u_shininess", m_shininess); 280 | 281 | // Set other uniforms 282 | m_cube_shader->uniform("u_camera_position", m_camera.get_pos()); 283 | m_cube_shader->uniform("u_model_matrix", m_model_matrix); 284 | m_cube_shader->uniform("u_view_matrix", m_view_matrix); 285 | m_cube_shader->uniform("u_projection_matrix", m_projection_matrix); 286 | m_cube_shader->uniform("u_normal_matrix", m_normal_matrix); 287 | m_cube_shader->uniform("u_vertex_color", m_vertex_color); 288 | 289 | glBindVertexArray(m_cube_vao); 290 | glDrawArrays(GL_TRIANGLES, 0, m_num_vertices); 291 | 292 | // Draw lights in scene 293 | m_lamp_shader->use(); 294 | 295 | // Calculate uniforms 296 | m_model_matrix = glm::mat4{ 1.0f }; 297 | m_model_matrix = glm::translate(m_model_matrix, m_light_pos); 298 | m_model_matrix = glm::scale(m_model_matrix, glm::vec3{ model_scale }); 299 | 300 | // Set uniforms 301 | m_lamp_shader->uniform("u_model_matrix", m_model_matrix); 302 | m_lamp_shader->uniform("u_view_matrix", m_view_matrix); 303 | m_lamp_shader->uniform("u_projection_matrix", m_projection_matrix); 304 | 305 | glBindVertexArray(m_lamp_vao); 306 | glDrawArrays(GL_TRIANGLES, 0, m_num_vertices); 307 | }; 308 | }; 309 | 310 | int main(int argc, char* argv[]) 311 | { 312 | std::unique_ptr app{ new LightingExample }; 313 | app->run(); 314 | } 315 | -------------------------------------------------------------------------------- /src/projects/point_sprites/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "point_sprites") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/projects/base_app.h 15 | ${INCLUDE_DIR}/projects/glsl_program.h 16 | ${INCLUDE_DIR}/projects/camera.h 17 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 18 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 20 | ) 21 | 22 | # Add executable to be built 23 | add_executable(${PROJECT_NAME} ${SOURCES}) 24 | 25 | # Specify libraries to be used 26 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 27 | 28 | # Copy resources 29 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 30 | 31 | # Other option 32 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 33 | 34 | # Set correct CWD 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 37 | endif() 38 | 39 | -------------------------------------------------------------------------------- /src/projects/point_sprites/assets/shaders/point_sprite.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0) out vec4 frag_color; 4 | 5 | void main() 6 | { 7 | frag_color = vec4(1.0); 8 | } -------------------------------------------------------------------------------- /src/projects/point_sprites/assets/shaders/point_sprite.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | uniform mat4 u_projection_matrix; 4 | uniform float u_x_offset; 5 | uniform float u_y_offset; 6 | 7 | vec4 starting_position = vec4(0.0, 0.0, -5.0, 1.0); 8 | 9 | void main() 10 | { 11 | starting_position.xy += vec2(u_x_offset, u_y_offset); 12 | gl_Position = u_projection_matrix * starting_position; 13 | gl_PointSize = 15.0; 14 | } 15 | -------------------------------------------------------------------------------- /src/projects/point_sprites/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This is a very simple point sprite example 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "glm/glm/gtc/matrix_transform.hpp" 8 | 9 | #include "base_app.h" 10 | #include "glsl_program.h" 11 | #include "camera.h" 12 | 13 | class PointSpriteExample : public Application 14 | { 15 | private: 16 | GLuint m_vao { 0 }; 17 | const int m_num_points{ 36 }; 18 | const std::vectorm_clear_color{ 0.2f, 0.0f, 0.2f, 1.0f }; 19 | Camera m_camera{ glm::vec3{0, 0, 5} }; 20 | std::unique_ptr m_shader; 21 | 22 | void set_info() override 23 | { 24 | Application::set_info(); 25 | m_info.title = "Point sprite example"; 26 | } 27 | 28 | void setup() override 29 | { 30 | // Create shader 31 | m_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/point_sprite.vert").fragment("../assets/shaders/point_sprite.frag")); 32 | m_shader->use(); 33 | 34 | // Set up and bind VAO 35 | glCreateVertexArrays(1, &m_vao); 36 | glBindVertexArray(m_vao); 37 | 38 | // Set OpenGL State 39 | glEnable(GL_PROGRAM_POINT_SIZE); 40 | glBlendFunc(GL_ONE, GL_ONE); 41 | } 42 | 43 | void render(double current_time) override 44 | { 45 | glViewport(0, 0, m_info.window_width, m_info.window_height); 46 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 47 | glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0); 48 | 49 | // Set uniforms and draw 50 | m_shader->uniform("u_projection_matrix", m_camera.get_proj_matrix()); 51 | for (int index { 0 }; index != m_num_points; index++) 52 | { 53 | // Note: This should use a random engine 54 | auto x_offset = static_cast((static_cast(rand()) / (RAND_MAX) * 2.0) - 1.0); 55 | auto y_offset = static_cast((static_cast(rand()) / (RAND_MAX) * 2.0) - 1.0); 56 | m_shader->uniform("u_x_offset", x_offset); 57 | m_shader->uniform("u_y_offset", y_offset); 58 | glDrawArrays(GL_POINTS, 0, 1); 59 | } 60 | }; 61 | }; 62 | 63 | int main(int argc, char* argv[]) 64 | { 65 | std::unique_ptr app{ new PointSpriteExample}; 66 | app->run(); 67 | } 68 | -------------------------------------------------------------------------------- /src/projects/raytracer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "raytracer") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/projects/base_app.h 15 | ${INCLUDE_DIR}/projects/glsl_program.h 16 | ${INCLUDE_DIR}/projects/camera.h 17 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 18 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 20 | ) 21 | 22 | # Add executable to be built 23 | add_executable(${PROJECT_NAME} ${SOURCES}) 24 | 25 | # Specify libraries to be used 26 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 27 | 28 | # Copy resources 29 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 30 | 31 | # Other option 32 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 33 | 34 | # Set correct CWD 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/projects/raytracer/assets/shaders/blit.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0) out vec4 frag_color; 4 | 5 | layout (binding = 0) uniform sampler2D composite_color_texture; 6 | 7 | void main() 8 | { 9 | frag_color = texelFetch(composite_color_texture, ivec2(gl_FragCoord.xy), 0); 10 | } 11 | -------------------------------------------------------------------------------- /src/projects/raytracer/assets/shaders/blit.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | void main() 4 | { 5 | vec4 vertices[4] = { 6 | {-1.0, -1.0, 1.0, 1.0}, 7 | { 1.0, -1.0, 1.0, 1.0}, 8 | {-1.0, 1.0, 1.0, 1.0}, 9 | { 1.0, 1.0, 1.0, 1.0} 10 | }; 11 | 12 | gl_Position = vertices[gl_VertexID]; 13 | } 14 | -------------------------------------------------------------------------------- /src/projects/raytracer/assets/shaders/raytracer.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | // Outputs 4 | layout (location = 0) out vec3 out_composite_color; 5 | layout (location = 1) out vec3 out_origin; 6 | layout (location = 2) out vec3 out_direction; 7 | layout (location = 3) out vec3 out_reflection_color; 8 | 9 | // Samplers 10 | layout (binding = 0) uniform sampler2D ray_origin_texture; 11 | layout (binding = 1) uniform sampler2D ray_direction_texture; 12 | layout (binding = 2) uniform sampler2D ray_reflection_color_texture; 13 | 14 | // Object intersection type constants 15 | const int object_type_miss = -1; 16 | const int object_type_sphere = 0; 17 | const int object_type_plane = 1; 18 | 19 | // Structs 20 | struct intersection 21 | { 22 | float t; 23 | int object_type; 24 | int object_index; 25 | vec3 incident; 26 | vec3 position; 27 | vec3 normal; 28 | }; 29 | 30 | struct ray 31 | { 32 | vec3 origin; 33 | vec3 direction; 34 | }; 35 | 36 | struct sphere 37 | { 38 | vec4 center; 39 | vec4 color; 40 | float radius; 41 | }; 42 | 43 | struct plane 44 | { 45 | vec4 normal; 46 | vec4 center; 47 | vec4 color; 48 | }; 49 | 50 | struct light 51 | { 52 | vec4 center; 53 | }; 54 | 55 | // UBOs 56 | layout (std140, binding = 0) uniform SPHERES 57 | { 58 | sphere my_spheres[4]; 59 | }; 60 | 61 | layout (std140, binding = 1) uniform PLANES 62 | { 63 | plane my_planes[6]; 64 | }; 65 | 66 | layout (std140, binding = 2) uniform LIGHTS 67 | { 68 | light my_lights[2]; 69 | }; 70 | 71 | // Intersection test constants 72 | const float epsilon = 0.001; 73 | const float max_t = 1000000.0; 74 | 75 | bool intersect_ray_sphere(in ray my_ray, in sphere sph, out vec3 hit_pos, out vec3 normal, out float t) 76 | { 77 | vec3 temp = my_ray.origin - sph.center.xyz; 78 | float b = 2.0 * dot(temp, my_ray.direction); 79 | float c = dot(temp, temp) - sph.radius * sph.radius; 80 | 81 | float discriminant = b * b - 4.0 * c; 82 | 83 | // Avoid taking the square root of a negative number 84 | if (discriminant < 0.0) 85 | { 86 | return false; 87 | } 88 | 89 | discriminant = sqrt(discriminant); 90 | float t0 = -b + discriminant; 91 | float t1 = -b - discriminant; 92 | 93 | t = 0.0; 94 | 95 | // We want to take the smallest positive root 96 | if (t1 > epsilon) 97 | { 98 | t = t1 * 0.5; 99 | } 100 | else if (t0 > epsilon) 101 | { 102 | t = t0 * 0.5; 103 | } 104 | else 105 | { 106 | return false; 107 | } 108 | 109 | hit_pos = my_ray.origin + t * my_ray.direction; 110 | normal = normalize(hit_pos - sph.center.xyz); 111 | 112 | return true; 113 | } 114 | 115 | bool intersect_ray_plane(in ray my_ray, in plane pln, out float t) 116 | { 117 | float denominator = dot(my_ray.direction, pln.normal.xyz); 118 | float numerator = dot(pln.center.xyz - my_ray.origin, pln.normal.xyz); 119 | 120 | // The ray is (basically) parallel to the plane 121 | if (denominator > epsilon) 122 | { 123 | return false; 124 | } 125 | 126 | t = numerator / denominator; 127 | 128 | if (t > 0.0) 129 | { 130 | return true; 131 | } 132 | 133 | // The ray intersected the plane from behind 134 | return false; 135 | } 136 | 137 | bool point_visible_to_light(vec3 point, vec3 L) 138 | { 139 | vec3 shadow_ray_direction = normalize(L - point); 140 | vec3 shadow_ray_origin = point + shadow_ray_direction * 0.001; 141 | ray shadow_ray = ray(shadow_ray_origin, shadow_ray_direction); 142 | 143 | // Temporary intersection test variables 144 | vec3 temp_hit_position; 145 | vec3 temp_hit_normal; 146 | float temp_t; 147 | 148 | // Test if shadow ray hits a sphere 149 | for (int index = 0; index < my_spheres.length(); ++index) 150 | { 151 | if (intersect_ray_sphere(shadow_ray, my_spheres[index], temp_hit_position, temp_hit_normal, temp_t)) 152 | { 153 | return false; 154 | } 155 | } 156 | 157 | return true; 158 | } 159 | 160 | vec3 light_point(vec3 position, vec3 normal, vec3 V, light my_light) 161 | { 162 | // Lighting constants 163 | const vec3 ambient = vec3(0.0); 164 | const vec3 diffuse_color = vec3(0.45); 165 | const vec3 rim_color = vec3(0.01); 166 | const vec3 specular_color = vec3(0.1); 167 | const float specular_term = 260.0; 168 | 169 | if (!point_visible_to_light(position, my_light.center.xyz)) 170 | { 171 | return ambient; 172 | } 173 | 174 | vec3 L = normalize(my_light.center.xyz - position); 175 | vec3 my_ray = reflect(-L, normal); 176 | 177 | // Rim light contribution 178 | float rim_amount = clamp(dot(normal, V), 0.0, 1.0); 179 | rim_amount = smoothstep(0.0, 1.0, 1.0 - rim_amount); 180 | 181 | // Diffuse contribution 182 | float diffuse_amount = clamp(dot(normal, L), 0.0, 1.0); 183 | 184 | // Specular contribution 185 | float specular_amount = pow(clamp(dot(my_ray, normal), 0.0, 1.0), specular_term); 186 | 187 | return ambient + rim_color * rim_amount + diffuse_color * diffuse_amount + specular_color * specular_amount; 188 | } 189 | 190 | void trace_ray(out vec3 out_composite_color, out vec3 out_origin, out vec3 out_direction, out vec3 out_reflection_color) 191 | { 192 | // Construct a ray 193 | vec3 ray_origin = texelFetch(ray_origin_texture, ivec2(gl_FragCoord.xy), 0).xyz; 194 | vec3 ray_direction = normalize(texelFetch(ray_direction_texture, ivec2(gl_FragCoord.xy), 0).xyz); 195 | ray my_ray = ray(ray_origin, ray_direction); 196 | 197 | // Intersection test data 198 | intersection intersect_data; 199 | intersect_data.t = max_t; 200 | 201 | // Temporary intersection test variables 202 | vec3 temp_hit_position; 203 | vec3 temp_hit_normal; 204 | 205 | // Test if ray hits a sphere 206 | for (int index = 0; index < my_spheres.length(); ++index) 207 | { 208 | float temp_t; 209 | if (intersect_ray_sphere(my_ray, my_spheres[index], temp_hit_position, temp_hit_normal, temp_t)) 210 | { 211 | // Was the intersection closer than any previous one? 212 | if (temp_t < intersect_data.t) 213 | { 214 | intersect_data.t = temp_t; 215 | intersect_data.object_type = object_type_sphere; 216 | intersect_data.object_index = index; 217 | intersect_data.position = temp_hit_position; 218 | intersect_data.normal = temp_hit_normal; 219 | } 220 | } 221 | } 222 | 223 | // Test if ray hits a plane 224 | for(int index = 0; index < my_planes.length(); ++index) 225 | { 226 | float temp_t; 227 | if (intersect_ray_plane(my_ray, my_planes[index], temp_t)) 228 | { 229 | if (temp_t < intersect_data.t) 230 | { 231 | intersect_data.t = temp_t; 232 | intersect_data.object_type = object_type_plane; 233 | intersect_data.object_index = index; 234 | intersect_data.position = my_ray.origin + my_ray.direction * temp_t; 235 | intersect_data.normal = normalize(my_planes[index].normal.xyz); 236 | } 237 | } 238 | } 239 | 240 | // Color point if there is a hit inside the bounds and calculate reflection 241 | if (intersect_data.t < max_t) 242 | { 243 | out_origin = intersect_data.position; 244 | out_direction = reflect(my_ray.direction, intersect_data.normal); 245 | 246 | // Calculate lighting at this point 247 | for (int index = 0; index < my_lights.length(); ++index) 248 | { 249 | out_composite_color += light_point(intersect_data.position, intersect_data.normal, -my_ray.direction, my_lights[index]); 250 | } 251 | 252 | // Multiply the composite color by the accumulated relection color (which is initially set to 1) 253 | vec3 accumulated_reflection_color = texelFetch(ray_reflection_color_texture, ivec2(gl_FragCoord.xy), 0).rgb; 254 | out_composite_color *= accumulated_reflection_color; 255 | 256 | if (intersect_data.object_type == object_type_sphere) 257 | { 258 | out_composite_color *= my_spheres[intersect_data.object_index].color.rgb; 259 | out_reflection_color = my_spheres[intersect_data.object_index].color.rgb * 0.5; 260 | } 261 | 262 | else if (intersect_data.object_type == object_type_plane) 263 | { 264 | out_composite_color *= my_planes[intersect_data.object_index].color.rgb; 265 | out_reflection_color = my_planes[intersect_data.object_index].color.rgb * 0.5; 266 | } 267 | } 268 | } 269 | 270 | void main() 271 | { 272 | trace_ray(out_composite_color, out_origin, out_direction, out_reflection_color); 273 | } 274 | -------------------------------------------------------------------------------- /src/projects/raytracer/assets/shaders/raytracer.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | void main() 4 | { 5 | vec4 vertices[4] = { 6 | {-1.0, -1.0, 1.0, 1.0}, 7 | { 1.0, -1.0, 1.0, 1.0}, 8 | {-1.0, 1.0, 1.0, 1.0}, 9 | { 1.0, 1.0, 1.0, 1.0} 10 | }; 11 | 12 | vec4 position = vertices[gl_VertexID]; 13 | 14 | gl_Position = position; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/projects/raytracer/assets/shaders/trace_prepare.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | in VS_OUT 4 | { 5 | vec3 ray_origin; 6 | vec3 ray_direction; 7 | } fs_in; 8 | 9 | layout (location = 0) out vec3 out_composite_color; 10 | layout (location = 1) out vec3 out_origin; 11 | layout (location = 2) out vec3 out_direction; 12 | layout (location = 3) out vec3 out_reflected_color; 13 | 14 | void main() 15 | { 16 | out_composite_color = vec3(0.0); 17 | out_origin = fs_in.ray_origin; 18 | out_direction = fs_in.ray_direction; 19 | out_reflected_color = vec3(1.0); 20 | } 21 | -------------------------------------------------------------------------------- /src/projects/raytracer/assets/shaders/trace_prepare.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | out VS_OUT 4 | { 5 | vec3 ray_origin; 6 | vec3 ray_direction; 7 | } vs_out; 8 | 9 | uniform vec3 ray_origin; 10 | uniform mat4 ray_lookat; 11 | 12 | void main() 13 | { 14 | vec4 vertices[4] = { 15 | {-1.0, -1.0, 1.0, 1.0}, 16 | { 1.0, -1.0, 1.0, 1.0}, 17 | {-1.0, 1.0, 1.0, 1.0}, 18 | { 1.0, 1.0, 1.0, 1.0} 19 | }; 20 | 21 | vec4 position = vertices[gl_VertexID]; 22 | gl_Position = position; 23 | 24 | vec3 ray_direction = position.xyz - ray_origin; 25 | 26 | // The intersection tests expect direction to be normalized 27 | vs_out.ray_direction = normalize((ray_lookat * vec4(ray_direction, 0.0)).xyz); 28 | vs_out.ray_origin = ray_origin; 29 | } 30 | -------------------------------------------------------------------------------- /src/projects/raytracer/src/main.cpp: -------------------------------------------------------------------------------- 1 | // A raytracer 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "glm/glm/gtc/matrix_transform.hpp" 8 | 9 | #include "base_app.h" 10 | #include "glsl_program.h" 11 | #include "camera.h" 12 | 13 | class RayTracer : public Application 14 | { 15 | private: 16 | // UBO structs 17 | // These use `alignas` to avoid needing to pad the structs 18 | struct alignas(16) sphere 19 | { 20 | glm::vec4 center; 21 | glm::vec4 color; 22 | float radius; 23 | }; 24 | 25 | struct alignas(16) plane 26 | { 27 | glm::vec4 normal; 28 | glm::vec4 center; 29 | glm::vec4 color; 30 | }; 31 | 32 | struct light 33 | { 34 | glm::vec4 center; 35 | }; 36 | 37 | // Spheres random (positions, colors, radii) 38 | std::vector m_spheres{ 39 | { 40 | glm::vec4{ 0 }, 41 | glm::vec4{ 0.8f, 0.5f, 0.5f, 0 }, 42 | 0.5f 43 | }, 44 | { 45 | glm::vec4{ 0 }, 46 | glm::vec4{0.8f, 0.8f, 0.8f, 0}, 47 | 0.75f 48 | }, 49 | { 50 | glm::vec4{ 0 }, 51 | glm::vec4{ 0.3f, 0.3f, 0.8f, 0 }, 52 | 0.25f 53 | } 54 | }; 55 | 56 | // Lights (positions, ignoring the fourth component) 57 | std::vector m_lights{ 58 | glm::vec4{ -2, 2, -1, 0 }, 59 | glm::vec4{ 2, -2, 1, 0 } 60 | }; 61 | 62 | // Planes (normals, positions and colors, ignoring the fourth component) 63 | std::vector m_planes{ 64 | // Left plane 65 | { 66 | glm::vec4{ 1, 0, 0, 0 }, 67 | glm::vec4{ -3, 0, 0, 0 }, 68 | glm::vec4{ 1, 0.4f, 0.4f, 0 } 69 | }, 70 | // Right plane 71 | { 72 | glm::vec4{ -1, 0, 0, 0 }, 73 | glm::vec4{ 3, 0, 0, 0 }, 74 | glm::vec4{ 0.3f, 0.8f, 0.8f, 0 } 75 | }, 76 | // Bottom plane 77 | { 78 | glm::vec4{ 0, 1, 0, 0 }, 79 | glm::vec4{ 0, -2, 0, 0 }, 80 | glm::vec4{ 0.9f, 1, 0.9f, 0 } 81 | }, 82 | // Top plane 83 | { 84 | glm::vec4{ 0, -1, 0, 0 }, 85 | glm::vec4{ 0, 2, 0, 0 }, 86 | glm::vec4{ 0.5, 0, 0.5, 0 } 87 | 88 | }, 89 | // Back plane 90 | { 91 | glm::vec4{ 0, 0, 1, 0 }, 92 | glm::vec4{ 0, 0, -10, 0 }, 93 | glm::vec4{ 1, 0.9f, 0.4, 0 } 94 | }, 95 | // Front plane 96 | { 97 | glm::vec4{ 0, 0, -1, 0 }, 98 | glm::vec4{ 0, 0, 10, 0 }, 99 | glm::vec4{ 0.7f, 0, 0, 0 } 100 | } 101 | }; 102 | 103 | // Constants 104 | static const int m_max_recursion_depth{ 5 }; 105 | 106 | // UBO binding indices 107 | const GLuint m_sphere_ubo_binding_index{ 0 }; 108 | const GLuint m_plane_ubo_binding_index{ 1 }; 109 | const GLuint m_light_ubo_binding_index{ 2 }; 110 | 111 | // UBO handles 112 | GLuint m_light_buffer{ 0 }; 113 | GLuint m_sphere_buffer{ 0 }; 114 | GLuint m_plane_buffer{ 0 }; 115 | 116 | // FBO texture handles 117 | GLuint m_composite_texture{ 0 }; 118 | GLuint m_ray_origin_texture[m_max_recursion_depth]; 119 | GLuint m_ray_direction_texture[m_max_recursion_depth]; 120 | GLuint m_ray_reflection_color_texture[m_max_recursion_depth]; 121 | 122 | // GLSL Programs 123 | std::unique_ptr m_prepare_program; 124 | std::unique_ptr m_trace_program; 125 | std::unique_ptr m_blit_program; 126 | 127 | // Camera constants 128 | glm::vec3 m_view_position{ glm::vec3{ 0, 0, 5 } }; 129 | Camera m_camera{ m_view_position }; 130 | 131 | // Other OpenGL handles 132 | GLuint m_vao{ 0 }; 133 | GLuint m_sampler{ 0 }; 134 | GLsync m_sync_object{ nullptr }; 135 | sphere* m_sphere_ptr{ nullptr }; 136 | 137 | // Make sure there is an FBO for every recursion level 138 | std::vector m_ray_fbos { std::vector(m_max_recursion_depth) }; 139 | std::vector m_fbo_draw_buffers{ 140 | GL_COLOR_ATTACHMENT0, 141 | GL_COLOR_ATTACHMENT1, 142 | GL_COLOR_ATTACHMENT2, 143 | GL_COLOR_ATTACHMENT3 144 | }; 145 | 146 | // Other member variables 147 | const float m_time_divisor{ 2.0 }; 148 | 149 | void lock_buffer() 150 | { 151 | if (m_sync_object) 152 | { 153 | glDeleteSync(m_sync_object); 154 | } 155 | 156 | m_sync_object = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); 157 | } 158 | 159 | void set_info() override 160 | { 161 | Application::set_info(); 162 | m_info.title = "Raytracer example"; 163 | } 164 | 165 | void setup() override 166 | { 167 | // Load shaders 168 | m_prepare_program = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/trace_prepare.vert").fragment("../assets/shaders/trace_prepare.frag")); 169 | m_trace_program = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/raytracer.vert").fragment("../assets/shaders/raytracer.frag")); 170 | m_blit_program = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/blit.vert").fragment("../assets/shaders/blit.frag")); 171 | 172 | // Create buffers and bind UBOs 173 | glCreateBuffers(1, &m_sphere_buffer); 174 | glNamedBufferStorage(m_sphere_buffer, m_spheres.size() * sizeof(sphere), nullptr, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); 175 | glBindBufferRange(GL_UNIFORM_BUFFER, m_sphere_ubo_binding_index, m_sphere_buffer, 0, m_spheres.size() * sizeof(sphere)); 176 | 177 | glCreateBuffers(1, &m_light_buffer); 178 | glNamedBufferStorage(m_light_buffer, m_lights.size() * sizeof(light), nullptr, GL_MAP_WRITE_BIT); 179 | glBindBufferRange(GL_UNIFORM_BUFFER, m_light_ubo_binding_index, m_light_buffer, 0, m_lights.size() * sizeof(light)); 180 | 181 | glCreateBuffers(1, &m_plane_buffer); 182 | glNamedBufferStorage(m_plane_buffer, m_planes.size() * sizeof(plane), nullptr, GL_MAP_WRITE_BIT); 183 | glBindBufferRange(GL_UNIFORM_BUFFER, m_plane_ubo_binding_index, m_plane_buffer, 0, m_planes.size() * sizeof(plane)); 184 | 185 | // Upload data that does not change to UBOs 186 | // An alternative to mapping a buffer could be using glNamedBufferSubData 187 | // i.e. glNamedBufferSubData(m_sphere_buffer, 0, sizeof(glm::vec4), &my_value); 188 | auto plane_ptr = static_cast(glMapNamedBufferRange(m_plane_buffer, 0, m_planes.size() * sizeof(plane), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT)); 189 | memcpy(plane_ptr, m_planes.data(), m_planes.size() * sizeof(plane)); 190 | glUnmapNamedBuffer(m_plane_buffer); 191 | 192 | auto light_ptr = static_cast(glMapNamedBufferRange(m_light_buffer, 0, m_lights.size() * sizeof(light), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT)); 193 | memcpy(light_ptr, m_lights.data(), m_lights.size() * sizeof(light)); 194 | glUnmapNamedBuffer(m_light_buffer); 195 | 196 | // The sphere data is updated every frame so its buffer is persistently mapped 197 | m_sphere_ptr = static_cast(glMapNamedBufferRange(m_sphere_buffer, 0, m_spheres.size() * sizeof(sphere), GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_INVALIDATE_RANGE_BIT)); 198 | 199 | // Create and bind a VAO 200 | glCreateVertexArrays(1, &m_vao); 201 | glBindVertexArray(m_vao); 202 | 203 | // Create FBOs 204 | glCreateFramebuffers(static_cast(m_max_recursion_depth), m_ray_fbos.data()); 205 | 206 | // Create a sampler 207 | // This is used by all three of our samplers 208 | glCreateSamplers(1, &m_sampler); 209 | glSamplerParameteri(m_sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 210 | glSamplerParameteri(m_sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 211 | for (GLuint index{ 0 }; index < 3; ++index) 212 | { 213 | glBindSampler(index, m_sampler); 214 | } 215 | 216 | // Create the composite texture 217 | glCreateTextures(GL_TEXTURE_2D, 1, &m_composite_texture); 218 | glTextureStorage2D(m_composite_texture, 1, GL_RGB16F, m_info.window_width, m_info.window_height); 219 | 220 | // Create the other FBO textures 221 | glCreateTextures(GL_TEXTURE_2D, m_max_recursion_depth, m_ray_origin_texture); 222 | glCreateTextures(GL_TEXTURE_2D, m_max_recursion_depth, m_ray_direction_texture); 223 | glCreateTextures(GL_TEXTURE_2D, m_max_recursion_depth, m_ray_reflection_color_texture); 224 | 225 | // Set up the FBOs' textures' stores and the FBO draw buffers 226 | for (int index{ 0 }; index < m_max_recursion_depth; ++index) 227 | { 228 | // Set up FBOs' textures' stores 229 | glTextureStorage2D(m_ray_origin_texture[index], 1, GL_RGB32F, m_info.window_width, m_info.window_height); 230 | glTextureStorage2D(m_ray_direction_texture[index], 1, GL_RGB16F, m_info.window_width, m_info.window_height); 231 | glTextureStorage2D(m_ray_reflection_color_texture[index], 1, GL_RGB16F, m_info.window_width, m_info.window_height); 232 | 233 | // Set FBOs framebuffer textures 234 | glNamedFramebufferTexture(m_ray_fbos[index], GL_COLOR_ATTACHMENT0, m_composite_texture, 0); 235 | glNamedFramebufferTexture(m_ray_fbos[index], GL_COLOR_ATTACHMENT1, m_ray_origin_texture[index], 0); 236 | glNamedFramebufferTexture(m_ray_fbos[index], GL_COLOR_ATTACHMENT2, m_ray_direction_texture[index], 0); 237 | glNamedFramebufferTexture(m_ray_fbos[index], GL_COLOR_ATTACHMENT3, m_ray_reflection_color_texture[index], 0); 238 | 239 | // Set FBOs draw buffers 240 | glNamedFramebufferDrawBuffers(m_ray_fbos[index], static_cast(m_fbo_draw_buffers.size()), m_fbo_draw_buffers.data()); 241 | } 242 | } 243 | 244 | void render(double current_time) override 245 | { 246 | // Wait until GPU is done with buffer 247 | glClientWaitSync(m_sync_object, GL_SYNC_FLUSH_COMMANDS_BIT, 1); 248 | 249 | // Update sphere position data 250 | // Normally this would be could be done with a uniform but it is good practice with mapped buffers 251 | auto sphere_x_offset { static_cast(cos(current_time)) / m_time_divisor }; 252 | for (int index{ 0 }; index < m_spheres.size(); ++index) 253 | { 254 | // The numbers here are offsets for the spheres 255 | float sphere_x { static_cast(index) / m_spheres.size() * 4.5f - 1.25f }; 256 | 257 | m_sphere_ptr[index].center = glm::vec4{ sphere_x + sphere_x_offset, -1, -5, 0 }; 258 | m_sphere_ptr[index].color = m_spheres[index].color; 259 | m_sphere_ptr[index].radius = m_spheres[index].radius; 260 | } 261 | glUnmapNamedBuffer(m_sphere_buffer); 262 | 263 | // Set viewport dimensions 264 | glViewport(0, 0, m_info.window_width, m_info.window_height); 265 | 266 | // Setup draw 267 | m_prepare_program->use(); 268 | m_prepare_program->uniform("ray_origin", m_view_position); 269 | m_prepare_program->uniform("ray_lookat", m_camera.get_view_matrix()); 270 | glBindFramebuffer(GL_FRAMEBUFFER, m_ray_fbos[0]); 271 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 272 | 273 | // Trace draw 274 | m_trace_program->use(); 275 | recurse(0); 276 | 277 | // Blit draw 278 | m_blit_program->use(); 279 | 280 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 281 | glBindTextureUnit(0, m_composite_texture); 282 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 283 | 284 | lock_buffer(); 285 | }; 286 | 287 | void recurse(int depth) { 288 | glBindFramebuffer(GL_FRAMEBUFFER, m_ray_fbos[depth + 1]); 289 | 290 | // Enable additive blending 291 | glEnablei(GL_BLEND, 0); 292 | glBlendFunci(0, GL_ONE, GL_ONE); 293 | 294 | glBindTextureUnit(0, m_ray_origin_texture[depth]); 295 | glBindTextureUnit(1, m_ray_direction_texture[depth]); 296 | glBindTextureUnit(2, m_ray_reflection_color_texture[depth]); 297 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 298 | 299 | if (depth != (m_max_recursion_depth - 1)) 300 | { 301 | recurse(depth + 1); 302 | } 303 | 304 | // Do not blend the blit operation 305 | glDisablei(GL_BLEND, 0); 306 | } 307 | }; 308 | 309 | int main(int argc, char* argv[]) 310 | { 311 | std::unique_ptr app{ new RayTracer }; 312 | app->run(); 313 | } 314 | -------------------------------------------------------------------------------- /src/projects/read_pixels/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "read_pixels") 3 | 4 | # Choose the library for the final build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/projects/base_app.h 15 | ${INCLUDE_DIR}/projects/glsl_program.h 16 | ${INCLUDE_DIR}/projects/camera.h 17 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 18 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 20 | ) 21 | 22 | # Add executable to be built 23 | add_executable(${PROJECT_NAME} ${SOURCES}) 24 | 25 | # Specify libraries to be used 26 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 27 | 28 | # Copy resources 29 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 30 | 31 | # Other option 32 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 33 | 34 | # Set correct CWD 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/projects/read_pixels/assets/shaders/cube.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0) in vec3 v_position; 4 | 5 | layout (location = 0) out vec4 frag_color; 6 | 7 | void main() 8 | { 9 | frag_color = vec4(v_position, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /src/projects/read_pixels/assets/shaders/cube.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | uniform mat4 u_model_view_matrix; 4 | uniform mat4 u_projection_matrix; 5 | 6 | layout (location = 0) in vec3 a_position; 7 | layout (location = 1) in vec3 a_normal; 8 | 9 | layout (location = 0) out vec3 v_position; 10 | 11 | void main() 12 | { 13 | v_position = a_position; 14 | gl_Position = u_projection_matrix * u_model_view_matrix * vec4(a_position, 1.0); 15 | } 16 | -------------------------------------------------------------------------------- /src/projects/read_pixels/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This uses glReadnPixels a safer version of glReadPixels and mapped Pixel Buffer Objects to improve performance 2 | // It saves the rendered image as a Targa (.tga) file 3 | // Interactivity: Press spacebar to take a screenshot 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "glm/glm/gtc/matrix_transform.hpp" 11 | 12 | #include "base_app.h" 13 | #include "glsl_program.h" 14 | #include "camera.h" 15 | 16 | // Cube cube_vertices 17 | const GLfloat cube_vertices[]{ 18 | // Positions // Normals 19 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 20 | 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 21 | 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 22 | 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 23 | -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 24 | -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 25 | 26 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 27 | 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 28 | 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 29 | 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 30 | -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 31 | -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 32 | 33 | -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 34 | -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 35 | -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 36 | -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 37 | -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 38 | -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 39 | 40 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 41 | 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 42 | 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 43 | 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 44 | 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 45 | 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 46 | 47 | -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 48 | 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 49 | 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 50 | 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 51 | -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 52 | -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 53 | 54 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 55 | 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 56 | 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 57 | 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 58 | -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 59 | -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f 60 | }; 61 | 62 | class ReadPixelsExample : public Application 63 | { 64 | private: 65 | GLuint m_vao{ 0 }; 66 | GLuint m_vbo{ 0 }; 67 | int m_data_size{ 0 }; 68 | int m_pbo_index{ 0 }; 69 | Camera m_camera{ glm::vec3{ 0, 0, 5} }; 70 | std::vector(m_pbos) { 0, 0 }; 71 | const GLuint m_num_vertices{ 36 }; 72 | const glm::vec3 m_world_up{ glm::vec3{ 0, 1, 0 } }; 73 | const std::vector m_clear_color{ 0.2f, 0.0f, 0.2f, 1.0f }; 74 | const char* screenshot_filename { "screenshot.tga" }; 75 | const unsigned char m_tga_image_type{ 2 }; 76 | const unsigned char m_tga_bpp{ 24 }; 77 | std::unique_ptr m_shader; 78 | 79 | void set_info() override 80 | { 81 | Application::set_info(); 82 | m_info.title = "Read pixels example"; 83 | } 84 | 85 | void on_key(int key, int action) override 86 | { 87 | Application::on_key(key, action); 88 | 89 | if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) 90 | { 91 | take_screen_shot(); 92 | } 93 | } 94 | 95 | void take_screen_shot() 96 | { // This will take a screenshot of the last frame using two PBOs for improved performance 97 | // Bind PBO to trigger asynchronous reads and then read data 98 | glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbos[m_pbo_index]); 99 | 100 | // Because a non-zero named buffer object is bound to the GL_PIXEL_PACK_BUFFER data is not returned in the pointer passed in at the end 101 | glReadnPixels(0, 0, m_info.window_width, m_info.window_height, GL_BGR, GL_UNSIGNED_BYTE, m_data_size, nullptr); 102 | 103 | // Swap buffers 104 | if (!m_pbo_index) 105 | { 106 | m_pbo_index += 1; 107 | // Need to wait a frame or do other work to make this really improve performance 108 | take_screen_shot(); 109 | } 110 | else 111 | { 112 | // Tightly pack members of this struct 113 | #pragma pack (push, 1) 114 | struct header { 115 | unsigned char ident_size;// Size of following ID field 116 | unsigned char cmap_type; // Color map type 0 = none 117 | unsigned char image_type; // Image type 2 = rgb 118 | short cmap_start; // First entry in palette 119 | short cmap_size; // Number of entries in palette 120 | unsigned char cmap_bpp; // Number of bits per palette 121 | short x_origin; // X origin 122 | short y_origin; // Y origin 123 | short width; // Width in pixels 124 | short height; // Height in pixels 125 | unsigned char bpp; // Bits per pixel 126 | unsigned char descriptor; // Descriptor bits 127 | } tga_header; 128 | #pragma pack (pop) 129 | 130 | // Setup TGA header 131 | memset(&tga_header, 0, sizeof(tga_header)); 132 | tga_header.image_type = m_tga_image_type; 133 | tga_header.width = static_cast(m_info.window_width); 134 | tga_header.height = static_cast(m_info.window_height); 135 | tga_header.bpp = m_tga_bpp; 136 | 137 | std::vector framebuffer_data(static_cast(m_data_size)); 138 | 139 | // Get a pointer to client memory and copy 140 | // Note: This should be done a few frames later to actually be asynchronous 141 | const int buffer_offset{ 0 }; 142 | void *ptr = glMapNamedBufferRange(m_pbos[m_pbo_index - 1], buffer_offset, m_data_size, GL_MAP_READ_BIT); 143 | 144 | if (!ptr) 145 | { 146 | check_gl_error(); 147 | } 148 | else 149 | { 150 | memcpy(framebuffer_data.data(), ptr, static_cast(m_data_size)); 151 | 152 | // Write file 153 | std::ofstream screenshot; 154 | screenshot.open(screenshot_filename, std::ios::out | std::ios::binary); 155 | screenshot.write(reinterpret_cast(&tga_header), sizeof(tga_header)); 156 | screenshot.write(reinterpret_cast(framebuffer_data.data()), m_data_size); 157 | screenshot.close(); 158 | 159 | glUnmapNamedBuffer(m_pbos[m_pbo_index - 1]); 160 | m_pbo_index = 0; 161 | } 162 | } 163 | } 164 | 165 | void setup() override 166 | { 167 | // Set m_data_size which is determined by window height 168 | // Multiply window width by 3 (RGB) round up and make a multiple of 4, then multiply by window height to get byte size 169 | m_data_size = ((m_info.window_width * 3 + 3) & ~3) * m_info.window_height; 170 | 171 | // Create shader 172 | m_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/cube.vert").fragment("../assets/shaders/cube.frag")); 173 | 174 | // Cube vertex attribute parameters 175 | const GLuint elements_per_face{ 6 }; 176 | 177 | const GLuint position_index{ 0 }; 178 | const GLuint position_size{ 3 }; 179 | const GLenum position_type{ GL_FLOAT }; 180 | const GLboolean position_normalize{ GL_FALSE }; 181 | const GLuint position_offset_in_buffer{ 0 }; 182 | 183 | const GLuint normal_index{ 1 }; 184 | const GLuint normal_size{ 3 }; 185 | const GLenum normal_type{ GL_FLOAT }; 186 | const GLboolean normal_normalize{ GL_FALSE }; 187 | const GLuint normal_offset_in_buffer{ sizeof(GLfloat) * position_size }; 188 | 189 | // Cube vertex buffer attributes 190 | const GLuint binding_index{ 0 }; 191 | const GLuint offset{ 0 }; 192 | const GLuint element_stride{ sizeof(GLfloat) * elements_per_face }; 193 | 194 | // Setup the cube VBO and its data store 195 | const GLuint flags{ 0 }; 196 | glCreateBuffers(1, &m_vbo); 197 | glNamedBufferStorage(m_vbo, sizeof(cube_vertices), cube_vertices, flags); 198 | 199 | // Setup the PBOs 200 | glCreateBuffers(static_cast(m_pbos.size()), m_pbos.data()); 201 | for (auto pbo : m_pbos) 202 | { 203 | glNamedBufferStorage(pbo, m_data_size, nullptr, GL_MAP_READ_BIT); 204 | } 205 | 206 | // Setup and bind a VAO 207 | glCreateVertexArrays(1, &m_vao); 208 | glBindVertexArray(m_vao); 209 | 210 | // Set attributes in the VAO 211 | glEnableVertexArrayAttrib(m_vao, position_index); 212 | glVertexArrayAttribFormat(m_vao, position_index, position_size, position_type, position_normalize, position_offset_in_buffer); 213 | glVertexArrayAttribBinding(m_vao, position_index, binding_index); 214 | 215 | glEnableVertexArrayAttrib(m_vao, normal_index); 216 | glVertexArrayAttribFormat(m_vao, normal_index, normal_size, normal_type, normal_normalize, normal_offset_in_buffer); 217 | glVertexArrayAttribBinding(m_vao, normal_index, binding_index); 218 | 219 | glVertexArrayVertexBuffer(m_vao, binding_index, m_vbo, offset, element_stride); 220 | } 221 | 222 | void render(double current_time) override 223 | { 224 | // Set OpenGL state 225 | glViewport(0, 0, m_info.window_width, m_info.window_height); 226 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 227 | glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0); 228 | glEnable(GL_DEPTH_TEST); 229 | glDepthFunc(GL_LEQUAL); 230 | 231 | // Set uniforms and draw first cube 232 | glm::mat4 model_matrix{ glm::mat4{ 1.0 } }; 233 | model_matrix = glm::rotate(model_matrix, static_cast(current_time), m_world_up); 234 | m_shader->use(); 235 | m_shader->uniform("u_model_view_matrix", m_camera.get_view_matrix() * model_matrix); 236 | m_shader->uniform("u_projection_matrix", m_camera.get_proj_matrix()); 237 | glDrawArrays(GL_TRIANGLES, 0, m_num_vertices); 238 | 239 | // Set uniforms and draw second cube 240 | glm::mat4 model_matrix2{ glm::mat4{ 1.0 } }; 241 | // The numbers here just shrink and move the cube by an arbitrary amount 242 | model_matrix2 = glm::translate(model_matrix2, glm::vec3{ 1.25f, 2.0f, 0.0f }); 243 | model_matrix2 = glm::rotate(model_matrix2, static_cast(current_time), m_world_up); 244 | model_matrix2 = glm::scale(model_matrix2, glm::vec3{ 0.5f }); 245 | m_shader->uniform("u_model_view_matrix", m_camera.get_view_matrix() * model_matrix2); 246 | glDrawArrays(GL_TRIANGLES, 0, m_num_vertices); 247 | }; 248 | }; 249 | 250 | int main(int argc, char* argv[]) 251 | { 252 | std::unique_ptr app{ new ReadPixelsExample }; 253 | app->run(); 254 | } 255 | -------------------------------------------------------------------------------- /src/projects/tesselation/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "tesselation") 3 | 4 | # Choose the library for the final build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/projects/base_app.h 15 | ${INCLUDE_DIR}/projects/glsl_program.h 16 | ${INCLUDE_DIR}/projects/camera.h 17 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 18 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 20 | ) 21 | 22 | # Add executable to be built 23 | add_executable(${PROJECT_NAME} ${SOURCES}) 24 | 25 | # Specify libraries to be used 26 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 27 | 28 | # Copy resources 29 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 30 | 31 | # Other option 32 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 33 | 34 | # Set correct CWD 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/projects/tesselation/assets/shaders/shader.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | out vec4 frag_color; 4 | 5 | void main() 6 | { 7 | frag_color = vec4(0.0, 0.8, 1.0, 1.0); 8 | } 9 | -------------------------------------------------------------------------------- /src/projects/tesselation/assets/shaders/shader.tesc: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | uniform float tess_level; 4 | 5 | layout (vertices = 3) out; 6 | 7 | void main() 8 | { 9 | // gl_InvocationID is the index of the TCS invocation within this patch 10 | // The initial vertex positions are not changed 11 | gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; 12 | 13 | // The conditional is here because if gl_TessLevel is not the same across TCS invocations behavior is undefined 14 | if (gl_InvocationID == 0) 15 | { 16 | gl_TessLevelInner[0] = tess_level; 17 | gl_TessLevelOuter[0] = tess_level; 18 | gl_TessLevelOuter[1] = tess_level; 19 | gl_TessLevelOuter[2] = tess_level; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/projects/tesselation/assets/shaders/shader.tese: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (triangles, equal_spacing, cw) in; 4 | 5 | void main() 6 | { 7 | vec4 p0 = gl_TessCoord.x * gl_in[0].gl_Position; 8 | vec4 p1 = gl_TessCoord.y * gl_in[1].gl_Position; 9 | vec4 p2 = gl_TessCoord.z * gl_in[2].gl_Position; 10 | 11 | gl_Position = p0 + p1 + p2; 12 | } 13 | -------------------------------------------------------------------------------- /src/projects/tesselation/assets/shaders/shader.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (location = 0) in vec3 position; 4 | 5 | void main() 6 | { 7 | gl_Position = vec4(position, 1.0); 8 | } 9 | -------------------------------------------------------------------------------- /src/projects/tesselation/assets/shaders/shader2.tese: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (triangles, fractional_even_spacing, cw) in; 4 | 5 | void main() 6 | { 7 | gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) + 8 | (gl_TessCoord.y * gl_in[1].gl_Position) + 9 | (gl_TessCoord.z * gl_in[2].gl_Position); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/projects/tesselation/assets/shaders/shader3.tese: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (triangles, fractional_odd_spacing, cw) in; 4 | 5 | void main() 6 | { 7 | gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) + 8 | (gl_TessCoord.y * gl_in[1].gl_Position) + 9 | (gl_TessCoord.z * gl_in[2].gl_Position); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/projects/tesselation/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This compares the various tesselation spacing options 2 | // Interactivity: The up arrow increases tesselation, down arrow decreases tesselation 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "base_app.h" 9 | #include "glsl_program.h" 10 | 11 | // Positions of three triangles 12 | static const GLfloat triangle_vertices[]{ 13 | -0.75f, -1.0f, 0.0f, 14 | 0.0f, -1.0f, 0.0f, 15 | -0.75f, 0.0f, 0.0f, 16 | 17 | 0.0f, -1.0f, 0.0f, 18 | 0.75f, -1.0f, 0.0f, 19 | 0.0f, 0.0f, 0.0f, 20 | 21 | -0.25f, 0.0f, 0.0f, 22 | 0.5f, 0.0f, 0.0f, 23 | -0.25f, 1.0f, 0.0f 24 | }; 25 | 26 | class TesselationExample : public Application 27 | { 28 | private: 29 | GLuint m_vao{ 0 }; 30 | GLuint m_vbo{ 0 }; 31 | GLuint m_vertices_per_face{ 3 }; 32 | GLuint m_vertices_per_patch{ 3 }; 33 | GLfloat m_tess_increment{ 0.05f }; 34 | const GLfloat m_min_tess_level{ 1.0f }; 35 | GLint m_max_tess_level { 0 }; 36 | GLfloat m_tess_level{ m_min_tess_level }; 37 | const std::vector m_clear_color{ 0.2f, 0.0f, 0.2f, 1.0f }; 38 | std::unique_ptr m_shader_one; 39 | std::unique_ptr m_shader_two; 40 | std::unique_ptr m_shader_three; 41 | 42 | void on_key(int key, int action) override 43 | { 44 | Application::on_key(key, action); 45 | 46 | if (glfwGetKey(m_window, GLFW_KEY_UP) == GLFW_PRESS) 47 | { 48 | m_tess_level < m_max_tess_level ? m_tess_level += m_tess_increment : m_max_tess_level; 49 | } 50 | else if (glfwGetKey(m_window, GLFW_KEY_DOWN) == GLFW_PRESS) 51 | { 52 | m_tess_level > m_min_tess_level ? m_tess_level -= m_tess_increment : m_tess_level; 53 | } 54 | } 55 | 56 | void set_info() override 57 | { 58 | Application::set_info(); 59 | m_info.title = "Tesselation Example"; 60 | } 61 | 62 | void setup() override 63 | { 64 | glGetIntegerv(GL_MAX_TESS_GEN_LEVEL, &m_max_tess_level); 65 | 66 | // Create shaders 67 | m_shader_one = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/shader.vert").fragment("../assets/shaders/shader.frag").tess_control("../assets/shaders/shader.tesc").tess_eval("../assets/shaders/shader.tese")); 68 | m_shader_two = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/shader.vert").fragment("../assets/shaders/shader.frag").tess_control("../assets/shaders/shader.tesc").tess_eval("../assets/shaders/shader2.tese")); 69 | m_shader_three = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/shader.vert").fragment("../assets/shaders/shader.frag").tess_control("../assets/shaders/shader.tesc").tess_eval("../assets/shaders/shader3.tese")); 70 | 71 | // Vertex attribute parameters 72 | const GLuint position_index{ 0 }; 73 | const GLuint position_size{ 3 }; 74 | const GLenum position_type{ GL_FLOAT }; 75 | const GLboolean position_normalize{ GL_FALSE }; 76 | const GLuint position_offset_in_buffer{ 0 }; 77 | 78 | // VBO attributes 79 | const GLuint binding_index{ 0 }; 80 | const GLuint first_element_offset{ 0 }; 81 | const GLuint element_stride{ sizeof(GLfloat) * 3 }; 82 | 83 | // Setup triangle VBO 84 | const GLuint flags{ 0 }; 85 | glCreateBuffers(1, &m_vbo); 86 | glNamedBufferStorage(m_vbo, sizeof(triangle_vertices), triangle_vertices, flags); 87 | 88 | // Set up position attribute in VAO 89 | glCreateVertexArrays(1, &m_vao); 90 | glEnableVertexArrayAttrib(m_vao, position_index); 91 | glVertexArrayAttribFormat(m_vao, position_index, position_size, position_type, position_normalize, position_offset_in_buffer); 92 | glVertexArrayVertexBuffer(m_vao, binding_index, m_vbo, first_element_offset, element_stride); 93 | glVertexArrayAttribBinding(m_vao, position_index, binding_index); 94 | 95 | // Specify vertices per patch 96 | glPatchParameteri(GL_PATCH_VERTICES, m_vertices_per_patch); 97 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 98 | } 99 | 100 | void render(double current_time) override 101 | { 102 | glViewport(0, 0, m_info.window_width, m_info.window_height); 103 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 104 | glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0); 105 | 106 | m_shader_one->use(); 107 | glBindVertexArray(m_vao); 108 | m_shader_one->uniform("tess_level", m_tess_level); 109 | glDrawArrays(GL_PATCHES, 0, m_vertices_per_face); 110 | 111 | m_shader_two->use(); 112 | glBindVertexArray(m_vao); 113 | m_shader_two->uniform("tess_level", m_tess_level); 114 | glDrawArrays(GL_PATCHES, 3, m_vertices_per_face); 115 | 116 | m_shader_three->use(); 117 | glBindVertexArray(m_vao); 118 | m_shader_three->uniform("tess_level", m_tess_level); 119 | glDrawArrays(GL_PATCHES, 6, m_vertices_per_face); 120 | }; 121 | }; 122 | 123 | int main(int argc, char* argv[]) 124 | { 125 | std::unique_ptr app{ new TesselationExample }; 126 | app->run(); 127 | } 128 | -------------------------------------------------------------------------------- /src/projects/tesselation_displacement_map/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "tesselation_displacement_map") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/projects/base_app.h 15 | ${INCLUDE_DIR}/projects/glsl_program.h 16 | ${INCLUDE_DIR}/projects/camera.h 17 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 18 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 20 | ) 21 | 22 | # Add executable to be built 23 | add_executable(${PROJECT_NAME} ${SOURCES}) 24 | 25 | # Specify libraries to be used 26 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 27 | 28 | # Copy resources 29 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 30 | 31 | # Other option 32 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 33 | 34 | # Set correct CWD 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/projects/tesselation_displacement_map/assets/images/noise.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a3570aa8ef26b6f6ee2e15b90095a66a4797a7b8fb42016cae11fc50b48dcc6e 3 | size 4941 4 | -------------------------------------------------------------------------------- /src/projects/tesselation_displacement_map/assets/images/noise_color.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:397438f1ddf83d17d873203ea097303fe099ae4a7429d45f868ccbf04b8bae02 3 | size 34167 4 | -------------------------------------------------------------------------------- /src/projects/tesselation_displacement_map/assets/shaders/terrain_disp.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | out vec4 fragColor; 4 | 5 | layout (binding = 1) uniform sampler2D uTexColor; 6 | 7 | in TES_OUT 8 | { 9 | vec2 uv; 10 | } fsIn; 11 | 12 | void main() 13 | { 14 | fragColor = texture(uTexColor, fsIn.uv); 15 | } 16 | -------------------------------------------------------------------------------- /src/projects/tesselation_displacement_map/assets/shaders/terrain_disp.tesc: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (vertices = 4) out; 4 | 5 | in VS_OUT 6 | { 7 | vec2 uv; 8 | } tcs_in[]; 9 | 10 | out TCS_OUT 11 | { 12 | vec2 uv; 13 | } tcs_out[]; 14 | 15 | uniform mat4 u_model_view_projection_matrix; 16 | 17 | const float scale = 16.0; 18 | const float bias = 1.0; 19 | 20 | void main() 21 | { 22 | if (gl_InvocationID == 0) 23 | { 24 | // Get point position in clip space 25 | vec4 p0 = u_model_view_projection_matrix * gl_in[0].gl_Position; 26 | vec4 p1 = u_model_view_projection_matrix * gl_in[1].gl_Position; 27 | vec4 p2 = u_model_view_projection_matrix * gl_in[2].gl_Position; 28 | vec4 p3 = u_model_view_projection_matrix * gl_in[3].gl_Position; 29 | 30 | // Get point position in NDC 31 | p0 /= p0.w; 32 | p1 /= p1.w; 33 | p2 /= p2.w; 34 | p3 /= p3.w; 35 | 36 | // Do not tesselate patches behind the viewer 37 | if (p0.z <= 0.0 || 38 | p1.z <= 0.0 || 39 | p2.z <= 0.0 || 40 | p3.z <= 0.0) 41 | { 42 | gl_TessLevelOuter[0] = 0.0; 43 | gl_TessLevelOuter[1] = 0.0; 44 | gl_TessLevelOuter[2] = 0.0; 45 | gl_TessLevelOuter[3] = 0.0; 46 | } 47 | else 48 | { 49 | // Set outer tesselation to length of quad in NDC * scale + bias 50 | float length0 = length(p2.xy - p0.xy) * scale + bias; 51 | float length1 = length(p3.xy - p2.xy) * scale + bias; 52 | float length2 = length(p3.xy - p1.xy) * scale + bias; 53 | float length3 = length(p1.xy - p0.xy) * scale + bias; 54 | gl_TessLevelOuter[0] = length0; 55 | gl_TessLevelOuter[1] = length1; 56 | gl_TessLevelOuter[2] = length2; 57 | gl_TessLevelOuter[3] = length3; 58 | 59 | // Set inner tesselation to the minimum outer level 60 | gl_TessLevelInner[0] = min(length1, length3); 61 | gl_TessLevelInner[1] = min(length0, length2); 62 | } 63 | } 64 | 65 | // Set output variables 66 | gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; 67 | tcs_out[gl_InvocationID].uv = tcs_in[gl_InvocationID].uv; 68 | } 69 | -------------------------------------------------------------------------------- /src/projects/tesselation_displacement_map/assets/shaders/terrain_disp.tese: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | layout (quads, fractional_odd_spacing) in; 4 | 5 | in TCS_OUT 6 | { 7 | vec2 uv; 8 | } tes_in[]; 9 | 10 | out TES_OUT 11 | { 12 | vec2 uv; 13 | } tes_out; 14 | 15 | uniform sampler2D u_tex_displacement; 16 | uniform mat4 u_model_view_projection_matrix; 17 | uniform float u_displacement_weight; 18 | 19 | void main() 20 | { 21 | // Calculate UV coordinate by interpolating the passed UV in X and then mix betweeen bottom and top 22 | vec2 uv_bottom = mix(tes_in[0].uv, tes_in[1].uv, gl_TessCoord.x); 23 | vec2 uv_top = mix(tes_in[2].uv, tes_in[3].uv, gl_TessCoord.x); 24 | vec2 uv = mix(uv_top, uv_bottom, gl_TessCoord.y); 25 | 26 | // Interpolate the position in the same way 27 | vec4 p_bottom = mix(gl_in[0].gl_Position, 28 | gl_in[1].gl_Position, 29 | gl_TessCoord.x); 30 | 31 | vec4 p_top = mix(gl_in[2].gl_Position, 32 | gl_in[3].gl_Position, 33 | gl_TessCoord.x); 34 | 35 | vec4 p = mix(p_top, p_bottom, gl_TessCoord.y); 36 | 37 | // Displacement map 38 | // Offset the point's Y position based on texture value at its UV coordinate 39 | p.y += texture(u_tex_displacement, uv).r * u_displacement_weight; 40 | 41 | gl_Position = u_model_view_projection_matrix * p; 42 | tes_out.uv = uv; 43 | } 44 | -------------------------------------------------------------------------------- /src/projects/tesselation_displacement_map/assets/shaders/terrain_disp.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | out VS_OUT 4 | { 5 | vec2 uv; 6 | } vsOut; 7 | 8 | const int grid_divisor = 64; 9 | const float half_grid_divisor = 32.0; 10 | const float half_quad_size = 0.5; 11 | 12 | void main() 13 | { 14 | // Positions for a unit quad in the XZ plane 15 | const vec4 vertices[] = { 16 | {-half_quad_size, 0.0, -half_quad_size, 1.0}, 17 | {half_quad_size, 0.0, -half_quad_size, 1.0}, 18 | {-half_quad_size, 0.0, half_quad_size, 1.0}, 19 | {half_quad_size, 0.0, half_quad_size, 1.0} 20 | }; 21 | 22 | // Calculate XZ offsets for each quad 23 | float x_offset = float(mod(gl_InstanceID, grid_divisor)); 24 | float z_offset = float(floor(gl_InstanceID / grid_divisor)); 25 | vec2 offsets = vec2(x_offset, z_offset); 26 | 27 | // Assign UV coordinates to each quad 28 | // This makes each quad's UV coordinates go from offset -> offset + 1 / grid_divisor; 29 | vsOut.uv = (vertices[gl_VertexID].xz + vec2(half_quad_size) + offsets) / float(grid_divisor); 30 | 31 | // This evenly distributes the grids around the origin 32 | gl_Position = vertices[gl_VertexID] + vec4(x_offset - half_grid_divisor, 0.0, z_offset - half_grid_divisor, 0.0); 33 | } 34 | -------------------------------------------------------------------------------- /src/projects/tesselation_displacement_map/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This uses a displacment map to offset vertices and tesselation 2 | // This uses an instanced quad with vertices embedded in the shader (there are no vertex attributes) 3 | // Interactivity: `w` key toggles showing wireframe 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define STB_IMAGE_IMPLEMENTATION 10 | #include "stb/stb_image.h" 11 | 12 | #include "base_app.h" 13 | #include "glsl_program.h" 14 | #include "camera.h" 15 | 16 | class DisplacmentMapTesselationExample : public Application 17 | { 18 | private: 19 | const GLfloat m_time_divisor{ 0.03f }; 20 | GLfloat m_slow_time { 0 }; 21 | GLfloat m_camera_rotation_value { 0 }; 22 | GLfloat m_camera_y_value { 0 }; 23 | const std::string m_displacement_map_path{ "../assets/images/noise.jpg"}; 24 | const std::string m_color_map_path{ "../assets/images/noise_color.jpg" }; 25 | bool m_show_wireframe{ false }; 26 | const GLuint m_vertices_per_patch{ 4 }; 27 | const GLuint m_num_instances{ 64 * 64 }; 28 | const std::vector m_clear_color{ 0.2f, 0.0f, 0.2f, 1.0f }; 29 | const GLfloat displace_weight{ 6.0 }; 30 | Camera m_camera; 31 | glm::mat4 view_matrix{ 1.0 }; 32 | glm::mat4 proj_matrix{ 1.0 }; 33 | GLuint m_vao{ 0 }; 34 | GLuint m_displacement_texture{ 0 }; 35 | GLuint m_color_texture{ 0 }; 36 | std::unique_ptr m_shader; 37 | 38 | void set_info() override 39 | { 40 | Application::set_info(); 41 | m_info.title = "Tesselation Displacement Map"; 42 | } 43 | 44 | void on_key(int key, int action) override 45 | { 46 | Application::on_key(key, action); 47 | if (key == GLFW_KEY_W && action == GLFW_PRESS) 48 | { 49 | m_show_wireframe = !m_show_wireframe; 50 | } 51 | } 52 | 53 | void setup() override 54 | { 55 | // Create shader 56 | m_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/terrain_disp.vert").fragment("../assets/shaders/terrain_disp.frag").tess_control("../assets/shaders/terrain_disp.tesc").tess_eval("../assets/shaders/terrain_disp.tese")); 57 | 58 | // Create and bind a VAO 59 | glCreateVertexArrays(1, &m_vao); 60 | glBindVertexArray(m_vao); 61 | 62 | // Load images 63 | GLint displacement_map_width; 64 | GLint displacement_map_height; 65 | GLint displacement_map_channels; 66 | GLubyte* displacement_map_data = stbi_load(m_displacement_map_path.c_str(), &displacement_map_width, &displacement_map_height, &displacement_map_channels, 0); 67 | if (!displacement_map_data) 68 | { 69 | std::cout << "Failed to load texture" << std::endl; 70 | } 71 | 72 | GLint color_map_width; 73 | GLint color_map_height; 74 | GLint color_map_channels; 75 | GLubyte* color_map_data = stbi_load(m_color_map_path.c_str(), &color_map_width, &color_map_height, &color_map_channels, 0); 76 | if (!color_map_data) 77 | { 78 | std::cout << "Failed to load texture" << std::endl; 79 | } 80 | 81 | // Set up sampler 82 | GLuint repeat_linear_sampler; 83 | glCreateSamplers(1, &repeat_linear_sampler); 84 | glSamplerParameteri(repeat_linear_sampler, GL_TEXTURE_WRAP_S, GL_REPEAT); 85 | glSamplerParameteri(repeat_linear_sampler, GL_TEXTURE_WRAP_T, GL_REPEAT); 86 | glSamplerParameteri(repeat_linear_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 87 | glSamplerParameteri(repeat_linear_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 88 | glBindSampler(0, repeat_linear_sampler); 89 | glBindSampler(1, repeat_linear_sampler); 90 | 91 | // Set up textures 92 | glCreateTextures(GL_TEXTURE_2D, 1, &m_displacement_texture); 93 | glTextureStorage2D(m_displacement_texture, 1, GL_RGB8, displacement_map_width, displacement_map_height); 94 | glTextureSubImage2D(m_displacement_texture, 0, 0, 0, displacement_map_width, displacement_map_height, GL_RGB, GL_UNSIGNED_BYTE, displacement_map_data); 95 | 96 | glCreateTextures(GL_TEXTURE_2D, 1, &m_color_texture); 97 | glTextureStorage2D(m_color_texture, 1, GL_RGB8, color_map_width, color_map_height); 98 | glTextureSubImage2D(m_color_texture, 0, 0, 0, color_map_width, color_map_height, GL_RGB, GL_UNSIGNED_BYTE, color_map_data); 99 | 100 | // Bind textures 101 | glBindTextureUnit(0, m_displacement_texture); 102 | glBindTextureUnit(1, m_color_texture); 103 | 104 | // Free image data 105 | stbi_image_free(displacement_map_data); 106 | stbi_image_free(color_map_data); 107 | } 108 | 109 | void render(double current_time) override 110 | { 111 | // Set OpenGL state 112 | glViewport(0, 0, m_info.window_width, m_info.window_height); 113 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 114 | glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0); 115 | glEnable(GL_DEPTH_TEST); 116 | glDepthFunc(GL_LEQUAL); 117 | glPatchParameteri(GL_PATCH_VERTICES, m_vertices_per_patch); 118 | 119 | // Show or hide wireframe 120 | if (m_show_wireframe) 121 | { 122 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 123 | } 124 | else 125 | { 126 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 127 | } 128 | 129 | // Move camera position over time 130 | // The numbers here offset the camera position arbitrarily over time 131 | m_slow_time = static_cast(current_time) * m_time_divisor; 132 | m_camera_rotation_value = sinf(m_slow_time * 5.0f) * 15.0f; 133 | m_camera_y_value = cosf(m_slow_time * 5.0f) + 5.0f; 134 | m_camera.set_position(sinf(m_slow_time) * m_camera_rotation_value, m_camera_y_value, cosf(m_slow_time) * m_camera_rotation_value); 135 | 136 | // Set uniforms in shader 137 | m_shader->use(); 138 | view_matrix = m_camera.get_view_matrix(); 139 | proj_matrix = m_camera.get_proj_matrix(); 140 | m_shader->uniform("u_model_view_projection_matrix", proj_matrix * view_matrix); 141 | m_shader->uniform("u_displacement_weight", displace_weight); 142 | 143 | glDrawArraysInstanced(GL_PATCHES, 0, m_vertices_per_patch, m_num_instances); 144 | }; 145 | }; 146 | 147 | int main(int argc, char* argv[]) 148 | { 149 | std::unique_ptr app{ new DisplacmentMapTesselationExample }; 150 | app->run(); 151 | } -------------------------------------------------------------------------------- /src/projects/texture/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "texture") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/stb/stb_image.h 15 | ${INCLUDE_DIR}/projects/base_app.h 16 | ${INCLUDE_DIR}/projects/glsl_program.h 17 | ${INCLUDE_DIR}/projects/camera.h 18 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 20 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 21 | ) 22 | 23 | # Add executable to be built 24 | add_executable(${PROJECT_NAME} ${SOURCES}) 25 | 26 | # Specify libraries to be used 27 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 28 | 29 | # Copy resources 30 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 31 | 32 | # Other option 33 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 34 | 35 | # Set correct CWD 36 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 37 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 38 | endif() 39 | -------------------------------------------------------------------------------- /src/projects/texture/assets/images/0.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9b9e4a6325a65e19e4712058b3a517d758c1cc2362072e58fec00f2c879099d2 3 | size 20434 4 | -------------------------------------------------------------------------------- /src/projects/texture/assets/shaders/simple_quad.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | in VS_OUT 4 | { 5 | vec2 uv; 6 | } fs_in; 7 | 8 | uniform sampler2D image_sampler; 9 | 10 | out vec4 frag_color; 11 | 12 | void main() 13 | { 14 | frag_color= texture(image_sampler, fs_in.uv); 15 | } 16 | -------------------------------------------------------------------------------- /src/projects/texture/assets/shaders/simple_quad.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | out VS_OUT 4 | { 5 | vec2 uv; 6 | } vs_out; 7 | 8 | void main() 9 | { 10 | const vec2[4] positions = { 11 | {-0.5, -0.5}, 12 | {0.5, -0.5}, 13 | {-0.5, 0.5}, 14 | {0.5, 0.5} 15 | }; 16 | 17 | vec2 position = positions[gl_VertexID].xy; 18 | vec2 uvs = vec2(0.0, 1.0) - position + 0.5; 19 | uvs.x = 1.0 - uvs.x; 20 | vs_out.uv = uvs; 21 | 22 | gl_Position = vec4(position, 0.0, 1.0); 23 | } 24 | -------------------------------------------------------------------------------- /src/projects/texture/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This loads a jpeg image and stores it in the S3TC compressed format 2 | // It checks that it is compressed and gets the compressed size 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define STB_IMAGE_IMPLEMENTATION 9 | #include "stb/stb_image.h" 10 | 11 | #include "base_app.h" 12 | #include "glsl_program.h" 13 | 14 | class TextureArrayExample : public Application 15 | { 16 | private: 17 | GLuint m_vao{ 0 }; 18 | GLuint m_texture{ 0 }; 19 | GLint m_is_image_compressed{ 0 }; 20 | GLint m_image_compressed_size{ 0 }; 21 | const std::string m_image_path{ "../assets/images/0.jpg" }; 22 | const std::vector m_clear_color { 0.2f, 0.0f, 0.2f, 1.0f }; 23 | std::unique_ptr m_shader; 24 | 25 | void setup() override 26 | { 27 | // Set and use shader 28 | m_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/simple_quad.vert").fragment("../assets/shaders/simple_quad.frag")); 29 | m_shader->use(); 30 | 31 | // Create and bind a VAO 32 | glCreateVertexArrays(1, &m_vao); 33 | glBindVertexArray(m_vao); 34 | 35 | // Make a texture object 36 | glCreateTextures(GL_TEXTURE_2D, 1, &m_texture); 37 | 38 | // Load the image 39 | GLint width; 40 | GLint height; 41 | GLint number_of_channels; 42 | 43 | GLubyte* data = stbi_load(m_image_path.c_str(), &width, &height, &number_of_channels, 0); 44 | if (!data) 45 | { 46 | std::cout << "Failed to load texture" << std::endl; 47 | } 48 | 49 | glTextureStorage2D(m_texture, 1, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, width, height); 50 | glTextureSubImage2D(m_texture, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, data); 51 | stbi_image_free(data); 52 | 53 | glTextureParameteri(m_texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 54 | glTextureParameteri(m_texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 55 | 56 | 57 | glGetTextureLevelParameteriv(m_texture, 0, GL_TEXTURE_COMPRESSED, &m_is_image_compressed); 58 | if (m_is_image_compressed) 59 | { 60 | std::cout << "Texture is compressed" << std::endl; 61 | } 62 | 63 | 64 | glGetTextureLevelParameteriv(m_texture, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &m_image_compressed_size); 65 | { 66 | std::cout << "Texture size is: " << m_image_compressed_size << std::endl; 67 | } 68 | 69 | glBindTextureUnit(0, m_texture); 70 | } 71 | 72 | void render(double current_time) override 73 | { 74 | glViewport(0, 0, m_info.window_width, m_info.window_height); 75 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 76 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 77 | }; 78 | 79 | 80 | }; 81 | 82 | int main(int argc, char* argv[]) 83 | { 84 | std::unique_ptr app{ new TextureArrayExample }; 85 | app->run(); 86 | } 87 | -------------------------------------------------------------------------------- /src/projects/texture_array/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "texture_array") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/stb/stb_image.h 15 | ${INCLUDE_DIR}/projects/base_app.h 16 | ${INCLUDE_DIR}/projects/glsl_program.h 17 | ${INCLUDE_DIR}/projects/camera.h 18 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 20 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 21 | ) 22 | 23 | # Add executable to be built 24 | add_executable(${PROJECT_NAME} ${SOURCES}) 25 | 26 | # Specify libraries to be used 27 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 28 | 29 | # Copy resources 30 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 31 | 32 | # Other option 33 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 34 | 35 | # Set correct CWD 36 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 37 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 38 | endif() 39 | -------------------------------------------------------------------------------- /src/projects/texture_array/assets/shaders/simple_quad.frag: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | in VS_OUT 4 | { 5 | flat int image_index; 6 | vec2 uv; 7 | } fs_in; 8 | 9 | uniform sampler2DArray image_sampler; 10 | 11 | layout (location = 0) out vec4 frag_color; 12 | 13 | void main() 14 | { 15 | frag_color = texture(image_sampler, vec3(fs_in.uv, float(fs_in.image_index))); 16 | } 17 | -------------------------------------------------------------------------------- /src/projects/texture_array/assets/shaders/simple_quad.vert: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | uniform float u_offset_x; 4 | uniform float u_offset_y; 5 | uniform float u_scale; 6 | uniform int u_image_index; 7 | 8 | out VS_OUT 9 | { 10 | flat int image_index; 11 | vec2 uv; 12 | } vs_out; 13 | 14 | void main() 15 | { 16 | const vec2[4] positions = { 17 | {-0.5, -0.5}, 18 | {0.5, -0.5}, 19 | {-0.5, 0.5}, 20 | {0.5, 0.5} 21 | }; 22 | 23 | // Flip the y coordinate 24 | vs_out.uv = vec2(0, 1.0) - positions[gl_VertexID].xy + 0.5; 25 | 26 | vec2 position = positions[gl_VertexID].xy + vec2(u_offset_x, u_offset_y); 27 | position *= vec2(u_scale); 28 | gl_Position = vec4(position, 0.0, 1.0); 29 | 30 | vs_out.image_index = u_image_index; 31 | } 32 | -------------------------------------------------------------------------------- /src/projects/texture_array/assets/texture_array/0.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9b9e4a6325a65e19e4712058b3a517d758c1cc2362072e58fec00f2c879099d2 3 | size 20434 4 | -------------------------------------------------------------------------------- /src/projects/texture_array/assets/texture_array/1.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:28b0609b2cc3f0c24a9966b7763e1919249b24315a5d49f40e830c71807c5d0c 3 | size 20501 4 | -------------------------------------------------------------------------------- /src/projects/texture_array/assets/texture_array/2.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:be99bbbbcd07b6e6a96371011f55c658771f720f1ef7264e8ea7dcf5fd9cff8c 3 | size 19982 4 | -------------------------------------------------------------------------------- /src/projects/texture_array/assets/texture_array/3.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:ca0efe56fb26f16d148dd287ad6717ede2ba382be0d1e3e0a908dd9b7b1c696f 3 | size 20152 4 | -------------------------------------------------------------------------------- /src/projects/texture_array/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This creates an array texture in S3TC compressed format 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define STB_IMAGE_IMPLEMENTATION 9 | #include "stb/stb_image.h" 10 | 11 | #include "base_app.h" 12 | #include "glsl_program.h" 13 | 14 | class TextureArrayExample : public Application 15 | { 16 | private: 17 | GLuint m_vao{ 0 }; 18 | GLuint m_texture_array{ 0 }; 19 | const std::string m_image_base_path{ "../assets/texture_array/" }; 20 | const GLuint m_num_billboards{ 4 }; 21 | const std::vector m_clear_color{ 0.2f, 0.0f, 0.2f, 1.0f }; 22 | std::unique_ptr m_shader; 23 | 24 | void setup() override 25 | { 26 | // Set and use shader 27 | m_shader = std::make_unique(GlslProgram::Format().vertex("../assets/shaders/simple_quad.vert").fragment("../assets/shaders/simple_quad.frag")); 28 | m_shader->use(); 29 | m_shader->introspect(); 30 | 31 | // Create and bind a VAO 32 | glCreateVertexArrays(1, &m_vao); 33 | glBindVertexArray(m_vao); 34 | 35 | // Make a 2D array texture 36 | glCreateTextures(GL_TEXTURE_2D_ARRAY, 1, &m_texture_array); 37 | 38 | for (int index{ 0 }; index != m_num_billboards; ++index) 39 | { 40 | // Get image path 41 | std::string image_path{ m_image_base_path + std::to_string(index) + ".jpg" }; 42 | 43 | // Load the image 44 | GLint width; 45 | GLint height; 46 | GLint number_of_channels; 47 | GLubyte* data = stbi_load(image_path.c_str(), &width, &height, &number_of_channels, 0); 48 | if (!data) 49 | { 50 | std::cout << "Failed to load texture" << std::endl; 51 | } 52 | 53 | // Use first image to set texture storage parameters 54 | if (!index) 55 | { 56 | glTextureStorage3D(m_texture_array, 1, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, width, height, m_num_billboards); 57 | } 58 | 59 | glTextureSubImage3D(m_texture_array, 0, 0, 0, index, width, height, 1, GL_RGB, GL_UNSIGNED_BYTE, data); 60 | stbi_image_free(data); 61 | } 62 | 63 | glTextureParameteri(m_texture_array, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 64 | glTextureParameteri(m_texture_array, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 65 | 66 | glBindTextureUnit(0, m_texture_array); 67 | } 68 | 69 | void render(double current_time) override 70 | { 71 | glViewport(0, 0, m_info.window_width, m_info.window_height); 72 | glClearBufferfv(GL_COLOR, 0, m_clear_color.data()); 73 | glClearBufferfi(GL_DEPTH_STENCIL, 0, 1.0f, 0); 74 | 75 | for (GLuint index{ 0 }; index != m_num_billboards; ++index) 76 | { 77 | // Set uniforms 78 | // The random numbersj just make the sprites move over time 79 | auto offset_x = static_cast(std::fmod(current_time / 2, 3) * 2.0 - 3.0); 80 | auto offset_y = static_cast(6.0f - static_cast(index) * 2.0f); 81 | auto scale = static_cast(0.1f + static_cast(index) / 8.0f); 82 | m_shader->uniform("u_image_index", index); 83 | m_shader->uniform("u_offset_x", offset_x); 84 | m_shader->uniform("u_offset_y", offset_y); 85 | m_shader->uniform("u_scale", scale); 86 | 87 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 88 | } 89 | }; 90 | }; 91 | 92 | int main(int argc, char* argv[]) 93 | { 94 | std::unique_ptr app{ new TextureArrayExample }; 95 | app->run(); 96 | } 97 | -------------------------------------------------------------------------------- /src/projects/transform_feedback/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Set project name 2 | set(PROJECT_NAME "transform_feedback") 3 | 4 | # Set directory for the build 5 | set(PROJECT_BUILD_DIR ${CMAKE_SOURCE_DIR}/bin/${PROJECT_NAME}) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BUILD_DIR}/$) 7 | 8 | # Set sources 9 | set(PROJECTS_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src/projects) 10 | set(SOURCES 11 | src/main.cpp 12 | ${INCLUDE_DIR}/glad/src/glad.c 13 | ${INCLUDE_DIR}/glm/glm/gtc/matrix_transform.hpp 14 | ${INCLUDE_DIR}/projects/base_app.h 15 | ${INCLUDE_DIR}/projects/glsl_program.h 16 | ${INCLUDE_DIR}/projects/camera.h 17 | ${PROJECTS_SOURCE_DIR}/base_app/base_app.cpp 18 | ${PROJECTS_SOURCE_DIR}/base_app/glsl_program.cpp 19 | ${PROJECTS_SOURCE_DIR}/base_app/camera.cpp 20 | ) 21 | 22 | # Add executable to be built 23 | add_executable(${PROJECT_NAME} ${SOURCES}) 24 | 25 | # Specify libraries to be used 26 | target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS} glfw ${GLFW_LIBRARIES}) 27 | 28 | # Copy resources 29 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/assets ${PROJECT_BUILD_DIR}/assets) 30 | 31 | # Other option 32 | # file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${PROJECT_BUILD_DIR}/) 33 | 34 | # Set correct CWD 35 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 36 | set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/projects/transform_feedback/src/main.cpp: -------------------------------------------------------------------------------- 1 | // This calculates the square root of some values and reads it back with transform feedback 2 | // There is no graphical output 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "glad/glad.h" 9 | 10 | #include "base_app.h" 11 | 12 | static const GLchar* vertex_shader_source[] = 13 | { 14 | "#version 440 core\n" 15 | 16 | "in float in_value;\n" 17 | "out float out_value;\n" 18 | 19 | "void main()\n" 20 | "{\n" 21 | " out_value = sqrt(in_value);\n" 22 | "}\n" 23 | }; 24 | 25 | class TransformFeedbackExample : public Application 26 | { 27 | private: 28 | 29 | GLuint m_vao{ 0 }; 30 | GLuint m_vbo{ 0 }; 31 | GLuint m_tbo{ 0 }; 32 | GLfloat m_data[5]{ 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; 33 | const std::vector m_clear_color{ 0.2f, 0.0f, 0.2f, 1.0f }; 34 | 35 | GLuint create_shader_program() 36 | { 37 | // Create vertex shader 38 | const GLuint log_length{ 1024 }; 39 | GLint success; 40 | GLchar info_log[log_length]; 41 | GLuint shader = glCreateShader(GL_VERTEX_SHADER); 42 | glShaderSource(shader, 1, vertex_shader_source, nullptr); 43 | glCompileShader(shader); 44 | glGetShaderiv(shader, GL_COMPILE_STATUS, &success); 45 | if (!success) 46 | { 47 | glGetShaderInfoLog(shader, log_length, nullptr, info_log); 48 | std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << "\n" << info_log << "\n ---- " << std::endl; 49 | } 50 | 51 | // Create program 52 | GLuint program = glCreateProgram(); 53 | glAttachShader(program, shader); 54 | 55 | // Specify transform feedback varyings 56 | static const GLchar* feedbackVaryings[] = { "out_value" }; 57 | glTransformFeedbackVaryings(program, sizeof(feedbackVaryings) / sizeof(*feedbackVaryings), feedbackVaryings, GL_INTERLEAVED_ATTRIBS); 58 | 59 | // Link and use program 60 | glLinkProgram(program); 61 | glGetProgramiv(program, GL_LINK_STATUS, &success); 62 | if (!success) 63 | { 64 | glGetProgramInfoLog(program, log_length, nullptr, info_log); 65 | std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << "\n" << info_log << "\n ---- " << std::endl; 66 | } 67 | 68 | return program; 69 | } 70 | 71 | void setup() override 72 | { 73 | GLuint program = create_shader_program(); 74 | glUseProgram(program); 75 | 76 | // Create VBO 77 | const GLuint flags{ 0 }; 78 | glCreateBuffers(1, &m_vbo); 79 | glNamedBufferStorage(m_vbo, sizeof(m_data), m_data, flags); 80 | 81 | // Create and bind VAO 82 | GLint attrib_index{ glGetAttribLocation(program, "in_value") }; 83 | const GLuint size{ 1 }; 84 | const GLenum type{ GL_FLOAT }; 85 | const GLboolean normalized{ GL_FALSE }; 86 | const GLuint stride{ sizeof(GLfloat) }; 87 | const GLuint offset{ 0 }; 88 | const GLuint binding_index{ 0 }; 89 | 90 | glCreateVertexArrays(1, &m_vao); 91 | glEnableVertexArrayAttrib(m_vao, static_cast(attrib_index)); 92 | glVertexArrayAttribFormat(m_vao, static_cast(attrib_index), size, type, normalized, offset); 93 | glVertexArrayVertexBuffer(m_vao, binding_index, m_vbo, offset, stride); 94 | glVertexArrayAttribBinding(m_vao, static_cast(attrib_index), binding_index); 95 | glBindVertexArray(m_vao); 96 | 97 | // Create transform feedback buffer 98 | glCreateBuffers(1, &m_tbo); 99 | glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_tbo); 100 | glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(m_data), nullptr, GL_DYNAMIC_COPY); 101 | 102 | // Perform feedback transform 103 | glEnable(GL_RASTERIZER_DISCARD); 104 | glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_tbo); 105 | glBeginTransformFeedback(GL_POINTS); 106 | glDrawArrays(GL_POINTS, 0, sizeof(m_data) / sizeof(*m_data)); 107 | glEndTransformFeedback(); 108 | glDisable(GL_RASTERIZER_DISCARD); 109 | glFlush(); 110 | 111 | // Fetch and print results 112 | GLfloat feedback_values[5]; 113 | glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(feedback_values), feedback_values); 114 | for (auto value : feedback_values) 115 | { 116 | std::cout << value < app{ new TransformFeedbackExample}; 124 | app->run(); 125 | } 126 | --------------------------------------------------------------------------------