├── .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 |
--------------------------------------------------------------------------------