├── .gitignore
├── LICENSE
├── README.md
├── helper_functions.h
├── screenshots
├── frustum_radius.png
├── shadow_mapping.png
├── shadow_mapping_advanced.png
├── shadow_mapping_cascade.png
├── shadow_mapping_cascade_advanced.png
├── shadow_mapping_cascade_horizontal_and_vertical.png
├── shadow_mapping_cascade_texture_array.png
├── shadow_mapping_pcf.png
├── variance_shadow_mapping.png
└── variance_shadow_mapping_MSM4.png
├── shadow_mapping.c
├── shadow_mapping_advanced.c
├── shadow_mapping_bidimensional_texture.c
├── shadow_mapping_cascade.c
├── shadow_mapping_cascade_advanced.c
├── shadow_mapping_cascade_horizontal.c
├── shadow_mapping_cascade_horizontal_and_vertical.c
├── shadow_mapping_cascade_texture_array.c
├── shadow_mapping_pcf.c
├── shadow_mapping_sESM1.c
├── shadow_mapping_with_dof.c
├── variance_shadow_mapping.c
├── variance_shadow_mapping_EVSM2.c
├── variance_shadow_mapping_EVSM4.c
└── variance_shadow_mapping_MSM4.c
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Object files
5 | *.o
6 | *.ko
7 | *.obj
8 | *.elf
9 |
10 | # Linker output
11 | *.ilk
12 | *.map
13 | *.exp
14 |
15 | # Precompiled Headers
16 | *.gch
17 | *.pch
18 |
19 | # Libraries
20 | *.lib
21 | *.a
22 | *.la
23 | *.lo
24 |
25 | # Shared objects (inc. Windows DLLs)
26 | *.dll
27 | *.so
28 | *.so.*
29 | *.dylib
30 |
31 | # Executables
32 | *.exe
33 | *.out
34 | *.app
35 | *.i*86
36 | *.x86_64
37 | *.hex
38 |
39 | # Debug files
40 | *.dSYM/
41 | *.su
42 | *.idb
43 | *.pdb
44 |
45 | # Kernel Module Compile Results
46 | *.mod*
47 | *.cmd
48 | .tmp_versions/
49 | modules.order
50 | Module.symvers
51 | Mkfile.old
52 | dkms.conf
53 |
54 | *.pro
55 | *.user
56 | *.sh
57 | *.ini
58 | *.autosave
59 | *.qmake.stash
60 | *.stash
61 | code/
62 | code
63 | reps/
64 | shadow_mapping
65 | shadow_mapping_advanced
66 | shadow_mapping_bidimensional_texture
67 | shadow_mapping_pcf
68 | shadow_mapping_cascade
69 | shadow_mapping_cascade_horizontal
70 | shadow_mapping_cascade_horizontal_and_vertical
71 | shadow_mapping_cascade_advanced
72 | shadow_mapping_cascade_texture_array
73 | shadow_mapping_sESM
74 | shadow_mapping_sESM1
75 | shadow_mapping_sESM2
76 | shadow_mapping_with_dof
77 | variance_shadow_mapping
78 | variance_shadow_mapping_EVSM
79 | variance_shadow_mapping_EVSM2
80 | variance_shadow_mapping_EVSM4
81 | variance_shadow_mapping_MSM
82 | variance_shadow_mapping_MSM2
83 | variance_shadow_mapping_MSM4
84 | Makefile
85 |
86 | # Other Stuff
87 | **/reps/
88 | reps/
89 | **/*.sh
90 | Tests/freeglut.dll
91 | Tests/glut.dll
92 | Tests/glut32.dll
93 | Tests/html/
94 | docs/
95 | reps/
96 | Tests/reps/
97 | *.pro
98 | *.user
99 | *.cfg
100 | *.ini
101 | Tests/Makefile
102 | *.exe
103 | Tests/test_im*
104 | Tests/test_shadows
105 | Tests/test_teapot
106 | Tests/test_matrix_stack
107 | Tests/test_im
108 | Tests/test_sdf
109 | glImmediateMode.h
110 | obj.h
111 | Sdf/
112 | screenshots/frustum_section_radius.png
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Flix
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 | # Tiny-OpenGL-Shadow-Mapping-Examples
2 | Compact OpenGL Shadow Mapping Examples in a single compilation unit.
3 |
4 |
5 | ## Features
6 | * A single .c file per example.
7 | * No extern math library used. No math structs used. Just plain float arrays.
8 | * Helper functions (mainly math stuff) grouped in the header helper_functions.h, for easier inclusion into user code.
9 | * Only a single directional light is used in all the examples.
10 | * Currently all the examples are flicker-free while the camera moves and rotates (on static objects). Please see the implementation section below for further info.
11 |
12 |
13 | ## Dependencies
14 | * glut (or freeglut)
15 | * glew (Windows only)
16 |
17 |
18 | ## How to compile
19 | Compilation instructions are at the top of each .c file.
20 | All the demos have been tested on Linux using an **NVIDIA graphic card** (AMD/ATI might need some glsl tweaking).
21 |
22 | ## Help needed
23 | The demos: shadow_mapping_sESM1.c, variance_shadow_mapping.c, variance_shadow_mapping_EVSM2.c and variance_shadow_mapping_EVSM4.c are all **WRONG**! The shadows should look blurry and instead are sharp! If you can help to fix these (and other) issues, please post a message in the *"Issues"* or *"Pull requests"* sections. Thank you in advance!
24 |
25 | ## Screenshots
26 | 
27 | 
28 | 
29 | 
30 | 
31 | 
32 | 
33 | 
34 | 
35 |
36 | ## Implmentation
37 | All the examples in this repository implement "Stable Shadow Mapping".
38 | That means that static shadows should not flicker when the camera moves or rotates, at the expense of the shadow resolution, that is usually only a half of the resolution that can be used in any "Non-Stable Shadow Mapping" technique.
39 |
40 |
41 | In addition, the light matrices calculated by the "Stable Shadow Mapping" algorithm are not optimal for frustum culling usage, and if we want to filter the shadow map (like in VSM), we waste a lot of resources, unless we manage to calculate a tighter texture viewport somehow (we address this issue in **shadow_mapping_advanced.c** and **shadow_mapping_cascade_advanced.c**).
42 |
43 |
44 | The demos **shadow_mapping.c**,**shadow_mapping_cascade.c** and **shadow_mapping_cascade_texture_array.c** can be compiled with the optional definition **USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE** so that we can compare the two techniques.
45 |
46 |
47 | The "Stable Shadow Mapping" algorithm calculates the minimum sphere that encloses the camera frustum, and further encloses it in a light ortho projection matrix.
48 |
49 | ### Calculation of the camera frustum center and radius
50 | The picture below is valid for every frustum field of view (fovx, fovy and fovd), but if we use the diagonal fov, then the points at **tn** and **tf** are real frustum corners.
51 | (**n** is the near camera frustum clipping plane and **f** is the far camera frustum clipping plane)
52 |
53 | 
54 |
55 | θ = (fovd/2); // half diagonal fov
56 | t = tan(θ);
57 | tSquared = (1.0+ar*ar)*tan(0.5*fovy)*tan(0.5*fovy); // ar = camera aspect ratio
58 | h = 0.5*(f+n) ; // half way between near and far plane
59 | d = h*(1.0+tSquared); // Found after all the math passages from the picture above
60 | if (d>f) d=f; // Clamping to save some shadow resolution
61 | r = sqrt((tSquared*f*f) + (f-d)*(f-d));
62 |
63 |
64 |
65 | ## Useful Links
66 | [opengl-cascaded-shadow-maps with code at the bottom](https://johanmedestrom.wordpress.com/2016/03/18/opengl-cascaded-shadow-maps/)
67 |
68 | [graphics-tech-shadow-maps-part-1 (explains why shadow resolution gets wasted)](http://the-witness.net/news/2010/03/graphics-tech-shadow-maps-part-1/)
69 |
70 | [graphics-tech-shadow-maps-part-2 (proposes a method to save GPU memory)](http://the-witness.net/news/2010/04/graphics-tech-shadow-maps-part-2-save-25-texture-memory-and-possibly-much-more/)
71 |
72 | [A sampling of shadow techniques](https://mynameismjp.wordpress.com/2013/09/10/shadow-maps/)
73 |
74 | [GPU Gems 3 - Chapter 10. Parallel-Split Shadow Maps on Programmable GPUs](https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch10.html)
75 |
76 | [variance_shadow_mapping.c is greatly based on this Fabien Sanglard's work](http://fabiensanglard.net/shadowmappingVSM/)
77 |
78 | [glsl-fast-gaussian-blur: my blur filters are based on this Github repository](https://github.com/Jam3/glsl-fast-gaussian-blur/)
79 |
80 | [Github repository containing Direct3D implementations of many modern shadow mapping techniques](https://github.com/TheRealMJP/Shadows)
81 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/screenshots/frustum_radius.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples/c511f1ea909d7e87a1854bf43893dc590c89ee74/screenshots/frustum_radius.png
--------------------------------------------------------------------------------
/screenshots/shadow_mapping.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples/c511f1ea909d7e87a1854bf43893dc590c89ee74/screenshots/shadow_mapping.png
--------------------------------------------------------------------------------
/screenshots/shadow_mapping_advanced.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples/c511f1ea909d7e87a1854bf43893dc590c89ee74/screenshots/shadow_mapping_advanced.png
--------------------------------------------------------------------------------
/screenshots/shadow_mapping_cascade.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples/c511f1ea909d7e87a1854bf43893dc590c89ee74/screenshots/shadow_mapping_cascade.png
--------------------------------------------------------------------------------
/screenshots/shadow_mapping_cascade_advanced.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples/c511f1ea909d7e87a1854bf43893dc590c89ee74/screenshots/shadow_mapping_cascade_advanced.png
--------------------------------------------------------------------------------
/screenshots/shadow_mapping_cascade_horizontal_and_vertical.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples/c511f1ea909d7e87a1854bf43893dc590c89ee74/screenshots/shadow_mapping_cascade_horizontal_and_vertical.png
--------------------------------------------------------------------------------
/screenshots/shadow_mapping_cascade_texture_array.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples/c511f1ea909d7e87a1854bf43893dc590c89ee74/screenshots/shadow_mapping_cascade_texture_array.png
--------------------------------------------------------------------------------
/screenshots/shadow_mapping_pcf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples/c511f1ea909d7e87a1854bf43893dc590c89ee74/screenshots/shadow_mapping_pcf.png
--------------------------------------------------------------------------------
/screenshots/variance_shadow_mapping.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples/c511f1ea909d7e87a1854bf43893dc590c89ee74/screenshots/variance_shadow_mapping.png
--------------------------------------------------------------------------------
/screenshots/variance_shadow_mapping_MSM4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples/c511f1ea909d7e87a1854bf43893dc590c89ee74/screenshots/variance_shadow_mapping_MSM4.png
--------------------------------------------------------------------------------
/shadow_mapping.c:
--------------------------------------------------------------------------------
1 | // https://github.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples
2 | /** License
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 | * SOFTWARE.
18 | */
19 |
20 | // DEPENDENCIES:
21 | /*
22 | -> glut or freeglut (the latter is recommended)
23 | -> glew (Windows only)
24 | */
25 |
26 | // HOW TO COMPILE:
27 | /*
28 | // LINUX:
29 | gcc -O2 -std=gnu89 -no-pie shadow_mapping.c -o shadow_mapping -I"../" -lglut -lGL -lX11 -lm
30 | // WINDOWS (here we use the static version of glew, and glut32.lib, that can be replaced by freeglut.lib):
31 | cl /O2 /MT /Tc shadow_mapping.c /D"GLEW_STATIC" /I"../" /link /out:shadow_mapping.exe glut32.lib glew32s.lib opengl32.lib gdi32.lib Shell32.lib comdlg32.lib user32.lib kernel32.lib
32 |
33 |
34 | // IN ADDITION:
35 | By default the source file assumes that every OpenGL-related header is in "GL/".
36 | But you can define in the command-line the correct paths you use in your system
37 | for glut.h, glew.h, etc. with something like:
38 | -DGLUT_PATH=\"Glut/glut.h\"
39 | -DGLEW_PATH=\"Glew/glew.h\"
40 | (this syntax works on Linux, don't know about Windows)
41 | */
42 |
43 | //#define USE_GLEW // By default it's only defined for Windows builds (but can be defined in Linux/Mac builds too)
44 |
45 |
46 | #define PROGRAM_NAME "shadow_mapping"
47 | #define VISUALIZE_DEPTH_TEXTURE
48 | #define SHADOW_MAP_RESOLUTION 1024 //1024
49 | #define SHADOW_MAP_CLAMP_MODE GL_CLAMP_TO_EDGE // GL_CLAMP or GL_CLAMP_TO_EDGE or GL_CLAMP_TO_BORDER
50 | // GL_CLAMP; // sampling outside of the shadow map gives always shadowed pixels
51 | // GL_CLAMP_TO_EDGE; // sampling outside of the shadow map can give shadowed or unshadowed pixels (it depends on the edge of the shadow map)
52 | // GL_CLAMP_TO_BORDER; // sampling outside of the shadow map gives always non-shadowed pixels (if we set the border color correctly)
53 | #define SHADOW_MAP_FILTER GL_LINEAR // GL_LINEAR or GL_NEAREST (GL_LINEAR is more useful with a sampler2DShadow, that cannot be used with esponential shadow mapping)
54 |
55 | //#define USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE // Better resolution, but shadow-swimming as the camera rotates (on static objects). Please see README.md about it.
56 | // in camera ortho3d mode [F1], the resolution is further better, but shadow-swimming appears when zooming in-out too.
57 |
58 | // These path definitions can be passed to the compiler command-line
59 | #ifndef GLUT_PATH
60 | # define GLUT_PATH "GL/glut.h" // Mandatory
61 | #endif //GLEW_PATH
62 | #ifndef FREEGLUT_EXT_PATH
63 | # define FREEGLUT_EXT_PATH "GL/freeglut_ext.h" // Optional (used only if glut.h comes from the freeglut library)
64 | #endif //GLEW_PATH
65 | #ifndef GLEW_PATH
66 | # define GLEW_PATH "GL/glew.h" // Mandatory for Windows only
67 | #endif //GLEW_PATH
68 |
69 | #ifdef _WIN32
70 | # include "windows.h"
71 | # define USE_GLEW
72 | #endif //_WIN32
73 |
74 | #ifdef USE_GLEW
75 | # include GLEW_PATH
76 | #else //USE_GLEW
77 | # define GL_GLEXT_PROTOTYPES
78 | #endif //USE_GLEW
79 |
80 | #include GLUT_PATH
81 | #ifdef __FREEGLUT_STD_H__
82 | # include FREEGLUT_EXT_PATH
83 | #endif //__FREEGLUT_STD_H__
84 |
85 | #define STR_MACRO(s) #s
86 | #define XSTR_MACRO(s) STR_MACRO(s)
87 |
88 | #include "helper_functions.h" // please search this .c file for "Helper_":
89 | // only very few of its functions are used.
90 |
91 | #include
92 | #include
93 | #include
94 |
95 |
96 | // Config file handling: basically there's an .ini file next to the
97 | // exe that you can tweak. (it's just an extra)
98 | const char* ConfigFileName = PROGRAM_NAME".ini";
99 | typedef struct {
100 | int fullscreen_width,fullscreen_height;
101 | int windowed_width,windowed_height;
102 | int fullscreen_enabled;
103 | int show_fps;
104 | int use_camera_ortho3d_projection_matrix;
105 | } Config;
106 | void Config_Init(Config* c) {
107 | c->fullscreen_width=c->fullscreen_height=0;
108 | c->windowed_width=960;c->windowed_height=540;
109 | c->fullscreen_enabled=0;
110 | c->show_fps = 0;
111 | c->use_camera_ortho3d_projection_matrix = 0;
112 | }
113 | int Config_Load(Config* c,const char* filePath) {
114 | FILE* f = fopen(filePath, "rt");
115 | char ch='\0';char buf[256]="";
116 | size_t nread=0;
117 | int numParsedItem=0;
118 | if (!f) return -1;
119 | while ((ch = fgetc(f)) !=EOF) {
120 | buf[nread]=ch;
121 | nread++;
122 | if (nread>255) {
123 | nread=0;
124 | continue;
125 | }
126 | if (ch=='\n') {
127 | buf[nread]='\0';
128 | if (nread<2 || buf[0]=='[' || buf[0]=='#') {nread = 0;continue;}
129 | if (nread>2 && buf[0]=='/' && buf[1]=='/') {nread = 0;continue;}
130 | // Parse
131 | switch (numParsedItem) {
132 | case 0:
133 | sscanf(buf, "%d %d", &c->fullscreen_width,&c->fullscreen_height);
134 | break;
135 | case 1:
136 | sscanf(buf, "%d %d", &c->windowed_width,&c->windowed_height);
137 | break;
138 | case 2:
139 | sscanf(buf, "%d", &c->fullscreen_enabled);
140 | break;
141 | case 3:
142 | sscanf(buf, "%d", &c->show_fps);
143 | break;
144 | case 4:
145 | sscanf(buf, "%d", &c->use_camera_ortho3d_projection_matrix);
146 | break;
147 | }
148 | nread=0;
149 | ++numParsedItem;
150 | }
151 | }
152 | fclose(f);
153 | if (c->windowed_width<=0) c->windowed_width=720;
154 | if (c->windowed_height<=0) c->windowed_height=405;
155 | return 0;
156 | }
157 | int Config_Save(Config* c,const char* filePath) {
158 | FILE* f = fopen(filePath, "wt");
159 | if (!f) return -1;
160 | fprintf(f, "[Size In Fullscreen Mode (zero means desktop size)]\n%d %d\n",c->fullscreen_width,c->fullscreen_height);
161 | fprintf(f, "[Size In Windowed Mode]\n%d %d\n",c->windowed_width,c->windowed_height);
162 | fprintf(f, "[Fullscreen Enabled (0 or 1) (CTRL+RETURN)]\n%d\n", c->fullscreen_enabled);
163 | fprintf(f, "[Show FPS (0 or 1) (F2)]\n%d\n", c->show_fps);
164 | fprintf(f, "[Use camera ortho3d projection matrix (0 or 1) (F1)]\n%d\n", c->use_camera_ortho3d_projection_matrix);
165 | fprintf(f,"\n");
166 | fclose(f);
167 | return 0;
168 | }
169 |
170 | Config config;
171 |
172 | // glut has a special fullscreen GameMode that you can toggle with CTRL+RETURN (not in WebGL)
173 | int windowId = 0; // window Id when not in fullscreen mode
174 | int gameModeWindowId = 0; // window Id when in fullscreen mode
175 |
176 | // Now we can start with our program
177 |
178 | // camera data:
179 | float targetPos[3]; // please set it in resetCamera()
180 | float cameraYaw; // please set it in resetCamera()
181 | float cameraPitch; // please set it in resetCamera()
182 | float cameraDistance; // please set it in resetCamera()
183 | float cameraPos[3]; // Derived value (do not edit)
184 | float vMatrix[16]; // view matrix
185 | float cameraSpeed = 0.5f; // When moving it
186 |
187 | // light data
188 | float lightYaw = M_PI*0.425f,lightPitch = M_PI*0.235f; // must be copied to resetLight() too
189 | float lightDirection[4] = {0,1,0,0}; // Derived value (do not edit) [lightDirection[3]==0]
190 |
191 | // pMatrix data:
192 | float pMatrix[16],pMatrixInverse[16]; // projection matrix (pMatrixInverse is used only when USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE is defined)
193 | const float pMatrixFovyDeg = 45.f; // smaller => better shadow resolution
194 | const float pMatrixNearPlane = 0.5f; // bigger => better shadow resolution
195 | const float pMatrixFarPlane = 20.f; // smaller => better shadow resolution
196 |
197 | float instantFrameTime = 16.2f;
198 |
199 | // Optional (to speed up Helper_GlutDrawGeometry(...) a bit)
200 | GLuint gDisplayListBase = 0;GLuint* pgDisplayListBase = &gDisplayListBase; // Can be set to 0 as a fallback.
201 |
202 |
203 | static const char* ShadowPassVertexShader[] = {
204 | " void main() {\n"
205 | " gl_Position = ftransform();\n"
206 | " }\n"
207 | };
208 | static const char* ShadowPassFragmentShader[] = {
209 | " void main() {\n"
210 | " //gl_FragColor = gl_Color;\n"
211 | " }\n"
212 | };
213 | typedef struct {
214 | GLuint fbo;
215 | GLuint textureId;
216 | GLuint program;
217 | } ShadowPass;
218 |
219 | ShadowPass shadowPass;
220 | void InitShadowPass(ShadowPass* sp) {
221 | sp->program = Helper_LoadShaderProgramFromSource(*ShadowPassVertexShader,*ShadowPassFragmentShader);
222 |
223 | // create depth texture
224 | glGenTextures(1, &sp->textureId);
225 | glBindTexture(GL_TEXTURE_2D, sp->textureId);
226 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, SHADOW_MAP_FILTER);
227 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, SHADOW_MAP_FILTER);
228 | # ifndef __EMSCRIPTEN__
229 | glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_MAP_RESOLUTION, SHADOW_MAP_RESOLUTION, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
230 | # else //__EMSCRIPTEN__
231 | glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_MAP_RESOLUTION, SHADOW_MAP_RESOLUTION, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0);
232 | # undef SHADOW_MAP_CLAMP_MODE
233 | # define SHADOW_MAP_CLAMP_MODE GL_CLAMP_TO_EDGE
234 | # endif //__EMSCRIPTEN__
235 | if (SHADOW_MAP_CLAMP_MODE==GL_CLAMP_TO_BORDER) {
236 | const GLfloat border[] = {1.0f,1.0f,1.0f,0.0f };
237 | glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
238 | }
239 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, SHADOW_MAP_CLAMP_MODE );
240 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, SHADOW_MAP_CLAMP_MODE );
241 | glBindTexture(GL_TEXTURE_2D, 0);
242 |
243 | // create depth fbo
244 | glGenFramebuffers(1, &sp->fbo);
245 | glBindFramebuffer(GL_FRAMEBUFFER, sp->fbo);
246 | # ifndef __EMSCRIPTEN__
247 | glDrawBuffer(GL_NONE); // Instruct openGL that we won't bind a color texture with the currently bound FBO
248 | glReadBuffer(GL_NONE);
249 | # endif //__EMSCRIPTEN__
250 | glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,sp->textureId, 0);
251 | {
252 | //Does the GPU support current FBO configuration?
253 | GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
254 | if (status!=GL_FRAMEBUFFER_COMPLETE) printf("glCheckFramebufferStatus(...) FAILED for shadowPass.fbo.\n");
255 | }
256 | glBindFramebuffer(GL_FRAMEBUFFER, 0);
257 | }
258 | void DestroyShadowPass(ShadowPass* sp) {
259 | if (sp->program) {glDeleteProgram(sp->program);sp->program=0;}
260 | if (sp->fbo) {glDeleteBuffers(1,&sp->fbo);sp->fbo=0;}
261 | if (sp->textureId) {glDeleteTextures(1,&sp->textureId);}
262 | }
263 |
264 | static const char* DefaultPassVertexShader[] = {
265 | "uniform mat4 u_biasedShadowMvpMatrix;\n" // (*) Actually it's already multiplied with vMatrixInverse (in C code, so that the multiplication can be easily done with doubles)
266 | "varying vec4 v_shadowCoord;\n"
267 | "varying vec4 v_diffuse;\n"
268 | "\n"
269 | "void main() {\n"
270 | " gl_Position = ftransform();\n"
271 | "\n"
272 | " vec3 normal = gl_NormalMatrix * gl_Normal;\n"
273 | " vec3 lightVector = gl_LightSource[0].position.xyz\n;// - gl_Vertex.xyz;\n"
274 | " float nxDir = max(0.0, dot(normal, lightVector));\n"
275 | " v_diffuse = gl_LightSource[0].diffuse * nxDir; \n"
276 | "\n"
277 | " gl_FrontColor = gl_Color;\n"
278 | "\n"
279 | " v_shadowCoord = u_biasedShadowMvpMatrix*(gl_ModelViewMatrix*gl_Vertex);\n" // (*) We don't pass a 'naked' mMatrix in shaders (not robust to double precision usage). We dress it in a mvMatrix. So here we're passing a mMatrix from camera space to light space (through a mvMatrix).
280 | "}\n" // (the bias just converts clip space to texture space)
281 | };
282 | static const char* DefaultPassFragmentShader[] = {
283 | "uniform sampler2D u_shadowMap;\n"
284 | "uniform vec2 u_shadowDarkening;\n" // .x = fDarkeningFactor [10.0-80.0], .y = min value clamp [0.0-1.0]
285 | "varying vec4 v_shadowCoord;\n"
286 | "varying vec4 v_diffuse;\n"
287 | "\n"
288 | "void main() {\n"
289 | " float shadowFactor = 1.0;\n"
290 | " vec4 shadowCoordinateWdivide = v_shadowCoord/v_shadowCoord.w;\n"
291 | " shadowFactor = clamp(exp(u_shadowDarkening.x*(texture2D(u_shadowMap,(shadowCoordinateWdivide.st)).r - shadowCoordinateWdivide.z)),u_shadowDarkening.y,1.0);\n"
292 | "// shadowFactor = clamp( exp(u_shadowDarkening.x*texture2D(u_shadowMap,(shadowCoordinateWdivide.st)).r) * exp(-u_shadowDarkening.x*shadowCoordinateWdivide.z),u_shadowDarkening.y,1.0);\n"
293 | " gl_FragColor = gl_LightSource[0].ambient + (v_diffuse * vec4(gl_Color.rgb*shadowFactor,1.0));\n"
294 | "}\n"
295 | };
296 | typedef struct {
297 | GLuint program;
298 | GLint uniform_location_biasedShadowMvpMatrix;
299 | GLint uniform_location_shadowMap;
300 | GLint uniform_location_shadowDarkening;
301 | } DefaultPass;
302 |
303 | DefaultPass defaultPass;
304 | void InitDefaultPass(DefaultPass* dp) {
305 | dp->program = Helper_LoadShaderProgramFromSource(*DefaultPassVertexShader,*DefaultPassFragmentShader);
306 | dp->uniform_location_biasedShadowMvpMatrix = glGetUniformLocation(dp->program,"u_biasedShadowMvpMatrix");
307 | dp->uniform_location_shadowMap = glGetUniformLocation(dp->program,"u_shadowMap");
308 | dp->uniform_location_shadowDarkening = glGetUniformLocation(dp->program,"u_shadowDarkening");
309 |
310 | glUseProgram(dp->program);
311 | glUniform1i(dp->uniform_location_shadowMap,0);
312 | glUniform2f(dp->uniform_location_shadowDarkening,80.0,0.45); // Default values are (40.0f,0.75f) in [0-80] and [0-1]
313 | //glUniformMatrix4fv(dp->uniform_location_biasedShadowMvpMatrix, 1 /*only setting 1 matrix*/, GL_FALSE /*transpose?*/, Matrix);
314 | glUseProgram(0);
315 | }
316 | void DestroyDefaultPass(DefaultPass* dp) {
317 | if (dp->program) {glDeleteProgram(dp->program);dp->program=0;}
318 | }
319 |
320 | float current_width=0,current_height=0,current_aspect_ratio=1; // Not sure when I've used these...
321 | void ResizeGL(int w,int h) {
322 | current_width = (float) w;
323 | current_height = (float) h;
324 | if (current_height!=0) current_aspect_ratio = current_width/current_height;
325 | if (h>0) {
326 | // We set our pMatrix
327 | if (!config.use_camera_ortho3d_projection_matrix)
328 | Helper_Perspective(pMatrix,pMatrixFovyDeg,(float)w/(float)h,pMatrixNearPlane,pMatrixFarPlane);
329 | else
330 | Helper_Ortho3D(pMatrix,cameraDistance,pMatrixFovyDeg,(float)w/(float)h,pMatrixNearPlane,pMatrixFarPlane);
331 |
332 | glMatrixMode(GL_PROJECTION);glLoadMatrixf(pMatrix);glMatrixMode(GL_MODELVIEW);
333 |
334 | # ifdef USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
335 | Helper_InvertMatrix(pMatrixInverse,pMatrix); // pMatrixInverse is used only when USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE is defined
336 | # endif //USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
337 | }
338 |
339 |
340 | if (w>0 && h>0 && !config.fullscreen_enabled) {
341 | // On exiting we'll like to save these data back
342 | config.windowed_width=w;
343 | config.windowed_height=h;
344 | }
345 |
346 | glViewport(0,0,w,h); // This is what people often call in ResizeGL()
347 |
348 | }
349 |
350 |
351 | void InitGL(void) {
352 |
353 | // These are important, but often overlooked OpenGL calls
354 | glEnable(GL_DEPTH_TEST);
355 | glEnable(GL_CULL_FACE);
356 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Otherwise transparent objects are not displayed correctly
357 | glClearColor(0.3f, 0.6f, 1.0f, 1.0f);
358 | glEnable(GL_TEXTURE_2D); // Only needed for ffp, when VISUALIZE_DEPTH_TEXTURE is defined
359 |
360 |
361 | // ffp stuff
362 | glEnable(GL_LIGHTING);
363 | glEnable(GL_LIGHT0);
364 | glEnable(GL_COLOR_MATERIAL);
365 | glEnable(GL_NORMALIZE);
366 |
367 | // New
368 | InitShadowPass(&shadowPass);
369 | InitDefaultPass(&defaultPass);
370 |
371 | // Please note that after InitGL(), this implementation calls ResizeGL(...,...).
372 | // If you copy/paste this code you can call it explicitly...
373 | }
374 |
375 | void DestroyGL() {
376 | // New
377 | DestroyShadowPass(&shadowPass);
378 | DestroyDefaultPass(&defaultPass);
379 | // 40 display lists are generated by Helper_GlutDrawGeometry(...) if pgDisplayListBase!=0
380 | if (pgDisplayListBase && *pgDisplayListBase) {glDeleteLists(*pgDisplayListBase,40);*pgDisplayListBase=0;}
381 | }
382 |
383 |
384 |
385 | void DrawGL(void)
386 | {
387 | // All the things about time are just used to display FPS (F2)
388 | // or to move objects around (NOT for shadow)
389 | static unsigned begin = 0;
390 | static unsigned cur_time = 0;
391 | unsigned elapsed_time,delta_time;
392 | float elapsedMs;float cosAlpha,sinAlpha; // used to move objects around
393 |
394 | // These two instead are necessary for shadow mapping
395 | static float vMatrixInverse[16]; // view Matrix inverse (it's the camera matrix).
396 | static float lvpMatrix[16]; // = light_pMatrix*light_vMatrix
397 |
398 | // Just some time stuff here
399 | if (begin==0) begin = glutGet(GLUT_ELAPSED_TIME);
400 | elapsed_time = glutGet(GLUT_ELAPSED_TIME) - begin;
401 | delta_time = elapsed_time - cur_time;
402 | instantFrameTime = (float)delta_time*0.001f;
403 | cur_time = elapsed_time;
404 |
405 | elapsedMs = (float)elapsed_time;
406 | cosAlpha = cos(elapsedMs*0.0005f);
407 | sinAlpha = sin(elapsedMs*0.00075f);
408 |
409 |
410 | // view Matrix
411 | Helper_LookAt(vMatrix,cameraPos[0],cameraPos[1],cameraPos[2],targetPos[0],targetPos[1],targetPos[2],0,1,0);
412 | glLoadMatrixf(vMatrix);
413 | glLightfv(GL_LIGHT0,GL_POSITION,lightDirection); // Important: the ffp must recalculate internally lightDirectionEyeSpace based on vMatrix [=> every frame]
414 |
415 | // view Matrix inverse (it's the camera matrix). Used twice below (and very important to keep in any case).
416 | Helper_InvertMatrixFast(vMatrixInverse,vMatrix); // We can use Helper_InvertMatrixFast(...) instead of Helper_InvertMatrix(...) here [No scaling inside and no projection matrix]
417 |
418 |
419 | // Draw to Shadow Map------------------------------------------------------------------------------------------
420 | {
421 | # ifndef USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
422 | Helper_GetLightViewProjectionMatrix(lvpMatrix,
423 | vMatrixInverse,pMatrixNearPlane,pMatrixFarPlane,pMatrixFovyDeg,current_aspect_ratio,
424 | lightDirection,1.0f/(float)SHADOW_MAP_RESOLUTION);
425 | # else //USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
426 | Helper_GetLightViewProjectionMatrixExtra(0,
427 | vMatrixInverse,pMatrixNearPlane,pMatrixFarPlane,pMatrixFovyDeg,current_aspect_ratio,config.use_camera_ortho3d_projection_matrix?cameraDistance:0, // in camera ortho3d mode, we can still pass zero as last arg here to avoid shadow-flickering when zooming in-out, at the expense of the further boost in shadow resolution that ortho mode can give us
428 | lightDirection,1.0f/(float)SHADOW_MAP_RESOLUTION,
429 | 0,0,
430 | pMatrixInverse, // Mandatory when we need to retrieve arguments that follow it
431 | 0,0,
432 | lvpMatrix); // Technically this was provided as an 'lvpMatrix for optimal frustum culling usage' in the 'Stable Shadow Mapping' case (but can be used to replace it too)
433 | # endif //USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
434 |
435 | // Draw to shadow map texture
436 | glMatrixMode(GL_PROJECTION);glPushMatrix();glLoadIdentity();glMatrixMode(GL_MODELVIEW); // We'll set the combined light view-projection matrix in GL_MODELVIEW (do you know that it's the same?)
437 | glBindFramebuffer(GL_FRAMEBUFFER, shadowPass.fbo);
438 | glViewport(0, 0, SHADOW_MAP_RESOLUTION,SHADOW_MAP_RESOLUTION);
439 | glClear(GL_DEPTH_BUFFER_BIT);
440 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
441 | glCullFace(GL_FRONT);
442 | glEnable(GL_DEPTH_CLAMP);
443 | glUseProgram(shadowPass.program); // we can just use glUseProgram(0) here
444 | glPushMatrix();glLoadMatrixf(lvpMatrix); // we load both (light) projection and view matrices here (it's the same after all)
445 | Helper_GlutDrawGeometry(elapsedMs,cosAlpha,sinAlpha,targetPos,pgDisplayListBase); // Done SHADOW_MAP_NUM_CASCADES times!
446 | glPopMatrix();
447 | glUseProgram(0);
448 | glDisable(GL_DEPTH_CLAMP);
449 | glCullFace(GL_BACK);
450 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
451 | glBindFramebuffer(GL_FRAMEBUFFER,0);
452 | glMatrixMode(GL_PROJECTION);glPopMatrix();glMatrixMode(GL_MODELVIEW);
453 |
454 | }
455 |
456 | // Draw world
457 | {
458 | // biasedShadowMvpMatrix is used only in the DefaultPass:
459 | static float bias[16] = {0.5,0,0,0, 0,0.5,0,0, 0,0,0.5,0, 0.5,0.5,0.5,1}; // Moving from unit cube in NDC [-1,1][-1,1][-1,1] to [0,1][0,1][0,1] (x and y are texCoords; z is the depth range, [0,1] by default in window coordinates)
460 | static float biasedShadowMvpMatrix[16]; // multiplied per vMatrixInverse
461 | Helper_MultMatrix(biasedShadowMvpMatrix,bias,lvpMatrix);
462 | Helper_MultMatrix(biasedShadowMvpMatrix,biasedShadowMvpMatrix,vMatrixInverse); // We do this, so that when in the vertex shader we multiply it with the camera mvMatrix, we get: biasedShadowMvpMatrix * mMatrix (using mMatrices directly in the shaders prevents the usage of double precision matrices: mvMatrices are good when converted to float to feed the shader, mMatrices are bad)
463 |
464 | // Draw to world
465 | glViewport(0, 0, current_width,current_height);
466 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
467 | glBindTexture(GL_TEXTURE_2D,shadowPass.textureId);
468 | glUseProgram(defaultPass.program);
469 | glUniformMatrix4fv(defaultPass.uniform_location_biasedShadowMvpMatrix, 1 /*only setting 1 matrix*/, GL_FALSE /*transpose?*/,biasedShadowMvpMatrix);
470 | Helper_GlutDrawGeometry(elapsedMs,cosAlpha,sinAlpha,targetPos,pgDisplayListBase); // Done SHADOW_MAP_NUM_CASCADES times!
471 | glUseProgram(0);
472 | glBindTexture(GL_TEXTURE_2D,0);
473 | }
474 |
475 |
476 | if (config.show_fps && instantFrameTime>0) {
477 | if ((elapsed_time/1000)%2==0) {
478 | printf("FPS=%1.0f\n",1.f/instantFrameTime);fflush(stdout);
479 | config.show_fps=0;
480 | }
481 | }
482 |
483 |
484 | # ifdef VISUALIZE_DEPTH_TEXTURE
485 | {
486 | glDisable(GL_DEPTH_TEST);
487 | glDisable(GL_CULL_FACE);
488 | glDepthMask(GL_FALSE);
489 |
490 | glMatrixMode(GL_PROJECTION);
491 | glPushMatrix();
492 | glLoadIdentity();
493 |
494 | glMatrixMode(GL_MODELVIEW);
495 | glPushMatrix();
496 | glLoadIdentity();
497 |
498 | glColor3f(1,1,1);
499 | glDisable(GL_LIGHTING);
500 | glEnable(GL_BLEND);
501 | glBindTexture(GL_TEXTURE_2D,shadowPass.textureId);
502 | glColor4f(1,1,1,0.9f);
503 | glBegin(GL_QUADS);
504 | glTexCoord2f(0,0);glVertex2f(-1, -1);
505 | glTexCoord2f(1,0);glVertex2f(-0.25*current_aspect_ratio, -1);
506 | glTexCoord2f(1,1);glVertex2f(-0.25*current_aspect_ratio, -0.25/current_aspect_ratio);
507 | glTexCoord2f(0,1);glVertex2f(-1, -0.25/current_aspect_ratio);
508 | glEnd();
509 | glBindTexture(GL_TEXTURE_2D,0);
510 | glDisable(GL_BLEND);
511 | glEnable(GL_LIGHTING);
512 |
513 | glPopMatrix();
514 | glMatrixMode(GL_PROJECTION);
515 | glPopMatrix();
516 | glMatrixMode(GL_MODELVIEW);
517 |
518 | glEnable(GL_DEPTH_TEST);
519 | glEnable(GL_CULL_FACE);
520 | glDepthMask(GL_TRUE);
521 | }
522 | # endif //VISUALIZE_DEPTH_TEXTURE
523 |
524 |
525 | }
526 |
527 | static void GlutDestroyWindow(void);
528 | static void GlutCreateWindow();
529 |
530 | void GlutCloseWindow(void) {Config_Save(&config,ConfigFileName);}
531 |
532 | void GlutNormalKeys(unsigned char key, int x, int y) {
533 | const int mod = glutGetModifiers();
534 | switch (key) {
535 | case 27: // esc key
536 | Config_Save(&config,ConfigFileName);
537 | GlutDestroyWindow();
538 | # ifdef __FREEGLUT_STD_H__
539 | glutLeaveMainLoop();
540 | # else
541 | exit(0);
542 | # endif
543 | break;
544 | case 13: // return key
545 | {
546 | if (mod&GLUT_ACTIVE_CTRL) {
547 | config.fullscreen_enabled = gameModeWindowId ? 0 : 1;
548 | GlutDestroyWindow();
549 | GlutCreateWindow();
550 | }
551 | }
552 | break;
553 | }
554 |
555 | }
556 |
557 | static void updateCameraPos() {
558 | const float distanceY = sin(cameraPitch)*cameraDistance;
559 | const float distanceXZ = cos(cameraPitch)*cameraDistance;
560 | cameraPos[0] = targetPos[0] + sin(cameraYaw)*distanceXZ;
561 | cameraPos[1] = targetPos[1] + distanceY;
562 | cameraPos[2] = targetPos[2] + cos(cameraYaw)*distanceXZ;
563 | }
564 |
565 | static void updateDirectionalLight() {
566 | const float distanceY = sin(lightPitch);
567 | const float distanceXZ = cos(lightPitch);
568 | lightDirection[0] = sin(lightYaw)*distanceXZ;
569 | lightDirection[1] = distanceY;
570 | lightDirection[2] = cos(lightYaw)*distanceXZ;
571 | Helper_Vector3Normalize(lightDirection);
572 | lightDirection[3]=0.f;
573 | }
574 |
575 | static void resetCamera() {
576 | // You can set the initial camera position here through:
577 | targetPos[0]=0; targetPos[1]=0; targetPos[2]=0; // The camera target point
578 | cameraYaw = 2*M_PI; // The camera rotation around the Y axis
579 | cameraPitch = M_PI*0.125f; // The camera rotation around the XZ plane
580 | cameraDistance = 5; // The distance between the camera position and the camera target point
581 |
582 | updateCameraPos();
583 | if (config.use_camera_ortho3d_projection_matrix) ResizeGL(current_width,current_height); // Needed because in Helper_Orho3D(...) cameraTargetDistance changes
584 | }
585 |
586 | static void resetLight() {
587 | lightYaw = M_PI*0.425f;
588 | lightPitch = M_PI*0.235f;
589 | updateDirectionalLight();
590 | }
591 |
592 | void GlutSpecialKeys(int key,int x,int y)
593 | {
594 | const int mod = glutGetModifiers();
595 | if (!(mod&GLUT_ACTIVE_CTRL) && !(mod&GLUT_ACTIVE_SHIFT)) {
596 | switch (key) {
597 | case GLUT_KEY_LEFT:
598 | case GLUT_KEY_RIGHT:
599 | cameraYaw+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_LEFT ? -4.0f : 4.0f);
600 | if (cameraYaw>M_PI) cameraYaw-=2*M_PI;
601 | else if (cameraYaw<=-M_PI) cameraYaw+=2*M_PI;
602 | updateCameraPos(); break;
603 | case GLUT_KEY_UP:
604 | case GLUT_KEY_DOWN:
605 | cameraPitch+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_UP ? 2.f : -2.f);
606 | if (cameraPitch>M_PI-0.001f) cameraPitch=M_PI-0.001f;
607 | else if (cameraPitch<-M_PI*0.05f) cameraPitch=-M_PI*0.05f;
608 | updateCameraPos();
609 | break;
610 | case GLUT_KEY_PAGE_UP:
611 | case GLUT_KEY_PAGE_DOWN:
612 | cameraDistance+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_PAGE_DOWN ? 25.0f : -25.0f);
613 | if (cameraDistance<1.f) cameraDistance=1.f;
614 | updateCameraPos();
615 | if (config.use_camera_ortho3d_projection_matrix) ResizeGL(current_width,current_height); // Needed because in Helper_Orho3D(...) cameraTargetDistance changes
616 | break;
617 | case GLUT_KEY_F2:
618 | config.show_fps = !config.show_fps;
619 | //printf("showFPS: %s.\n",config.show_fps?"ON":"OFF");fflush(stdout);
620 | break;
621 | case GLUT_KEY_F1:
622 | config.use_camera_ortho3d_projection_matrix = !config.use_camera_ortho3d_projection_matrix;
623 | //printf("camera ortho mode: %s.\n",config.use_camera_ortho3d_projection_matrix?"ON":"OFF");fflush(stdout);
624 | ResizeGL(current_width,current_height);
625 | break;
626 | case GLUT_KEY_HOME:
627 | // Reset the camera
628 | resetCamera();
629 | break;
630 | }
631 | }
632 | else if (mod&GLUT_ACTIVE_CTRL) {
633 | switch (key) {
634 | case GLUT_KEY_LEFT:
635 | case GLUT_KEY_RIGHT:
636 | case GLUT_KEY_UP:
637 | case GLUT_KEY_DOWN:
638 | {
639 | // Here we move targetPos and cameraPos at the same time
640 |
641 | // We must find a pivot relative to the camera here (ignoring Y)
642 | float forward[3] = {targetPos[0]-cameraPos[0],0,targetPos[2]-cameraPos[2]};
643 | float up[3] = {0,1,0};
644 | float left[3];
645 |
646 | Helper_Vector3Normalize(forward);
647 | Helper_Vector3Cross(left,up,forward);
648 | {
649 | float delta[3] = {0,0,0};int i;
650 | if (key==GLUT_KEY_LEFT || key==GLUT_KEY_RIGHT) {
651 | float amount = instantFrameTime*cameraSpeed*(key==GLUT_KEY_RIGHT ? -25.0f : 25.0f);
652 | for (i=0;i<3;i++) delta[i]+=amount*left[i];
653 | }
654 | else {
655 | float amount = instantFrameTime*cameraSpeed*(key==GLUT_KEY_DOWN ? -25.0f : 25.0f);
656 | for ( i=0;i<3;i++) delta[i]+=amount*forward[i];
657 | }
658 | for ( i=0;i<3;i++) {
659 | targetPos[i]+=delta[i];
660 | cameraPos[i]+=delta[i];
661 | }
662 | }
663 | }
664 | break;
665 | case GLUT_KEY_PAGE_UP:
666 | case GLUT_KEY_PAGE_DOWN:
667 | // We use world space coords here.
668 | targetPos[1]+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_PAGE_DOWN ? -25.0f : 25.0f);
669 | if (targetPos[1]<-50.f) targetPos[1]=-50.f;
670 | else if (targetPos[1]>500.f) targetPos[1]=500.f;
671 | updateCameraPos();
672 | if (config.use_camera_ortho3d_projection_matrix) ResizeGL(current_width,current_height); // Needed because in Helper_Orho3D(...) cameraTargetDistance changes
673 | break;
674 | }
675 | }
676 | else if (mod&GLUT_ACTIVE_SHIFT) {
677 | switch (key) {
678 | case GLUT_KEY_LEFT:
679 | case GLUT_KEY_RIGHT:
680 | lightYaw+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_LEFT ? -4.0f : 4.0f);
681 | if (lightYaw>M_PI) lightYaw-=2*M_PI;
682 | else if (lightYaw<=-M_PI) lightYaw+=2*M_PI;
683 | updateDirectionalLight();
684 | break;
685 | case GLUT_KEY_UP:
686 | case GLUT_KEY_DOWN:
687 | case GLUT_KEY_PAGE_UP:
688 | case GLUT_KEY_PAGE_DOWN:
689 | lightPitch+= instantFrameTime*cameraSpeed*( (key==GLUT_KEY_UP || key==GLUT_KEY_PAGE_UP) ? 2.f : -2.f);
690 | if (lightPitch>M_PI-0.001f) lightPitch=M_PI-0.001f;
691 | else if (lightPitch<-M_PI*0.05f) lightPitch=-M_PI*0.05f;
692 | updateDirectionalLight();
693 | break;
694 | case GLUT_KEY_HOME:
695 | // Reset the light
696 | resetLight();
697 | break;
698 | }
699 | }
700 | }
701 |
702 | void GlutMouse(int a,int b,int c,int d) {
703 |
704 | }
705 |
706 | // Note that we have used GlutFakeDrawGL() so that at startup
707 | // the calling order is: InitGL(),ResizeGL(...),DrawGL()
708 | // Also note that glutSwapBuffers() must NOT be called inside DrawGL()
709 | static void GlutDrawGL(void) {DrawGL();glutSwapBuffers();}
710 | static void GlutIdle(void) {glutPostRedisplay();}
711 | static void GlutFakeDrawGL(void) {glutDisplayFunc(GlutDrawGL);}
712 | void GlutDestroyWindow(void) {
713 | if (gameModeWindowId || windowId) {
714 | DestroyGL();
715 |
716 | if (gameModeWindowId) {
717 | glutLeaveGameMode();
718 | gameModeWindowId = 0;
719 | }
720 | if (windowId) {
721 | glutDestroyWindow(windowId);
722 | windowId=0;
723 | }
724 | }
725 | }
726 | void GlutCreateWindow() {
727 | GlutDestroyWindow();
728 | if (config.fullscreen_enabled) {
729 | char gms[16]="";
730 | if (config.fullscreen_width>0 && config.fullscreen_height>0) {
731 | sprintf(gms,"%dx%d:32",config.fullscreen_width,config.fullscreen_height);
732 | glutGameModeString(gms);
733 | if (glutGameModeGet (GLUT_GAME_MODE_POSSIBLE)) gameModeWindowId = glutEnterGameMode();
734 | else config.fullscreen_width=config.fullscreen_height=0;
735 | }
736 | if (gameModeWindowId==0) {
737 | const int screenWidth = glutGet(GLUT_SCREEN_WIDTH);
738 | const int screenHeight = glutGet(GLUT_SCREEN_HEIGHT);
739 | sprintf(gms,"%dx%d:32",screenWidth,screenHeight);
740 | glutGameModeString(gms);
741 | if (glutGameModeGet (GLUT_GAME_MODE_POSSIBLE)) gameModeWindowId = glutEnterGameMode();
742 | }
743 | }
744 | if (!gameModeWindowId) {
745 | char windowTitle[1024] = PROGRAM_NAME".c\t";
746 | # ifdef USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
747 | strcat(windowTitle,"[Unstable]\t");
748 | # endif //USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
749 | strcat(windowTitle,"("XSTR_MACRO(SHADOW_MAP_RESOLUTION)")");
750 | config.fullscreen_enabled = 0;
751 | glutInitWindowPosition(100,100);
752 | glutInitWindowSize(config.windowed_width,config.windowed_height);
753 | windowId = glutCreateWindow(windowTitle);
754 | }
755 |
756 | glutKeyboardFunc(GlutNormalKeys);
757 | glutSpecialFunc(GlutSpecialKeys);
758 | glutMouseFunc(GlutMouse);
759 | glutIdleFunc(GlutIdle);
760 | glutReshapeFunc(ResizeGL);
761 | glutDisplayFunc(GlutFakeDrawGL);
762 | # ifdef __FREEGLUT_STD_H__
763 | glutWMCloseFunc(GlutCloseWindow);
764 | # endif //__FREEGLUT_STD_H__
765 |
766 | #ifdef USE_GLEW
767 | {
768 | GLenum err = glewInit();
769 | if( GLEW_OK != err ) {
770 | fprintf(stderr, "Error initializing GLEW: %s\n", glewGetErrorString(err) );
771 | return;
772 | }
773 | }
774 | #endif //USE_GLEW
775 |
776 | InitGL();
777 |
778 | }
779 |
780 |
781 | int main(int argc, char** argv)
782 | {
783 |
784 | glutInit(&argc, argv);
785 | glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
786 | //glutInitContextFlags(GLUT_FORWARD_COMPATIBLE);
787 | #ifdef __FREEGLUT_STD_H__
788 | glutSetOption ( GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION ) ;
789 | #endif //__FREEGLUT_STD_H__
790 |
791 | Config_Init(&config);
792 | Config_Load(&config,ConfigFileName);
793 |
794 | GlutCreateWindow();
795 |
796 | //OpenGL info
797 | printf("\nGL Vendor: %s\n", glGetString( GL_VENDOR ));
798 | printf("GL Renderer : %s\n", glGetString( GL_RENDERER ));
799 | printf("GL Version (string) : %s\n", glGetString( GL_VERSION ));
800 | printf("GLSL Version : %s\n", glGetString( GL_SHADING_LANGUAGE_VERSION ));
801 | //printf("GL Extensions:\n%s\n",(char *) glGetString(GL_EXTENSIONS));
802 |
803 | printf("\nKEYS:\n");
804 | printf("AROW KEYS + PAGE_UP/PAGE_DOWN:\tmove camera (optionally with CTRL down)\n");
805 | printf("HOME KEY:\t\t\treset camera\n");
806 | printf("ARROW KEYS + SHIFT:\tmove directional light\n");
807 | printf("CTRL+RETURN:\t\ttoggle fullscreen on/off\n");
808 | printf("F2:\t\t\tdisplay FPS\n");
809 | printf("F1:\t\t\ttoggle camera ortho mode on and off\n");
810 | printf("\n");
811 |
812 | resetCamera(); // Mandatory
813 | resetLight(); // Mandatory
814 |
815 | glutMainLoop();
816 |
817 |
818 | return 0;
819 | }
820 |
821 | void DrawGeometry(float elapsedMs,float cosAlpha,float sinAlpha) {
822 | int i,j;
823 |
824 | // ground
825 | glColor3f(0.2,0.4,0.2);
826 | glPushMatrix();
827 |
828 | glTranslatef(0,-0.5,0);
829 |
830 | glPushMatrix();
831 | glScalef(11.f,0.5f,14.f);
832 | glutSolidCube(1.0);
833 | glPopMatrix();
834 |
835 | glPopMatrix();
836 |
837 | // sphere
838 | glColor3f(0.8,0.8,0);
839 | glPushMatrix();
840 |
841 | glTranslatef(-1+2.5*cosAlpha,0.25,-1+sinAlpha);
842 |
843 | glPushMatrix();
844 | glRotatef(-elapsedMs*0.05f,0,1,0);
845 | glScalef(1.f,1.f,1.f);
846 | glutSolidSphere(0.5,16,16);
847 | glPopMatrix();
848 |
849 | glPopMatrix();
850 |
851 | // tours
852 | glColor3f(0.4,0.4,0.8);
853 | glPushMatrix();
854 |
855 | glTranslatef(-0.5+2.5*cosAlpha,0.5,2-sinAlpha);
856 |
857 | glPushMatrix();
858 | glRotatef(elapsedMs*0.05f,0,1,0);
859 | glScalef(1.f,1.f,1.f);
860 | glutSolidTorus(0.25,0.5,16,16);
861 | glPopMatrix();
862 |
863 | glPopMatrix();
864 |
865 | // teapot
866 | glColor3f(0.4,0.2,0.0);
867 | glPushMatrix();
868 |
869 | glTranslatef(-0.4,0.1,-4);
870 |
871 | glPushMatrix();
872 | glRotatef(elapsedMs*0.1f,0,1,0);
873 | glScalef(1.f,1.f,1.f);
874 | glFrontFace(GL_CW);glutSolidTeapot(0.5);glFrontFace(GL_CCW);
875 | //glutSolidCube(0.75);
876 | glPopMatrix();
877 |
878 | glPopMatrix();
879 |
880 | // cube
881 | glColor3f(0.5,0.5,1.0);
882 | glPushMatrix();
883 | glTranslatef(-2,0.3,0.25);
884 | glScalef(0.5f,1.5f,0.5f);
885 | glutSolidCube(0.75);
886 | glPopMatrix();
887 |
888 |
889 | // columns
890 | glColor3f(0.35,0.35,0.35);
891 | for (j=0;j<2;j++) {
892 | for (i=0;i<=10;i++) {
893 | glPushMatrix();
894 |
895 | glTranslatef(4.75-j*2.0,2.7+0.31,-5.f+(float)i*1.0);
896 | glPushMatrix();
897 | glRotatef(90,1,0,0);
898 |
899 | glPushMatrix();
900 | glScalef(0.2f,0.2f,2.8f);
901 |
902 | // central part
903 | glutSolidCylinder(0.5,1.0,8,8);
904 |
905 | // higher part
906 | glutSolidCone(0.8f,0.1f,8,8);
907 | glTranslatef(0.f,0.f,-0.025f);
908 | glutSolidCylinder(0.8,0.025,8,8);
909 |
910 | // lower part
911 | glTranslatef(0.f,0.f,1.05);
912 | glFrontFace(GL_CW);glutSolidCone(0.8f,-0.1f,8,8);glFrontFace(GL_CCW);
913 | glTranslatef(0.f,0.f,0.0f);
914 | glutSolidCylinder(0.8,0.025,8,8);
915 |
916 | glPopMatrix();
917 |
918 |
919 | glPopMatrix();
920 |
921 | glPopMatrix();
922 | }
923 | }
924 |
925 |
926 | // column plane under roof
927 | glColor3f(0.8,0.8,0.8);
928 | glPushMatrix();
929 | glTranslatef(3.75,3.16,0.f);
930 | glScalef(2.5f,0.155f,10.75f);
931 | glutSolidCube(1.0);
932 | glPopMatrix();
933 |
934 | // column roof
935 | glColor3f(0.4,0.0,0.0);
936 | glPushMatrix();
937 | glTranslatef(3.75,3.02+0.48,-10.75*0.5);
938 | glScalef(0.825f,0.3f,1.f);
939 | glRotatef(90,0,0,1);
940 | glutSolidCylinder(1.75,10.75,3,3);
941 | glPopMatrix();
942 |
943 | // column base
944 | glColor3f(0.2,0.2,0.2);
945 | glPushMatrix();
946 |
947 | glTranslatef(3.75,-0.01f,0.f);
948 | glScalef(2.8f,0.155f,10.5f);
949 | glutSolidCube(1.0);
950 |
951 | glTranslatef(0,-1,0);
952 | glScalef(1.15f,1,1.05f);
953 | glutSolidCube(1.0);
954 |
955 | glPopMatrix();
956 |
957 | // camera target pos:
958 | // Center
959 | glColor3f(0,0,0);
960 | glPushMatrix();
961 | glTranslatef(targetPos[0],targetPos[1],targetPos[2]);
962 | glPushMatrix();
963 | glutSolidSphere(0.04,8,8);
964 |
965 | // X Axis
966 | glPushMatrix();
967 | glColor3f(1,0,0);
968 | glRotatef(90,0,1,0);
969 | glutSolidCylinder(0.04,0.25,8,8);
970 | glTranslatef(0,0,0.25);
971 | glutSolidCone(0.06,0.1,8,8);
972 | glPopMatrix();
973 |
974 | // Y Axis
975 | glPushMatrix();
976 | glColor3f(0,1,0);
977 | glRotatef(-90,1,0,0);
978 | glutSolidCylinder(0.04,0.25,8,8);
979 | glTranslatef(0,0,0.25);
980 | glutSolidCone(0.06,0.1,8,8);
981 | glPopMatrix();
982 |
983 | // Z Axis
984 | glPushMatrix();
985 | glColor3f(0,0,1);
986 | glutSolidCylinder(0.04,0.25,8,8);
987 | glTranslatef(0,0,0.25);
988 | glutSolidCone(0.06,0.1,8,8);
989 | glPopMatrix();
990 |
991 | glPopMatrix();
992 | glPopMatrix();
993 | // End camera target position
994 |
995 | //# define DBG_BIG_OCCLUDING_WALL
996 | # ifdef DBG_BIG_OCCLUDING_WALL
997 | // big occluding wall
998 | glColor3f(0.5,0.1,0.1);
999 | glPushMatrix();
1000 |
1001 | glTranslatef(10,0,0);
1002 |
1003 | glPushMatrix();
1004 | glScalef(0.25f,2.f*pMatrixFarPlane,2.f*pMatrixFarPlane);
1005 | glutSolidCube(1.0);
1006 | glPopMatrix();
1007 |
1008 | glPopMatrix();
1009 | # endif // DBG_BIG_OCCLUDING_WALL
1010 |
1011 | }
1012 |
1013 |
1014 |
1015 |
1016 |
1017 |
1018 |
1019 |
1020 |
1021 |
1022 |
--------------------------------------------------------------------------------
/shadow_mapping_cascade.c:
--------------------------------------------------------------------------------
1 | // https://github.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples
2 | /** License
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 | * SOFTWARE.
18 | */
19 |
20 | // DEPENDENCIES:
21 | /*
22 | -> glut or freeglut (the latter is recommended)
23 | -> glew (Windows only)
24 | */
25 |
26 | // HOW TO COMPILE:
27 | /*
28 | // LINUX:
29 | gcc -O2 -std=gnu89 -no-pie shadow_mapping_cascade.c -o shadow_mapping_cascade -I"../" -lglut -lGL -lX11 -lm
30 | // WINDOWS (here we use the static version of glew, and glut32.lib, that can be replaced by freeglut.lib):
31 | cl /O2 /MT /Tc shadow_mapping_cascade.c /D"GLEW_STATIC" /I"../" /link /out:shadow_mapping_cascade.exe glut32.lib glew32s.lib opengl32.lib gdi32.lib Shell32.lib comdlg32.lib user32.lib kernel32.lib
32 |
33 |
34 | // IN ADDITION:
35 | By default the source file assumes that every OpenGL-related header is in "GL/".
36 | But you can define in the command-line the correct paths you use in your system
37 | for glut.h, glew.h, etc. with something like:
38 | -DGLUT_PATH=\"Glut/glut.h\"
39 | -DGLEW_PATH=\"Glew/glew.h\"
40 | (this syntax works on Linux, don't know about Windows)
41 | */
42 |
43 | //#define USE_GLEW // By default it's only defined for Windows builds (but can be defined in Linux/Mac builds too)
44 |
45 |
46 | #define PROGRAM_NAME "shadow_mapping_cascade"
47 | #define VISUALIZE_DEPTH_TEXTURE
48 | //#define VISUALIZE_CASCADE_SPLITS
49 | #define SHADOW_MAP_HEIGHT 512 //SHADOW_MAP_WIDTH = SHADOW_MAP_NUM_CASCADES*SHADOW_MAP_HEIGHT
50 | #define SHADOW_MAP_NUM_CASCADES 4
51 | #define SHADOW_MAP_CASCADE_LAMBDA 0.7 // in [0=uniform splits,1=logarithmic splits] logarithmic splits put higher resolution near the camera
52 | #define SHADOW_MAP_CLAMP_MODE GL_CLAMP_TO_EDGE // GL_CLAMP or GL_CLAMP_TO_EDGE or GL_CLAMP_TO_BORDER
53 | // GL_CLAMP; // sampling outside of the shadow map gives always shadowed pixels
54 | // GL_CLAMP_TO_EDGE; // sampling outside of the shadow map can give shadowed or unshadowed pixels (it depends on the edge of the shadow map)
55 | // GL_CLAMP_TO_BORDER; // sampling outside of the shadow map gives always non-shadowed pixels (if we set the border color correctly)
56 | #define SHADOW_MAP_FILTER GL_LINEAR // GL_LINEAR or GL_NEAREST (GL_LINEAR is more useful with a sampler2DShadow, that cannot be used with esponential shadow mapping)
57 |
58 | //#define USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE // Better resolution, but shadow-swimming as the camera rotates (on static objects). Please see README.md about it.
59 |
60 | // Warning: This is one of the demos that supports switching from perspective camera view to ortho camera view [F1 key].
61 | // However please note that in ortho view the shadow mapping algorithm is currently ALWAYS a bit unstable (even if USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE is not defined).
62 | //
63 | // The reason is that I've tried just using the 'perspective' version of the shadow map algorithm and it does not work correctly for cascaded shadow maps (it worked well in non-cascaded shadow_mapping.c).
64 | // That's why Helper_GetLightViewProjectionMatrices(...) takes one more argument than Helper_GetLightViewProjectionMatrix(...): 'cameraTargetDistanceForOrtho3DModeOnly_or_zero'.
65 | //
66 | // Ideally it should be called 'cameraTargetDistanceForUnstableOrtho3DModeOnly_or_zero', but in the case of cascaded shadow maps if we set it to zero (= we use the same algo used in perspective mode for ortho mode)
67 | // it does not work. That's why ortho mode is currently always a bit 'unstable' for cascaded shadow maps (well, less unstable than defining USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE, that works as expected).
68 | //
69 | // Hope this point is clear enough...
70 |
71 |
72 | // These path definitions can be passed to the compiler command-line
73 | #ifndef GLUT_PATH
74 | # define GLUT_PATH "GL/glut.h" // Mandatory
75 | #endif //GLEW_PATH
76 | #ifndef FREEGLUT_EXT_PATH
77 | # define FREEGLUT_EXT_PATH "GL/freeglut_ext.h" // Optional (used only if glut.h comes from the freeglut library)
78 | #endif //GLEW_PATH
79 | #ifndef GLEW_PATH
80 | # define GLEW_PATH "GL/glew.h" // Mandatory for Windows only
81 | #endif //GLEW_PATH
82 |
83 | #ifdef _WIN32
84 | # include "windows.h"
85 | # define USE_GLEW
86 | #endif //_WIN32
87 |
88 | #ifdef USE_GLEW
89 | # include GLEW_PATH
90 | #else //USE_GLEW
91 | # define GL_GLEXT_PROTOTYPES
92 | #endif //USE_GLEW
93 |
94 | #include GLUT_PATH
95 | #ifdef __FREEGLUT_STD_H__
96 | # include FREEGLUT_EXT_PATH
97 | #endif //__FREEGLUT_STD_H__
98 |
99 | // These derived definitions can't be touched [XSTR_MACRO(...) is used to insert an 'integer definition' between double quotes]
100 | #define STR_MACRO(s) #s
101 | #define XSTR_MACRO(s) STR_MACRO(s)
102 | #define SHADOW_MAP_NUM_CASCADES_STRING XSTR_MACRO(SHADOW_MAP_NUM_CASCADES)
103 | #define SHADOW_MAP_WIDTH (SHADOW_MAP_HEIGHT*SHADOW_MAP_NUM_CASCADES) // Fixed
104 |
105 |
106 | #include "helper_functions.h" // please search this .c file for "Helper_":
107 | // only very few of its functions are used.
108 |
109 | #include
110 | #include
111 | #include
112 |
113 |
114 | // Config file handling: basically there's an .ini file next to the
115 | // exe that you can tweak. (it's just an extra)
116 | const char* ConfigFileName = PROGRAM_NAME".ini";
117 | typedef struct {
118 | int fullscreen_width,fullscreen_height;
119 | int windowed_width,windowed_height;
120 | int fullscreen_enabled;
121 | int show_fps;
122 | int use_camera_ortho3d_projection_matrix;
123 | } Config;
124 | void Config_Init(Config* c) {
125 | c->fullscreen_width=c->fullscreen_height=0;
126 | c->windowed_width=960;c->windowed_height=540;
127 | c->fullscreen_enabled=0;
128 | c->show_fps = 0;
129 | c->use_camera_ortho3d_projection_matrix = 0;
130 | }
131 | int Config_Load(Config* c,const char* filePath) {
132 | FILE* f = fopen(filePath, "rt");
133 | char ch='\0';char buf[256]="";
134 | size_t nread=0;
135 | int numParsedItem=0;
136 | if (!f) return -1;
137 | while ((ch = fgetc(f)) !=EOF) {
138 | buf[nread]=ch;
139 | nread++;
140 | if (nread>255) {
141 | nread=0;
142 | continue;
143 | }
144 | if (ch=='\n') {
145 | buf[nread]='\0';
146 | if (nread<2 || buf[0]=='[' || buf[0]=='#') {nread = 0;continue;}
147 | if (nread>2 && buf[0]=='/' && buf[1]=='/') {nread = 0;continue;}
148 | // Parse
149 | switch (numParsedItem) {
150 | case 0:
151 | sscanf(buf, "%d %d", &c->fullscreen_width,&c->fullscreen_height);
152 | break;
153 | case 1:
154 | sscanf(buf, "%d %d", &c->windowed_width,&c->windowed_height);
155 | break;
156 | case 2:
157 | sscanf(buf, "%d", &c->fullscreen_enabled);
158 | break;
159 | case 3:
160 | sscanf(buf, "%d", &c->show_fps);
161 | break;
162 | case 4:
163 | sscanf(buf, "%d", &c->use_camera_ortho3d_projection_matrix);
164 | break;
165 | }
166 | nread=0;
167 | ++numParsedItem;
168 | }
169 | }
170 | fclose(f);
171 | if (c->windowed_width<=0) c->windowed_width=720;
172 | if (c->windowed_height<=0) c->windowed_height=405;
173 | return 0;
174 | }
175 | int Config_Save(Config* c,const char* filePath) {
176 | FILE* f = fopen(filePath, "wt");
177 | if (!f) return -1;
178 | fprintf(f, "[Size In Fullscreen Mode (zero means desktop size)]\n%d %d\n",c->fullscreen_width,c->fullscreen_height);
179 | fprintf(f, "[Size In Windowed Mode]\n%d %d\n",c->windowed_width,c->windowed_height);
180 | fprintf(f, "[Fullscreen Enabled (0 or 1) (CTRL+RETURN)]\n%d\n", c->fullscreen_enabled);
181 | fprintf(f, "[Show FPS (0 or 1) (F2)]\n%d\n", c->show_fps);
182 | fprintf(f, "[Use camera ortho3d projection matrix (0 or 1) (F1)]\n%d\n", c->use_camera_ortho3d_projection_matrix);
183 | fprintf(f,"\n");
184 | fclose(f);
185 | return 0;
186 | }
187 |
188 | Config config;
189 |
190 | // glut has a special fullscreen GameMode that you can toggle with CTRL+RETURN (not in WebGL)
191 | int windowId = 0; // window Id when not in fullscreen mode
192 | int gameModeWindowId = 0; // window Id when in fullscreen mode
193 |
194 | // Now we can start with our program
195 |
196 | // camera data:
197 | float targetPos[3]; // please set it in resetCamera()
198 | float cameraYaw; // please set it in resetCamera()
199 | float cameraPitch; // please set it in resetCamera()
200 | float cameraDistance; // please set it in resetCamera()
201 | float cameraPos[3]; // Derived value (do not edit)
202 | float vMatrix[16]; // view matrix
203 | float cameraSpeed = 0.5f; // When moving it
204 |
205 | // light data
206 | float lightYaw = M_PI*0.425f,lightPitch = M_PI*0.235f; // must be copied to resetLight() too
207 | float lightDirection[4] = {0,1,0,0}; // Derived value (do not edit) [lightDirection[3]==0]
208 |
209 | // pMatrix data:
210 | float pMatrix[16]; // projection matrix
211 | const float pMatrixFovyDeg = 45.f;
212 | const float pMatrixNearPlane = 0.5f;
213 | const float pMatrixFarPlane = 20.0f;
214 |
215 | // we calculate these in ResizeGL(...)
216 | float gCascadeNearAndFarClippingPlanes[SHADOW_MAP_NUM_CASCADES+1]; // Array of the clipping planes of each cascade (gCascadeNearAndFarClippingPlanes[0]==pMatrixNearPlane and gCascadeNearAndFarClippingPlanes[SHADOW_MAP_NUM_CASCADES]==pMatrixFarPlane)
217 | #ifdef USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
218 | float gCascadePMatricesInv[16*SHADOW_MAP_NUM_CASCADES]; // One inverse pMatrix per cascade (calculated in ResizeGL(...))
219 | #endif //USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
220 |
221 |
222 | float instantFrameTime = 16.2f;
223 |
224 | // Optional (to speed up Helper_GlutDrawGeometry(...) a bit)
225 | GLuint gDisplayListBase = 0;GLuint* pgDisplayListBase = &gDisplayListBase; // Can be set to 0 as a fallback.
226 |
227 |
228 | static const char* ShadowPassVertexShader[] = {
229 | " void main() {\n"
230 | " gl_Position = ftransform();\n"
231 | " }\n"
232 | };
233 | static const char* ShadowPassFragmentShader[] = {
234 | " void main() {\n"
235 | " //gl_FragColor = gl_Color;\n"
236 | " }\n"
237 | };
238 | typedef struct {
239 | GLuint fbo;
240 | GLuint textureId;
241 | GLuint program;
242 | } ShadowPass;
243 |
244 | ShadowPass shadowPass;
245 | void InitShadowPass(ShadowPass* sp) {
246 | sp->program = Helper_LoadShaderProgramFromSource(*ShadowPassVertexShader,*ShadowPassFragmentShader);
247 |
248 | // create depth texture
249 | glGenTextures(1, &sp->textureId);
250 | glBindTexture(GL_TEXTURE_2D, sp->textureId);
251 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, SHADOW_MAP_FILTER);
252 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, SHADOW_MAP_FILTER);
253 | # ifndef __EMSCRIPTEN__
254 | glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_MAP_WIDTH, SHADOW_MAP_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
255 | # else //__EMSCRIPTEN__
256 | glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_MAP_WIDTH, SHADOW_MAP_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0);
257 | # undef SHADOW_MAP_CLAMP_MODE
258 | # define SHADOW_MAP_CLAMP_MODE GL_CLAMP_TO_EDGE
259 | # endif //__EMSCRIPTEN__
260 | if (SHADOW_MAP_CLAMP_MODE==GL_CLAMP_TO_BORDER) {
261 | const GLfloat border[] = {1.0f,1.0f,1.0f,0.0f };
262 | glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
263 | }
264 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, SHADOW_MAP_CLAMP_MODE );
265 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, SHADOW_MAP_CLAMP_MODE );
266 | glBindTexture(GL_TEXTURE_2D, 0);
267 |
268 | // create depth fbo
269 | glGenFramebuffers(1, &sp->fbo);
270 | glBindFramebuffer(GL_FRAMEBUFFER, sp->fbo);
271 | # ifndef __EMSCRIPTEN__
272 | glDrawBuffer(GL_NONE); // Instruct openGL that we won't bind a color texture with the currently bound FBO
273 | glReadBuffer(GL_NONE);
274 | # endif //__EMSCRIPTEN__
275 | glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,sp->textureId, 0);
276 | {
277 | //Does the GPU support current FBO configuration?
278 | GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
279 | if (status!=GL_FRAMEBUFFER_COMPLETE) printf("glCheckFramebufferStatus(...) FAILED for shadowPass.fbo.\n");
280 | }
281 | glBindFramebuffer(GL_FRAMEBUFFER, 0);
282 | }
283 | void DestroyShadowPass(ShadowPass* sp) {
284 | if (sp->program) {glDeleteProgram(sp->program);sp->program=0;}
285 | if (sp->fbo) {glDeleteBuffers(1,&sp->fbo);sp->fbo=0;}
286 | if (sp->textureId) {glDeleteTextures(1,&sp->textureId);}
287 | }
288 |
289 |
290 | static const char* DefaultPassVertexShader[] = {
291 | "varying vec4 v_diffuse;\n"
292 | "varying vec4 v_vertexModelViewSpace;\n"
293 | "varying float v_vertexModelViewSpaceDepth;\n" // A bit redundant (we can calculate it in the fragment shader using v_vertexModelViewSpace)
294 | "\n"
295 | "void main() {\n"
296 | " gl_Position = ftransform();\n"
297 | "\n"
298 | " vec3 normal = gl_NormalMatrix * gl_Normal;\n"
299 | " vec3 lightVector = gl_LightSource[0].position.xyz\n;// - gl_Vertex.xyz;\n"
300 | " float nxDir = max(0.0, dot(normal, lightVector));\n"
301 | " v_diffuse = gl_LightSource[0].diffuse * nxDir; \n"
302 | "\n"
303 | " gl_FrontColor = gl_Color;\n"
304 | "\n"
305 | " v_vertexModelViewSpace = gl_ModelViewMatrix*gl_Vertex;\n"
306 | " v_vertexModelViewSpaceDepth = -v_vertexModelViewSpace.z/v_vertexModelViewSpace.w;\n" // Negative, because camera looks in the -z axis (and u_cascadeNearAndFarClippingPlanes[...] are all positive quantities)
307 | "}\n"
308 | };
309 | static const char* DefaultPassFragmentShader[] = {
310 | # ifdef VISUALIZE_CASCADE_SPLITS
311 | "#version 120\n"
312 | "#extension GL_EXT_gpu_shader4 : enable\n"
313 | "#define NUM_CASCADES "SHADOW_MAP_NUM_CASCADES_STRING"\n"
314 | "const vec3 dbgSplitColors[4] = vec3[4](vec3(0.5,0.0,0.0),vec3(0.0,0.5,0.0),vec3(0.0,0.0,0.5),vec3(0.5,0.5,0.0));\n"
315 | # else //VISUALIZE_CASCADE_SPLITS
316 | "#define NUM_CASCADES "SHADOW_MAP_NUM_CASCADES_STRING"\n"
317 | # endif //VISUALIZE_CASCADE_SPLITS
318 | "\n"
319 | "uniform sampler2D u_shadowMap;\n"
320 | "uniform vec2 u_shadowDarkening;\n" // .x = fDarkeningFactor [10.0-80.0], .y = min value clamp [0.0-1.0]
321 | "uniform float u_cascadeNearAndFarClippingPlanes[NUM_CASCADES+1];\n"
322 | "uniform mat4 u_biasedShadowMvpMatrix[NUM_CASCADES];\n" // Actually they are: (u_biasedShadowMvpMatrix[NUM_CASCADES] * vMatrixInverseCamera) please see the code.
323 | "\n"
324 | "varying vec4 v_diffuse;\n"
325 | "varying vec4 v_vertexModelViewSpace;\n"
326 | "varying float v_vertexModelViewSpaceDepth;\n"
327 | "\n"
328 | "float CalcShadowFactor(int CascadeIndex, vec4 shadowCoord) {\n"
329 | " vec4 shadowCoordinateWdivide = shadowCoord/shadowCoord.w;\n"
330 | " shadowCoordinateWdivide.x+= float(CascadeIndex);shadowCoordinateWdivide.x/= float(NUM_CASCADES);\n"
331 | " return clamp(exp(u_shadowDarkening.x*(texture2D(u_shadowMap,(shadowCoordinateWdivide.st)).r - shadowCoordinateWdivide.z)),u_shadowDarkening.y,1.0);\n"
332 | " }\n"
333 | "\n"
334 | "void main() {\n"
335 | " // Figure out which cascade to sample from\n"
336 | " float cascadeIdxFloat = float(NUM_CASCADES-1);\n"
337 | " for(int i=1;iprogram = Helper_LoadShaderProgramFromSource(*DefaultPassVertexShader,*DefaultPassFragmentShader);
368 | dp->uniform_location_biasedShadowMvpMatrix = glGetUniformLocation(dp->program,"u_biasedShadowMvpMatrix");
369 | dp->uniform_location_shadowMap = glGetUniformLocation(dp->program,"u_shadowMap");
370 | dp->uniform_location_shadowDarkening = glGetUniformLocation(dp->program,"u_shadowDarkening");
371 | dp->uniform_location_cascadeNearAndFarClippingPlanes = glGetUniformLocation(dp->program,"u_cascadeNearAndFarClippingPlanes");
372 |
373 | glUseProgram(dp->program);
374 | glUniform1i(dp->uniform_location_shadowMap,0);
375 | glUniform2f(dp->uniform_location_shadowDarkening,80.0,0.45); // Default values are (40.0f,0.75f) in [0-80] and [0-1]
376 | //glUniformMatrix4fv(dp->uniform_location_biasedShadowMvpMatrix, SHADOW_MAP_NUM_CASCADES /*only setting 1 matrix*/, GL_FALSE /*transpose?*/, Matrix);
377 | //glUniform1fv(dp->uniform_location_cascadeNearAndFarClippingPlanes,SHADOW_MAP_NUM_CASCADES,gCascadeNearAndFarClippingPlanes);
378 | glUseProgram(0);
379 | }
380 | void DestroyDefaultPass(DefaultPass* dp) {
381 | if (dp->program) {glDeleteProgram(dp->program);dp->program=0;}
382 | }
383 |
384 | float current_width=0,current_height=0,current_aspect_ratio=1; // Not sure when I've used these...
385 | void ResizeGL(int w,int h) {
386 | current_width = (float) w;
387 | current_height = (float) h;
388 | if (current_height!=0) current_aspect_ratio = current_width/current_height;
389 | if (h>0) {
390 | // We set our pMatrix
391 | if (!config.use_camera_ortho3d_projection_matrix)
392 | Helper_Perspective(pMatrix,pMatrixFovyDeg,(float)w/(float)h,pMatrixNearPlane,pMatrixFarPlane);
393 | else
394 | Helper_Ortho3D(pMatrix,cameraDistance,pMatrixFovyDeg,(float)w/(float)h,pMatrixNearPlane,pMatrixFarPlane);
395 |
396 | glMatrixMode(GL_PROJECTION);glLoadMatrixf(pMatrix);glMatrixMode(GL_MODELVIEW);
397 |
398 | // Here we calculate the splits (gCascadeNearAndFarClippingPlanes must contain SHADOW_MAP_NUM_CASCADES+1 elements) based on lambda, and camera near and far planes:
399 | Helper_GetCascadeNearAndFarClippingPlaneArray(gCascadeNearAndFarClippingPlanes,SHADOW_MAP_NUM_CASCADES,SHADOW_MAP_CASCADE_LAMBDA,pMatrixNearPlane,pMatrixFarPlane);
400 | // Here we update the uniform 'u_cascadeNearAndFarClippingPlanes' in the default pass shader program
401 | glUseProgram(defaultPass.program);
402 | glUniform1fv(defaultPass.uniform_location_cascadeNearAndFarClippingPlanes, SHADOW_MAP_NUM_CASCADES,gCascadeNearAndFarClippingPlanes);
403 | glUseProgram(0);
404 |
405 | # ifdef USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
406 | // Here we fill float gCascadePMatricesInv[16*SHADOW_MAP_NUM_CASCADES]: (basically the inverse of all the camera projection matrices, one per split)
407 | {
408 | int i;
409 | for (i=0;i0 && h>0 && !config.fullscreen_enabled) {
423 | // On exiting we'll like to save these data back
424 | config.windowed_width=w;
425 | config.windowed_height=h;
426 | }
427 |
428 | glViewport(0,0,w,h); // This is what people often call in ResizeGL()
429 |
430 | }
431 |
432 |
433 | void InitGL(void) {
434 |
435 | // These are important, but often overlooked OpenGL calls
436 | glEnable(GL_DEPTH_TEST);
437 | glEnable(GL_CULL_FACE);
438 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Otherwise transparent objects are not displayed correctly
439 | glClearColor(0.3f, 0.6f, 1.0f, 1.0f);
440 | glEnable(GL_TEXTURE_2D); // Only needed for ffp, when VISUALIZE_DEPTH_TEXTURE is defined
441 |
442 |
443 | // ffp stuff
444 | glEnable(GL_LIGHTING);
445 | glEnable(GL_LIGHT0);
446 | glEnable(GL_COLOR_MATERIAL);
447 | glEnable(GL_NORMALIZE);
448 |
449 | // New
450 | InitShadowPass(&shadowPass);
451 | InitDefaultPass(&defaultPass);
452 |
453 | // Please note that after InitGL(), this implementation calls ResizeGL(...,...).
454 | // If you copy/paste this code you can call it explicitly...
455 | }
456 |
457 | void DestroyGL() {
458 | // New
459 | DestroyShadowPass(&shadowPass);
460 | DestroyDefaultPass(&defaultPass);
461 | // 40 display lists are generated by Helper_GlutDrawGeometry(...) if pgDisplayListBase!=0
462 | if (pgDisplayListBase && *pgDisplayListBase) {glDeleteLists(*pgDisplayListBase,40);*pgDisplayListBase=0;}
463 | }
464 |
465 |
466 | void DrawGL(void)
467 | {
468 | // We need to calculate the "instantFrameTime", because it's necessary to "dynamic_resolution.h"
469 | static unsigned begin = 0;
470 | static unsigned cur_time = 0;
471 | unsigned elapsed_time,delta_time;
472 |
473 | static float vMatrixInverse[16];
474 | static float lvpMatrices[SHADOW_MAP_NUM_CASCADES*16]; // = light_pMatrix*light_vMatrix
475 | static float biasedShadowMvpMatrices[SHADOW_MAP_NUM_CASCADES*16]; // multiplied per vMatrixInverse
476 |
477 | float elapsedMs;float cosAlpha,sinAlpha; // used to move objects around
478 |
479 | int i;
480 |
481 | if (begin==0) begin = glutGet(GLUT_ELAPSED_TIME);
482 | elapsed_time = glutGet(GLUT_ELAPSED_TIME) - begin;
483 | delta_time = elapsed_time - cur_time;
484 | instantFrameTime = (float)delta_time*0.001f;
485 | cur_time = elapsed_time;
486 |
487 | elapsedMs = (float)elapsed_time;
488 | cosAlpha = cos(elapsedMs*0.0005f);
489 | sinAlpha = sin(elapsedMs*0.00075f);
490 |
491 | // view Matrix
492 | Helper_LookAt(vMatrix,cameraPos[0],cameraPos[1],cameraPos[2],targetPos[0],targetPos[1],targetPos[2],0,1,0);
493 | glLoadMatrixf(vMatrix);
494 | glLightfv(GL_LIGHT0,GL_POSITION,lightDirection); // Important: the ffp must recalculate internally lightDirectionEyeSpace based on vMatrix [=> every frame]
495 |
496 | // view Matrix inverse (it's the camera matrix). Used twice below. So it's better to keep it here.
497 | Helper_InvertMatrixFast(vMatrixInverse,vMatrix);
498 |
499 |
500 | // Draw to Shadow Map------------------------------------------------------------------------------------------
501 | {
502 | # ifndef USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
503 | Helper_GetLightViewProjectionMatrices(lvpMatrices,gCascadeNearAndFarClippingPlanes,SHADOW_MAP_NUM_CASCADES,
504 | vMatrixInverse,
505 | pMatrixFovyDeg,current_aspect_ratio,config.use_camera_ortho3d_projection_matrix?cameraDistance:0, // Last arg is not present in the non-cascaded equivalent function (please read the Warning at the top of the file)
506 | lightDirection,1.0f/(float)SHADOW_MAP_HEIGHT
507 | //,0,0//,vMatrix
508 | );
509 | # else //USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
510 | Helper_GetLightViewProjectionMatricesExtra(0,gCascadeNearAndFarClippingPlanes,SHADOW_MAP_NUM_CASCADES,
511 | vMatrixInverse,
512 | pMatrixFovyDeg,current_aspect_ratio,config.use_camera_ortho3d_projection_matrix?cameraDistance:0,
513 | lightDirection,1.0f/(float)SHADOW_MAP_HEIGHT,
514 | 0,0,
515 | gCascadePMatricesInv, // Mandatory when we need to retrieve arguments that follow it
516 | 0,0,
517 | lvpMatrices // Technically this was provided as an 'lvpMatrices for optimal frustum culling usage' argument to be used in the 'Stable Shadow Mapping' case (but can be used to replace it too)
518 | );
519 | # endif //USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
520 |
521 | // Draw to shadow map texture
522 | glMatrixMode(GL_PROJECTION);glPushMatrix();glLoadIdentity();glMatrixMode(GL_MODELVIEW); // We'll set the combined light view-projection matrix in GL_MODELVIEW (do you know that it's the same?)
523 | glBindFramebuffer(GL_FRAMEBUFFER, shadowPass.fbo);
524 | glViewport(0, 0, SHADOW_MAP_WIDTH,SHADOW_MAP_HEIGHT);
525 | glClear(GL_DEPTH_BUFFER_BIT); // Clears all the shadow map
526 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
527 | glCullFace(GL_FRONT);
528 | glEnable(GL_DEPTH_CLAMP);
529 | glUseProgram(shadowPass.program);
530 | for (i=0;i0) {
568 | if ((elapsed_time/1000)%2==0) {
569 | printf("FPS=%1.0f\n",1.f/instantFrameTime);fflush(stdout);
570 | config.show_fps=0;
571 | }
572 | }
573 |
574 |
575 | # ifdef VISUALIZE_DEPTH_TEXTURE
576 | {
577 | glDisable(GL_DEPTH_TEST);
578 | glDisable(GL_CULL_FACE);
579 | glDepthMask(GL_FALSE);
580 |
581 | glMatrixMode(GL_PROJECTION);
582 | glPushMatrix();
583 | glLoadIdentity();
584 |
585 | glMatrixMode(GL_MODELVIEW);
586 | glPushMatrix();
587 | glLoadIdentity();
588 |
589 | glColor3f(1,1,1);
590 | glDisable(GL_LIGHTING);
591 | glEnable(GL_BLEND);
592 | glBindTexture(GL_TEXTURE_2D,shadowPass.textureId);
593 | glColor4f(1,1,1,0.9f);
594 | glBegin(GL_QUADS);
595 | glTexCoord2f(0,0);glVertex2f(-1, 0.75);
596 | glTexCoord2f(1,0);glVertex2f(-1+0.25*(float)SHADOW_MAP_NUM_CASCADES/current_aspect_ratio, 0.75);
597 | glTexCoord2f(1,1);glVertex2f(-1+0.25*(float)SHADOW_MAP_NUM_CASCADES/current_aspect_ratio, 1.0);
598 | glTexCoord2f(0,1);glVertex2f(-1, 1.0);
599 | glEnd();
600 | glBindTexture(GL_TEXTURE_2D,0);
601 | glDisable(GL_BLEND);
602 | glEnable(GL_LIGHTING);
603 |
604 | glPopMatrix();
605 | glMatrixMode(GL_PROJECTION);
606 | glPopMatrix();
607 | glMatrixMode(GL_MODELVIEW);
608 |
609 | glEnable(GL_DEPTH_TEST);
610 | glEnable(GL_CULL_FACE);
611 | glDepthMask(GL_TRUE);
612 | }
613 | # endif //VISUALIZE_DEPTH_TEXTURE
614 |
615 |
616 | }
617 |
618 | static void GlutDestroyWindow(void);
619 | static void GlutCreateWindow();
620 |
621 | void GlutCloseWindow(void) {Config_Save(&config,ConfigFileName);}
622 |
623 | void GlutNormalKeys(unsigned char key, int x, int y) {
624 | const int mod = glutGetModifiers();
625 | switch (key) {
626 | case 27: // esc key
627 | Config_Save(&config,ConfigFileName);
628 | GlutDestroyWindow();
629 | # ifdef __FREEGLUT_STD_H__
630 | glutLeaveMainLoop();
631 | # else
632 | exit(0);
633 | # endif
634 | break;
635 | case 13: // return key
636 | {
637 | if (mod&GLUT_ACTIVE_CTRL) {
638 | config.fullscreen_enabled = gameModeWindowId ? 0 : 1;
639 | GlutDestroyWindow();
640 | GlutCreateWindow();
641 | }
642 | }
643 | break;
644 | }
645 |
646 | }
647 |
648 | static void updateCameraPos() {
649 | const float distanceY = sin(cameraPitch)*cameraDistance;
650 | const float distanceXZ = cos(cameraPitch)*cameraDistance;
651 | cameraPos[0] = targetPos[0] + sin(cameraYaw)*distanceXZ;
652 | cameraPos[1] = targetPos[1] + distanceY;
653 | cameraPos[2] = targetPos[2] + cos(cameraYaw)*distanceXZ;
654 | }
655 |
656 | static void updateDirectionalLight() {
657 | const float distanceY = sin(lightPitch);
658 | const float distanceXZ = cos(lightPitch);
659 | lightDirection[0] = sin(lightYaw)*distanceXZ;
660 | lightDirection[1] = distanceY;
661 | lightDirection[2] = cos(lightYaw)*distanceXZ;
662 | Helper_Vector3Normalize(lightDirection);
663 | lightDirection[3]=0.f;
664 | }
665 |
666 | static void resetCamera() {
667 | // You can set the initial camera position here through:
668 | targetPos[0]=0; targetPos[1]=0; targetPos[2]=0; // The camera target point
669 | cameraYaw = 2*M_PI; // The camera rotation around the Y axis
670 | cameraPitch = M_PI*0.125f; // The camera rotation around the XZ plane
671 | cameraDistance = 5; // The distance between the camera position and the camera target point
672 |
673 | updateCameraPos();
674 | if (config.use_camera_ortho3d_projection_matrix) ResizeGL(current_width,current_height); // Needed because in Helper_Orho3D(...) cameraTargetDistance changes
675 | }
676 |
677 | static void resetLight() {
678 | lightYaw = M_PI*0.425f;
679 | lightPitch = M_PI*0.235f;
680 | updateDirectionalLight();
681 | }
682 |
683 | void GlutSpecialKeys(int key,int x,int y)
684 | {
685 | const int mod = glutGetModifiers();
686 | if (!(mod&GLUT_ACTIVE_CTRL) && !(mod&GLUT_ACTIVE_SHIFT)) {
687 | switch (key) {
688 | case GLUT_KEY_LEFT:
689 | case GLUT_KEY_RIGHT:
690 | cameraYaw+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_LEFT ? -4.0f : 4.0f);
691 | if (cameraYaw>M_PI) cameraYaw-=2*M_PI;
692 | else if (cameraYaw<=-M_PI) cameraYaw+=2*M_PI;
693 | updateCameraPos(); break;
694 | case GLUT_KEY_UP:
695 | case GLUT_KEY_DOWN:
696 | cameraPitch+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_UP ? 2.f : -2.f);
697 | if (cameraPitch>M_PI-0.001f) cameraPitch=M_PI-0.001f;
698 | else if (cameraPitch<-M_PI*0.05f) cameraPitch=-M_PI*0.05f;
699 | updateCameraPos();
700 | break;
701 | case GLUT_KEY_PAGE_UP:
702 | case GLUT_KEY_PAGE_DOWN:
703 | cameraDistance+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_PAGE_DOWN ? 25.0f : -25.0f);
704 | if (cameraDistance<1.f) cameraDistance=1.f;
705 | updateCameraPos();
706 | if (config.use_camera_ortho3d_projection_matrix) ResizeGL(current_width,current_height); // Needed because in Helper_Orho3D(...) cameraTargetDistance changes
707 | break;
708 | case GLUT_KEY_F2:
709 | config.show_fps = !config.show_fps;
710 | //printf("showFPS: %s.\n",config.show_fps?"ON":"OFF");fflush(stdout);
711 | break;
712 | case GLUT_KEY_F1:
713 | config.use_camera_ortho3d_projection_matrix = !config.use_camera_ortho3d_projection_matrix;
714 | //printf("camera ortho mode: %s.\n",config.use_camera_ortho3d_projection_matrix?"ON":"OFF");fflush(stdout);
715 | ResizeGL(current_width,current_height);
716 | break;
717 | case GLUT_KEY_HOME:
718 | // Reset the camera
719 | resetCamera();
720 | break;
721 | }
722 | }
723 | else if (mod&GLUT_ACTIVE_CTRL) {
724 | switch (key) {
725 | case GLUT_KEY_LEFT:
726 | case GLUT_KEY_RIGHT:
727 | case GLUT_KEY_UP:
728 | case GLUT_KEY_DOWN:
729 | {
730 | // Here we move targetPos and cameraPos at the same time
731 |
732 | // We must find a pivot relative to the camera here (ignoring Y)
733 | float forward[3] = {targetPos[0]-cameraPos[0],0,targetPos[2]-cameraPos[2]};
734 | float up[3] = {0,1,0};
735 | float left[3];
736 |
737 | Helper_Vector3Normalize(forward);
738 | Helper_Vector3Cross(left,up,forward);
739 | {
740 | float delta[3] = {0,0,0};int i;
741 | if (key==GLUT_KEY_LEFT || key==GLUT_KEY_RIGHT) {
742 | float amount = instantFrameTime*cameraSpeed*(key==GLUT_KEY_RIGHT ? -25.0f : 25.0f);
743 | for (i=0;i<3;i++) delta[i]+=amount*left[i];
744 | }
745 | else {
746 | float amount = instantFrameTime*cameraSpeed*(key==GLUT_KEY_DOWN ? -25.0f : 25.0f);
747 | for ( i=0;i<3;i++) delta[i]+=amount*forward[i];
748 | }
749 | for ( i=0;i<3;i++) {
750 | targetPos[i]+=delta[i];
751 | cameraPos[i]+=delta[i];
752 | }
753 | }
754 | }
755 | break;
756 | case GLUT_KEY_PAGE_UP:
757 | case GLUT_KEY_PAGE_DOWN:
758 | // We use world space coords here.
759 | targetPos[1]+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_PAGE_DOWN ? -25.0f : 25.0f);
760 | if (targetPos[1]<-50.f) targetPos[1]=-50.f;
761 | else if (targetPos[1]>500.f) targetPos[1]=500.f;
762 | updateCameraPos();
763 | if (config.use_camera_ortho3d_projection_matrix) ResizeGL(current_width,current_height); // Needed because in Helper_Orho3D(...) cameraTargetDistance changes
764 | break;
765 | }
766 | }
767 | else if (mod&GLUT_ACTIVE_SHIFT) {
768 | switch (key) {
769 | case GLUT_KEY_LEFT:
770 | case GLUT_KEY_RIGHT:
771 | lightYaw+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_LEFT ? -4.0f : 4.0f);
772 | if (lightYaw>M_PI) lightYaw-=2*M_PI;
773 | else if (lightYaw<=-M_PI) lightYaw+=2*M_PI;
774 | updateDirectionalLight();
775 | break;
776 | case GLUT_KEY_UP:
777 | case GLUT_KEY_DOWN:
778 | case GLUT_KEY_PAGE_UP:
779 | case GLUT_KEY_PAGE_DOWN:
780 | lightPitch+= instantFrameTime*cameraSpeed*( (key==GLUT_KEY_UP || key==GLUT_KEY_PAGE_UP) ? 2.f : -2.f);
781 | if (lightPitch>M_PI-0.001f) lightPitch=M_PI-0.001f;
782 | else if (lightPitch<-M_PI*0.05f) lightPitch=-M_PI*0.05f;
783 | updateDirectionalLight();
784 | break;
785 | case GLUT_KEY_HOME:
786 | // Reset the light
787 | resetLight();
788 | break;
789 | }
790 | }
791 | }
792 |
793 | void GlutMouse(int a,int b,int c,int d) {
794 |
795 | }
796 |
797 | // Note that we have used GlutFakeDrawGL() so that at startup
798 | // the calling order is: InitGL(),ResizeGL(...),DrawGL()
799 | // Also note that glutSwapBuffers() must NOT be called inside DrawGL()
800 | static void GlutDrawGL(void) {DrawGL();glutSwapBuffers();}
801 | static void GlutIdle(void) {glutPostRedisplay();}
802 | static void GlutFakeDrawGL(void) {glutDisplayFunc(GlutDrawGL);}
803 | void GlutDestroyWindow(void) {
804 | if (gameModeWindowId || windowId) {
805 | DestroyGL();
806 |
807 | if (gameModeWindowId) {
808 | glutLeaveGameMode();
809 | gameModeWindowId = 0;
810 | }
811 | if (windowId) {
812 | glutDestroyWindow(windowId);
813 | windowId=0;
814 | }
815 | }
816 | }
817 | void GlutCreateWindow() {
818 | GlutDestroyWindow();
819 | if (config.fullscreen_enabled) {
820 | char gms[16]="";
821 | if (config.fullscreen_width>0 && config.fullscreen_height>0) {
822 | sprintf(gms,"%dx%d:32",config.fullscreen_width,config.fullscreen_height);
823 | glutGameModeString(gms);
824 | if (glutGameModeGet (GLUT_GAME_MODE_POSSIBLE)) gameModeWindowId = glutEnterGameMode();
825 | else config.fullscreen_width=config.fullscreen_height=0;
826 | }
827 | if (gameModeWindowId==0) {
828 | const int screenWidth = glutGet(GLUT_SCREEN_WIDTH);
829 | const int screenHeight = glutGet(GLUT_SCREEN_HEIGHT);
830 | sprintf(gms,"%dx%d:32",screenWidth,screenHeight);
831 | glutGameModeString(gms);
832 | if (glutGameModeGet (GLUT_GAME_MODE_POSSIBLE)) gameModeWindowId = glutEnterGameMode();
833 | }
834 | }
835 | if (!gameModeWindowId) {
836 | char windowTitle[1024] = PROGRAM_NAME".c\t";
837 | # ifdef USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
838 | strcat(windowTitle,"[Unstable]\t");
839 | # endif //USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
840 | strcat(windowTitle,"("XSTR_MACRO(SHADOW_MAP_HEIGHT)"x"SHADOW_MAP_NUM_CASCADES_STRING")");
841 | config.fullscreen_enabled = 0;
842 | glutInitWindowPosition(100,100);
843 | glutInitWindowSize(config.windowed_width,config.windowed_height);
844 | windowId = glutCreateWindow(windowTitle);
845 | }
846 |
847 | glutKeyboardFunc(GlutNormalKeys);
848 | glutSpecialFunc(GlutSpecialKeys);
849 | glutMouseFunc(GlutMouse);
850 | glutIdleFunc(GlutIdle);
851 | glutReshapeFunc(ResizeGL);
852 | glutDisplayFunc(GlutFakeDrawGL);
853 | # ifdef __FREEGLUT_STD_H__
854 | glutWMCloseFunc(GlutCloseWindow);
855 | # endif //__FREEGLUT_STD_H__
856 |
857 | #ifdef USE_GLEW
858 | {
859 | GLenum err = glewInit();
860 | if( GLEW_OK != err ) {
861 | fprintf(stderr, "Error initializing GLEW: %s\n", glewGetErrorString(err) );
862 | return;
863 | }
864 | }
865 | #endif //USE_GLEW
866 |
867 | InitGL();
868 |
869 | }
870 |
871 |
872 | int main(int argc, char** argv)
873 | {
874 | glutInit(&argc, argv);
875 | glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
876 | //glutInitContextFlags(GLUT_FORWARD_COMPATIBLE);
877 | #ifdef __FREEGLUT_STD_H__
878 | glutSetOption ( GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION ) ;
879 | #endif //__FREEGLUT_STD_H__
880 |
881 | Config_Init(&config);
882 | Config_Load(&config,ConfigFileName);
883 |
884 | GlutCreateWindow();
885 |
886 | //OpenGL info
887 | printf("\nGL Vendor: %s\n", glGetString( GL_VENDOR ));
888 | printf("GL Renderer : %s\n", glGetString( GL_RENDERER ));
889 | printf("GL Version (string) : %s\n", glGetString( GL_VERSION ));
890 | printf("GLSL Version : %s\n", glGetString( GL_SHADING_LANGUAGE_VERSION ));
891 | //printf("GL Extensions:\n%s\n",(char *) glGetString(GL_EXTENSIONS));
892 | {int maxTextureSize=0;glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);printf("Max Texture Size: %d\n", maxTextureSize);}
893 | // Many GPUs return 16384 as their max size.
894 | // For 4 bytes per pixel: 16384*16384*4 bytes = 1 Gb
895 | // For 4 float per pixel: 16384*16384*4 floats (a floating point texture) = 4Gb.
896 | // Many GPUs don't even have 4Gb of memory
897 |
898 | printf("\nKEYS:\n");
899 | printf("AROW KEYS + PAGE_UP/PAGE_DOWN:\tmove camera (optionally with CTRL down)\n");
900 | printf("HOME KEY:\t\t\treset camera\n");
901 | printf("ARROW KEYS + SHIFT:\tmove directional light\n");
902 | printf("CTRL+RETURN:\t\ttoggle fullscreen on/off\n");
903 | printf("F2:\t\t\tdisplay FPS\n");
904 | printf("F1:\t\t\ttoggle camera ortho mode on and off\n");
905 | printf("\n");
906 |
907 | resetCamera(); // Mandatory
908 | resetLight(); // Mandatory
909 |
910 | glutMainLoop();
911 |
912 |
913 | return 0;
914 | }
915 |
916 |
917 |
918 |
919 |
920 |
921 |
922 |
923 |
924 |
925 |
--------------------------------------------------------------------------------
/shadow_mapping_cascade_horizontal.c:
--------------------------------------------------------------------------------
1 | // https://github.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples
2 | /** License
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 | * SOFTWARE.
18 | */
19 |
20 | // DEPENDENCIES:
21 | /*
22 | -> glut or freeglut (the latter is recommended)
23 | -> glew (Windows only)
24 | */
25 |
26 | // HOW TO COMPILE:
27 | /*
28 | // LINUX:
29 | gcc -O2 -std=gnu89 -no-pie shadow_mapping_cascade_horizontal.c -o shadow_mapping_cascade_horizontal -I"../" -lglut -lGL -lX11 -lm
30 | // WINDOWS (here we use the static version of glew, and glut32.lib, that can be replaced by freeglut.lib):
31 | cl /O2 /MT /Tc shadow_mapping_cascade_horizontal.c /D"GLEW_STATIC" /I"../" /link /out:shadow_mapping_cascade_horizontal.exe glut32.lib glew32s.lib opengl32.lib gdi32.lib Shell32.lib comdlg32.lib user32.lib kernel32.lib
32 |
33 |
34 | // IN ADDITION:
35 | By default the source file assumes that every OpenGL-related header is in "GL/".
36 | But you can define in the command-line the correct paths you use in your system
37 | for glut.h, glew.h, etc. with something like:
38 | -DGLUT_PATH=\"Glut/glut.h\"
39 | -DGLEW_PATH=\"Glew/glew.h\"
40 | (this syntax works on Linux, don't know about Windows)
41 | */
42 |
43 | //#define USE_GLEW // By default it's only defined for Windows builds (but can be defined in Linux/Mac builds too)
44 |
45 |
46 | #define PROGRAM_NAME "shadow_mapping_cascade_horizontal"
47 | #define VISUALIZE_DEPTH_TEXTURE
48 | //#define VISUALIZE_CASCADE_SPLITS
49 | #define SHADOW_MAP_HEIGHT 512 //SHADOW_MAP_WIDTH = 2*SHADOW_MAP_HEIGHT
50 |
51 | #define SHADOW_MAP_CLAMP_MODE GL_CLAMP_TO_EDGE // GL_CLAMP or GL_CLAMP_TO_EDGE or GL_CLAMP_TO_BORDER
52 | // GL_CLAMP; // sampling outside of the shadow map gives always shadowed pixels
53 | // GL_CLAMP_TO_EDGE; // sampling outside of the shadow map can give shadowed or unshadowed pixels (it depends on the edge of the shadow map)
54 | // GL_CLAMP_TO_BORDER; // sampling outside of the shadow map gives always non-shadowed pixels (if we set the border color correctly)
55 | #define SHADOW_MAP_FILTER GL_LINEAR // GL_LINEAR or GL_NEAREST (GL_LINEAR is more useful with a sampler2DShadow, that cannot be used with esponential shadow mapping)
56 |
57 |
58 | // These path definitions can be passed to the compiler command-line
59 | #ifndef GLUT_PATH
60 | # define GLUT_PATH "GL/glut.h" // Mandatory
61 | #endif //GLEW_PATH
62 | #ifndef FREEGLUT_EXT_PATH
63 | # define FREEGLUT_EXT_PATH "GL/freeglut_ext.h" // Optional (used only if glut.h comes from the freeglut library)
64 | #endif //GLEW_PATH
65 | #ifndef GLEW_PATH
66 | # define GLEW_PATH "GL/glew.h" // Mandatory for Windows only
67 | #endif //GLEW_PATH
68 |
69 | #ifdef _WIN32
70 | # include "windows.h"
71 | # define USE_GLEW
72 | #endif //_WIN32
73 |
74 | #ifdef USE_GLEW
75 | # include GLEW_PATH
76 | #else //USE_GLEW
77 | # define GL_GLEXT_PROTOTYPES
78 | #endif //USE_GLEW
79 |
80 | #include GLUT_PATH
81 | #ifdef __FREEGLUT_STD_H__
82 | # include FREEGLUT_EXT_PATH
83 | #endif //__FREEGLUT_STD_H__
84 |
85 | // These derived definitions can't be touched [XSTR_MACRO(...) is used to insert an 'integer definition' between double quotes]
86 | #define STR_MACRO(s) #s
87 | #define XSTR_MACRO(s) STR_MACRO(s)
88 | //#define SHADOW_MAP_NUM_CASCADES_STRING XSTR_MACRO(SHADOW_MAP_NUM_CASCADES)
89 | #define SHADOW_MAP_WIDTH (SHADOW_MAP_HEIGHT*2) // Fixed
90 |
91 |
92 | #include "helper_functions.h" // please search this .c file for "Helper_":
93 | // only very few of its functions are used.
94 |
95 | #include
96 | #include
97 | #include
98 |
99 |
100 | // Config file handling: basically there's an .ini file next to the
101 | // exe that you can tweak. (it's just an extra)
102 | const char* ConfigFileName = PROGRAM_NAME".ini";
103 | typedef struct {
104 | int fullscreen_width,fullscreen_height;
105 | int windowed_width,windowed_height;
106 | int fullscreen_enabled;
107 | int show_fps;
108 | int use_camera_ortho3d_projection_matrix;
109 | } Config;
110 | void Config_Init(Config* c) {
111 | c->fullscreen_width=c->fullscreen_height=0;
112 | c->windowed_width=960;c->windowed_height=540;
113 | c->fullscreen_enabled=0;
114 | c->show_fps = 0;
115 | c->use_camera_ortho3d_projection_matrix = 0;
116 | }
117 | int Config_Load(Config* c,const char* filePath) {
118 | FILE* f = fopen(filePath, "rt");
119 | char ch='\0';char buf[256]="";
120 | size_t nread=0;
121 | int numParsedItem=0;
122 | if (!f) return -1;
123 | while ((ch = fgetc(f)) !=EOF) {
124 | buf[nread]=ch;
125 | nread++;
126 | if (nread>255) {
127 | nread=0;
128 | continue;
129 | }
130 | if (ch=='\n') {
131 | buf[nread]='\0';
132 | if (nread<2 || buf[0]=='[' || buf[0]=='#') {nread = 0;continue;}
133 | if (nread>2 && buf[0]=='/' && buf[1]=='/') {nread = 0;continue;}
134 | // Parse
135 | switch (numParsedItem) {
136 | case 0:
137 | sscanf(buf, "%d %d", &c->fullscreen_width,&c->fullscreen_height);
138 | break;
139 | case 1:
140 | sscanf(buf, "%d %d", &c->windowed_width,&c->windowed_height);
141 | break;
142 | case 2:
143 | sscanf(buf, "%d", &c->fullscreen_enabled);
144 | break;
145 | case 3:
146 | sscanf(buf, "%d", &c->show_fps);
147 | break;
148 | case 4:
149 | sscanf(buf, "%d", &c->use_camera_ortho3d_projection_matrix);
150 | break;
151 | }
152 | nread=0;
153 | ++numParsedItem;
154 | }
155 | }
156 | fclose(f);
157 | if (c->windowed_width<=0) c->windowed_width=720;
158 | if (c->windowed_height<=0) c->windowed_height=405;
159 | return 0;
160 | }
161 | int Config_Save(Config* c,const char* filePath) {
162 | FILE* f = fopen(filePath, "wt");
163 | if (!f) return -1;
164 | fprintf(f, "[Size In Fullscreen Mode (zero means desktop size)]\n%d %d\n",c->fullscreen_width,c->fullscreen_height);
165 | fprintf(f, "[Size In Windowed Mode]\n%d %d\n",c->windowed_width,c->windowed_height);
166 | fprintf(f, "[Fullscreen Enabled (0 or 1) (CTRL+RETURN)]\n%d\n", c->fullscreen_enabled);
167 | fprintf(f, "[Show FPS (0 or 1) (F2)]\n%d\n", c->show_fps);
168 | fprintf(f, "[Use camera ortho3d projection matrix (0 or 1) (F1)]\n%d\n", c->use_camera_ortho3d_projection_matrix);
169 | fprintf(f,"\n");
170 | fclose(f);
171 | return 0;
172 | }
173 |
174 | Config config;
175 |
176 | // glut has a special fullscreen GameMode that you can toggle with CTRL+RETURN (not in WebGL)
177 | int windowId = 0; // window Id when not in fullscreen mode
178 | int gameModeWindowId = 0; // window Id when in fullscreen mode
179 |
180 | // Now we can start with our program
181 |
182 | // camera data:
183 | float targetPos[3]; // please set it in resetCamera()
184 | float cameraYaw; // please set it in resetCamera()
185 | float cameraPitch; // please set it in resetCamera()
186 | float cameraDistance; // please set it in resetCamera()
187 | float cameraPos[3]; // Derived value (do not edit)
188 | float vMatrix[16]; // view matrix
189 | float cameraSpeed = 0.5f; // When moving it
190 |
191 | // light data
192 | float lightYaw = M_PI*0.425f,lightPitch = M_PI*0.235f; // must be copied to resetLight() too
193 | float lightDirection[4] = {0,1,0,0}; // Derived value (do not edit) [lightDirection[3]==0]
194 |
195 | // pMatrix data:
196 | float pMatrix[16]; // projection matrix
197 | const float pMatrixFovyDeg = 80.f; // we have increased fov (it was 45), because horizontal splits are usually useful for large fovs.
198 | const float pMatrixNearPlane = 0.5f;
199 | const float pMatrixFarPlane = 20.0f;
200 |
201 |
202 | float instantFrameTime = 16.2f;
203 |
204 | // Optional (to speed up Helper_GlutDrawGeometry(...) a bit)
205 | GLuint gDisplayListBase = 0;GLuint* pgDisplayListBase = &gDisplayListBase; // Can be set to 0 as a fallback.
206 |
207 |
208 | static const char* ShadowPassVertexShader[] = {
209 | " void main() {\n"
210 | " gl_Position = ftransform();\n"
211 | " }\n"
212 | };
213 | static const char* ShadowPassFragmentShader[] = {
214 | " void main() {\n"
215 | " //gl_FragColor = gl_Color;\n"
216 | " }\n"
217 | };
218 | typedef struct {
219 | GLuint fbo;
220 | GLuint textureId;
221 | GLuint program;
222 | } ShadowPass;
223 |
224 | ShadowPass shadowPass;
225 | void InitShadowPass(ShadowPass* sp) {
226 | sp->program = Helper_LoadShaderProgramFromSource(*ShadowPassVertexShader,*ShadowPassFragmentShader);
227 |
228 | // create depth texture
229 | glGenTextures(1, &sp->textureId);
230 | glBindTexture(GL_TEXTURE_2D, sp->textureId);
231 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, SHADOW_MAP_FILTER);
232 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, SHADOW_MAP_FILTER);
233 | # ifndef __EMSCRIPTEN__
234 | glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_MAP_WIDTH, SHADOW_MAP_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
235 | # else //__EMSCRIPTEN__
236 | glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_MAP_WIDTH, SHADOW_MAP_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0);
237 | # undef SHADOW_MAP_CLAMP_MODE
238 | # define SHADOW_MAP_CLAMP_MODE GL_CLAMP_TO_EDGE
239 | # endif //__EMSCRIPTEN__
240 | if (SHADOW_MAP_CLAMP_MODE==GL_CLAMP_TO_BORDER) {
241 | const GLfloat border[] = {1.0f,1.0f,1.0f,0.0f };
242 | glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
243 | }
244 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, SHADOW_MAP_CLAMP_MODE );
245 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, SHADOW_MAP_CLAMP_MODE );
246 | glBindTexture(GL_TEXTURE_2D, 0);
247 |
248 | // create depth fbo
249 | glGenFramebuffers(1, &sp->fbo);
250 | glBindFramebuffer(GL_FRAMEBUFFER, sp->fbo);
251 | # ifndef __EMSCRIPTEN__
252 | glDrawBuffer(GL_NONE); // Instruct openGL that we won't bind a color texture with the currently bound FBO
253 | glReadBuffer(GL_NONE);
254 | # endif //__EMSCRIPTEN__
255 | glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,sp->textureId, 0);
256 | {
257 | //Does the GPU support current FBO configuration?
258 | GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
259 | if (status!=GL_FRAMEBUFFER_COMPLETE) printf("glCheckFramebufferStatus(...) FAILED for shadowPass.fbo.\n");
260 | }
261 | glBindFramebuffer(GL_FRAMEBUFFER, 0);
262 | }
263 | void DestroyShadowPass(ShadowPass* sp) {
264 | if (sp->program) {glDeleteProgram(sp->program);sp->program=0;}
265 | if (sp->fbo) {glDeleteBuffers(1,&sp->fbo);sp->fbo=0;}
266 | if (sp->textureId) {glDeleteTextures(1,&sp->textureId);}
267 | }
268 |
269 |
270 | static const char* DefaultPassVertexShader[] = {
271 | "varying vec4 v_diffuse;\n"
272 | "varying vec4 v_vertexModelViewSpace;\n"
273 | "varying float v_vertexModelViewSpaceX;\n" // A bit redundant (we can calculate it in the fragment shader using v_vertexModelViewSpace)
274 | "\n"
275 | "void main() {\n"
276 | " gl_Position = ftransform();\n"
277 | "\n"
278 | " vec3 normal = gl_NormalMatrix * gl_Normal;\n"
279 | " vec3 lightVector = gl_LightSource[0].position.xyz\n;// - gl_Vertex.xyz;\n"
280 | " float nxDir = max(0.0, dot(normal, lightVector));\n"
281 | " v_diffuse = gl_LightSource[0].diffuse * nxDir; \n"
282 | "\n"
283 | " gl_FrontColor = gl_Color;\n"
284 | "\n"
285 | " v_vertexModelViewSpace = gl_ModelViewMatrix*gl_Vertex;\n"
286 | " v_vertexModelViewSpaceX = -v_vertexModelViewSpace.x;//-v_vertexModelViewSpace.x/v_vertexModelViewSpace.w;\n" // MUST BE NEGATIVE!
287 | "}\n"
288 | };
289 | static const char* DefaultPassFragmentShader[] = {
290 | # ifdef VISUALIZE_CASCADE_SPLITS
291 | "#version 120\n"
292 | "#extension GL_EXT_gpu_shader4 : enable\n"
293 | "const vec3 dbgSplitColors[4] = vec3[4](vec3(0.5,0.0,0.0),vec3(0.0,0.5,0.0),vec3(0.0,0.0,0.5),vec3(0.5,0.5,0.0));\n"
294 | # endif //VISUALIZE_CASCADE_SPLITS
295 | "\n"
296 | "uniform sampler2D u_shadowMap;\n"
297 | "uniform vec2 u_shadowDarkening;\n" // .x = fDarkeningFactor [10.0-80.0], .y = min value clamp [0.0-1.0]
298 | "uniform mat4 u_biasedShadowMvpMatrix[2];\n" // Actually they are: (u_biasedShadowMvpMatrix[2] * vMatrixInverseCamera) please see the code.
299 | "\n"
300 | "varying vec4 v_diffuse;\n"
301 | "varying vec4 v_vertexModelViewSpace;\n"
302 | "varying float v_vertexModelViewSpaceX;\n"
303 | "\n"
304 | "float CalcShadowFactor(int CascadeIndex, vec4 shadowCoord) {\n"
305 | " vec4 shadowCoordinateWdivide = shadowCoord/shadowCoord.w;\n"
306 | " shadowCoordinateWdivide.x+= float(CascadeIndex);shadowCoordinateWdivide.x/= 2.0;\n"
307 | " return clamp(exp(u_shadowDarkening.x*(texture2D(u_shadowMap,(shadowCoordinateWdivide.st)).r - shadowCoordinateWdivide.z)),u_shadowDarkening.y,1.0);\n"
308 | " }\n"
309 | "\n"
310 | "void main() {\n"
311 | " // Figure out which cascade to sample from\n"
312 | " float cascadeIdxFloat=max(sign(v_vertexModelViewSpaceX),0.0);\n" // branchless!
313 | "\n"
314 | " int cascadeIdx = int(cascadeIdxFloat);\n"
315 | " vec4 lightSpacePos = u_biasedShadowMvpMatrix[cascadeIdx]*v_vertexModelViewSpace;\n" // There's a hidden vMatrixInverseCamera multiplication that removes the view component, moving the mMatrix from the camera space to the light space
316 | " float shadowFactor = CalcShadowFactor(cascadeIdx, lightSpacePos);\n"
317 | "\n"
318 | # ifdef VISUALIZE_CASCADE_SPLITS
319 | " vec3 color = dbgSplitColors[cascadeIdx%4];\n"
320 | # else //VISUALIZE_CASCADE_SPLITS
321 | " vec3 color = gl_Color.rgb;\n"
322 | # endif //VISUALIZE_CASCADE_SPLITS
323 | "\n"
324 | " gl_FragColor = gl_LightSource[0].ambient + (v_diffuse * vec4(color*shadowFactor,1.0));\n"
325 | "}\n"
326 | };
327 |
328 |
329 | typedef struct {
330 | GLuint program;
331 | GLint uniform_location_biasedShadowMvpMatrix;
332 | GLint uniform_location_shadowMap;
333 | GLint uniform_location_shadowDarkening;
334 | } DefaultPass;
335 |
336 | DefaultPass defaultPass;
337 | void InitDefaultPass(DefaultPass* dp) {
338 | dp->program = Helper_LoadShaderProgramFromSource(*DefaultPassVertexShader,*DefaultPassFragmentShader);
339 | dp->uniform_location_biasedShadowMvpMatrix = glGetUniformLocation(dp->program,"u_biasedShadowMvpMatrix");
340 | dp->uniform_location_shadowMap = glGetUniformLocation(dp->program,"u_shadowMap");
341 | dp->uniform_location_shadowDarkening = glGetUniformLocation(dp->program,"u_shadowDarkening");
342 |
343 | glUseProgram(dp->program);
344 | glUniform1i(dp->uniform_location_shadowMap,0);
345 | glUniform2f(dp->uniform_location_shadowDarkening,80.0,0.45); // Default values are (40.0f,0.75f) in [0-80] and [0-1]
346 | //glUniformMatrix4fv(dp->uniform_location_biasedShadowMvpMatrix, 2 /*only setting 1 matrix*/, GL_FALSE /*transpose?*/, Matrix);
347 | glUseProgram(0);
348 | }
349 | void DestroyDefaultPass(DefaultPass* dp) {
350 | if (dp->program) {glDeleteProgram(dp->program);dp->program=0;}
351 | }
352 |
353 | float current_width=0,current_height=0,current_aspect_ratio=1; // Not sure when I've used these...
354 | void ResizeGL(int w,int h) {
355 | current_width = (float) w;
356 | current_height = (float) h;
357 | if (current_height!=0) current_aspect_ratio = current_width/current_height;
358 | if (h>0) {
359 | // We set our pMatrix
360 | if (!config.use_camera_ortho3d_projection_matrix)
361 | Helper_Perspective(pMatrix,pMatrixFovyDeg,(float)w/(float)h,pMatrixNearPlane,pMatrixFarPlane);
362 | else
363 | Helper_Ortho3D(pMatrix,cameraDistance,pMatrixFovyDeg,(float)w/(float)h,pMatrixNearPlane,pMatrixFarPlane);
364 |
365 | glMatrixMode(GL_PROJECTION);glLoadMatrixf(pMatrix);glMatrixMode(GL_MODELVIEW);
366 | }
367 |
368 |
369 | if (w>0 && h>0 && !config.fullscreen_enabled) {
370 | // On exiting we'll like to save these data back
371 | config.windowed_width=w;
372 | config.windowed_height=h;
373 | }
374 |
375 | glViewport(0,0,w,h); // This is what people often call in ResizeGL()
376 |
377 | }
378 |
379 |
380 | void InitGL(void) {
381 |
382 | // These are important, but often overlooked OpenGL calls
383 | glEnable(GL_DEPTH_TEST);
384 | glEnable(GL_CULL_FACE);
385 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Otherwise transparent objects are not displayed correctly
386 | glClearColor(0.3f, 0.6f, 1.0f, 1.0f);
387 | glEnable(GL_TEXTURE_2D); // Only needed for ffp, when VISUALIZE_DEPTH_TEXTURE is defined
388 |
389 |
390 | // ffp stuff
391 | glEnable(GL_LIGHTING);
392 | glEnable(GL_LIGHT0);
393 | glEnable(GL_COLOR_MATERIAL);
394 | glEnable(GL_NORMALIZE);
395 |
396 | // New
397 | InitShadowPass(&shadowPass);
398 | InitDefaultPass(&defaultPass);
399 |
400 | // Please note that after InitGL(), this implementation calls ResizeGL(...,...).
401 | // If you copy/paste this code you can call it explicitly...
402 | }
403 |
404 | void DestroyGL() {
405 | // New
406 | DestroyShadowPass(&shadowPass);
407 | DestroyDefaultPass(&defaultPass);
408 | // 40 display lists are generated by Helper_GlutDrawGeometry(...) if pgDisplayListBase!=0
409 | if (pgDisplayListBase && *pgDisplayListBase) {glDeleteLists(*pgDisplayListBase,40);*pgDisplayListBase=0;}
410 | }
411 |
412 |
413 | void DrawGL(void)
414 | {
415 | // We need to calculate the "instantFrameTime", because it's necessary to "dynamic_resolution.h"
416 | static unsigned begin = 0;
417 | static unsigned cur_time = 0;
418 | unsigned elapsed_time,delta_time;
419 |
420 | static float vMatrixInverse[16];
421 | static float lvpMatrices[2*16]; // = light_pMatrix*light_vMatrix
422 | static float biasedShadowMvpMatrices[2*16]; // multiplied per vMatrixInverse
423 |
424 | float elapsedMs;float cosAlpha,sinAlpha; // used to move objects around
425 |
426 | int i;
427 |
428 | if (begin==0) begin = glutGet(GLUT_ELAPSED_TIME);
429 | elapsed_time = glutGet(GLUT_ELAPSED_TIME) - begin;
430 | delta_time = elapsed_time - cur_time;
431 | instantFrameTime = (float)delta_time*0.001f;
432 | cur_time = elapsed_time;
433 |
434 | elapsedMs = (float)elapsed_time;
435 | cosAlpha = cos(elapsedMs*0.0005f);
436 | sinAlpha = sin(elapsedMs*0.00075f);
437 |
438 | // view Matrix
439 | Helper_LookAt(vMatrix,cameraPos[0],cameraPos[1],cameraPos[2],targetPos[0],targetPos[1],targetPos[2],0,1,0);
440 | glLoadMatrixf(vMatrix);
441 | glLightfv(GL_LIGHT0,GL_POSITION,lightDirection); // Important: the ffp must recalculate internally lightDirectionEyeSpace based on vMatrix [=> every frame]
442 |
443 | // view Matrix inverse (it's the camera matrix). Used twice below. So it's better to keep it here.
444 | Helper_InvertMatrixFast(vMatrixInverse,vMatrix);
445 |
446 |
447 | // Draw to Shadow Map------------------------------------------------------------------------------------------
448 | {
449 | // Experimental: it's currently wrong.
450 | Helper_GetLightViewProjectionMatricesHorizontal(&lvpMatrices[0],&lvpMatrices[16],pMatrixNearPlane,pMatrixFarPlane,
451 | vMatrixInverse,
452 | pMatrixFovyDeg,current_aspect_ratio,config.use_camera_ortho3d_projection_matrix?cameraDistance:0, // Last arg is not present in the non-cascaded equivalent function (please read the Warning at the top of the file)
453 | lightDirection,1.0f/(float)SHADOW_MAP_HEIGHT
454 | //,0,0//,vMatrix
455 | );
456 |
457 | // Draw to shadow map texture
458 | glMatrixMode(GL_PROJECTION);glPushMatrix();glLoadIdentity();glMatrixMode(GL_MODELVIEW); // We'll set the combined light view-projection matrix in GL_MODELVIEW (do you know that it's the same?)
459 | glBindFramebuffer(GL_FRAMEBUFFER, shadowPass.fbo);
460 | glViewport(0, 0, SHADOW_MAP_WIDTH,SHADOW_MAP_HEIGHT);
461 | glClear(GL_DEPTH_BUFFER_BIT); // Clears all the shadow map
462 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
463 | glCullFace(GL_FRONT);
464 | glEnable(GL_DEPTH_CLAMP);
465 | glUseProgram(shadowPass.program);
466 | for (i=0;i<2;i++) {
467 | glViewport(SHADOW_MAP_HEIGHT*i, 0, SHADOW_MAP_HEIGHT,SHADOW_MAP_HEIGHT);
468 | glPushMatrix();glLoadMatrixf(&lvpMatrices[i*16]); // we load both (light) projection and view matrices here (it's the same after all)
469 | Helper_GlutDrawGeometry(elapsedMs,cosAlpha,sinAlpha,targetPos,pgDisplayListBase); // Done 2 times!
470 | glPopMatrix();
471 | }
472 | glUseProgram(0);
473 | glDisable(GL_DEPTH_CLAMP);
474 | glCullFace(GL_BACK);
475 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
476 | glBindFramebuffer(GL_FRAMEBUFFER,0);
477 | glMatrixMode(GL_PROJECTION);glPopMatrix();glMatrixMode(GL_MODELVIEW);
478 |
479 | }
480 |
481 | // Draw world
482 | {
483 | // biasedShadowMvpMatrix is used only in the DefaultPass:
484 | static float bias[16] = {0.5,0,0,0, 0,0.5,0,0, 0,0,0.5,0, 0.5,0.5,0.5,1}; // Moving from unit cube [-1,1] to [0,1]
485 | for (i=0;i<2;i++) {
486 | Helper_MultMatrix(&biasedShadowMvpMatrices[i*16],bias,&lvpMatrices[i*16]);
487 | Helper_MultMatrix(&biasedShadowMvpMatrices[i*16],&biasedShadowMvpMatrices[i*16],vMatrixInverse); // We do this, so that when in the vs we multiply it with the camera mvMatrix, we get: biasedShadowMvpMatrix * mMatrix (using mMatrices directly in the shaders prevents the usage of double precision matrices: mvMatrices are good when converted to float to feed the shader, mMatrices are bad)
488 | }
489 |
490 | // Draw to world
491 | glViewport(0, 0, current_width,current_height);
492 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
493 | glBindTexture(GL_TEXTURE_2D,shadowPass.textureId);
494 | glUseProgram(defaultPass.program);
495 | glUniformMatrix4fv(defaultPass.uniform_location_biasedShadowMvpMatrix, 2 /*setting many matrices*/, GL_FALSE /*transpose?*/,biasedShadowMvpMatrices);
496 | Helper_GlutDrawGeometry(elapsedMs,cosAlpha,sinAlpha,targetPos,pgDisplayListBase);
497 | glUseProgram(0);
498 | glBindTexture(GL_TEXTURE_2D,0);
499 | }
500 |
501 |
502 |
503 | if (config.show_fps && instantFrameTime>0) {
504 | if ((elapsed_time/1000)%2==0) {
505 | printf("FPS=%1.0f\n",1.f/instantFrameTime);fflush(stdout);
506 | config.show_fps=0;
507 | }
508 | }
509 |
510 |
511 | # ifdef VISUALIZE_DEPTH_TEXTURE
512 | {
513 | glDisable(GL_DEPTH_TEST);
514 | glDisable(GL_CULL_FACE);
515 | glDepthMask(GL_FALSE);
516 |
517 | glMatrixMode(GL_PROJECTION);
518 | glPushMatrix();
519 | glLoadIdentity();
520 |
521 | glMatrixMode(GL_MODELVIEW);
522 | glPushMatrix();
523 | glLoadIdentity();
524 |
525 | glColor3f(1,1,1);
526 | glDisable(GL_LIGHTING);
527 | glEnable(GL_BLEND);
528 | glBindTexture(GL_TEXTURE_2D,shadowPass.textureId);
529 | glColor4f(1,1,1,0.9f);
530 | glBegin(GL_QUADS);
531 | glTexCoord2f(0,0);glVertex2f(-1, 0.75);
532 | glTexCoord2f(1,0);glVertex2f(-1+0.25*(float)2/current_aspect_ratio, 0.75);
533 | glTexCoord2f(1,1);glVertex2f(-1+0.25*(float)2/current_aspect_ratio, 1.0);
534 | glTexCoord2f(0,1);glVertex2f(-1, 1.0);
535 | glEnd();
536 | glBindTexture(GL_TEXTURE_2D,0);
537 | glDisable(GL_BLEND);
538 | glEnable(GL_LIGHTING);
539 |
540 | glPopMatrix();
541 | glMatrixMode(GL_PROJECTION);
542 | glPopMatrix();
543 | glMatrixMode(GL_MODELVIEW);
544 |
545 | glEnable(GL_DEPTH_TEST);
546 | glEnable(GL_CULL_FACE);
547 | glDepthMask(GL_TRUE);
548 | }
549 | # endif //VISUALIZE_DEPTH_TEXTURE
550 |
551 |
552 | }
553 |
554 | static void GlutDestroyWindow(void);
555 | static void GlutCreateWindow();
556 |
557 | void GlutCloseWindow(void) {Config_Save(&config,ConfigFileName);}
558 |
559 | void GlutNormalKeys(unsigned char key, int x, int y) {
560 | const int mod = glutGetModifiers();
561 | switch (key) {
562 | case 27: // esc key
563 | Config_Save(&config,ConfigFileName);
564 | GlutDestroyWindow();
565 | # ifdef __FREEGLUT_STD_H__
566 | glutLeaveMainLoop();
567 | # else
568 | exit(0);
569 | # endif
570 | break;
571 | case 13: // return key
572 | {
573 | if (mod&GLUT_ACTIVE_CTRL) {
574 | config.fullscreen_enabled = gameModeWindowId ? 0 : 1;
575 | GlutDestroyWindow();
576 | GlutCreateWindow();
577 | }
578 | }
579 | break;
580 | }
581 |
582 | }
583 |
584 | static void updateCameraPos() {
585 | const float distanceY = sin(cameraPitch)*cameraDistance;
586 | const float distanceXZ = cos(cameraPitch)*cameraDistance;
587 | cameraPos[0] = targetPos[0] + sin(cameraYaw)*distanceXZ;
588 | cameraPos[1] = targetPos[1] + distanceY;
589 | cameraPos[2] = targetPos[2] + cos(cameraYaw)*distanceXZ;
590 | }
591 |
592 | static void updateDirectionalLight() {
593 | const float distanceY = sin(lightPitch);
594 | const float distanceXZ = cos(lightPitch);
595 | lightDirection[0] = sin(lightYaw)*distanceXZ;
596 | lightDirection[1] = distanceY;
597 | lightDirection[2] = cos(lightYaw)*distanceXZ;
598 | Helper_Vector3Normalize(lightDirection);
599 | lightDirection[3]=0.f;
600 | }
601 |
602 | static void resetCamera() {
603 | // You can set the initial camera position here through:
604 | targetPos[0]=0; targetPos[1]=0; targetPos[2]=0; // The camera target point
605 | cameraYaw = 2*M_PI; // The camera rotation around the Y axis
606 | cameraPitch = M_PI*0.125f; // The camera rotation around the XZ plane
607 | cameraDistance = 5; // The distance between the camera position and the camera target point
608 |
609 | updateCameraPos();
610 | if (config.use_camera_ortho3d_projection_matrix) ResizeGL(current_width,current_height); // Needed because in Helper_Orho3D(...) cameraTargetDistance changes
611 | }
612 |
613 | static void resetLight() {
614 | lightYaw = M_PI*0.425f;
615 | lightPitch = M_PI*0.235f;
616 | updateDirectionalLight();
617 | }
618 |
619 | void GlutSpecialKeys(int key,int x,int y)
620 | {
621 | const int mod = glutGetModifiers();
622 | if (!(mod&GLUT_ACTIVE_CTRL) && !(mod&GLUT_ACTIVE_SHIFT)) {
623 | switch (key) {
624 | case GLUT_KEY_LEFT:
625 | case GLUT_KEY_RIGHT:
626 | cameraYaw+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_LEFT ? -4.0f : 4.0f);
627 | if (cameraYaw>M_PI) cameraYaw-=2*M_PI;
628 | else if (cameraYaw<=-M_PI) cameraYaw+=2*M_PI;
629 | updateCameraPos(); break;
630 | case GLUT_KEY_UP:
631 | case GLUT_KEY_DOWN:
632 | cameraPitch+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_UP ? 2.f : -2.f);
633 | if (cameraPitch>M_PI-0.001f) cameraPitch=M_PI-0.001f;
634 | else if (cameraPitch<-M_PI*0.05f) cameraPitch=-M_PI*0.05f;
635 | updateCameraPos();
636 | break;
637 | case GLUT_KEY_PAGE_UP:
638 | case GLUT_KEY_PAGE_DOWN:
639 | cameraDistance+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_PAGE_DOWN ? 25.0f : -25.0f);
640 | if (cameraDistance<1.f) cameraDistance=1.f;
641 | updateCameraPos();
642 | if (config.use_camera_ortho3d_projection_matrix) ResizeGL(current_width,current_height); // Needed because in Helper_Orho3D(...) cameraTargetDistance changes
643 | break;
644 | case GLUT_KEY_F2:
645 | config.show_fps = !config.show_fps;
646 | //printf("showFPS: %s.\n",config.show_fps?"ON":"OFF");fflush(stdout);
647 | break;
648 | case GLUT_KEY_F1:
649 | config.use_camera_ortho3d_projection_matrix = !config.use_camera_ortho3d_projection_matrix;
650 | //printf("camera ortho mode: %s.\n",config.use_camera_ortho3d_projection_matrix?"ON":"OFF");fflush(stdout);
651 | ResizeGL(current_width,current_height);
652 | break;
653 | case GLUT_KEY_HOME:
654 | // Reset the camera
655 | resetCamera();
656 | break;
657 | }
658 | }
659 | else if (mod&GLUT_ACTIVE_CTRL) {
660 | switch (key) {
661 | case GLUT_KEY_LEFT:
662 | case GLUT_KEY_RIGHT:
663 | case GLUT_KEY_UP:
664 | case GLUT_KEY_DOWN:
665 | {
666 | // Here we move targetPos and cameraPos at the same time
667 |
668 | // We must find a pivot relative to the camera here (ignoring Y)
669 | float forward[3] = {targetPos[0]-cameraPos[0],0,targetPos[2]-cameraPos[2]};
670 | float up[3] = {0,1,0};
671 | float left[3];
672 |
673 | Helper_Vector3Normalize(forward);
674 | Helper_Vector3Cross(left,up,forward);
675 | {
676 | float delta[3] = {0,0,0};int i;
677 | if (key==GLUT_KEY_LEFT || key==GLUT_KEY_RIGHT) {
678 | float amount = instantFrameTime*cameraSpeed*(key==GLUT_KEY_RIGHT ? -25.0f : 25.0f);
679 | for (i=0;i<3;i++) delta[i]+=amount*left[i];
680 | }
681 | else {
682 | float amount = instantFrameTime*cameraSpeed*(key==GLUT_KEY_DOWN ? -25.0f : 25.0f);
683 | for ( i=0;i<3;i++) delta[i]+=amount*forward[i];
684 | }
685 | for ( i=0;i<3;i++) {
686 | targetPos[i]+=delta[i];
687 | cameraPos[i]+=delta[i];
688 | }
689 | }
690 | }
691 | break;
692 | case GLUT_KEY_PAGE_UP:
693 | case GLUT_KEY_PAGE_DOWN:
694 | // We use world space coords here.
695 | targetPos[1]+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_PAGE_DOWN ? -25.0f : 25.0f);
696 | if (targetPos[1]<-50.f) targetPos[1]=-50.f;
697 | else if (targetPos[1]>500.f) targetPos[1]=500.f;
698 | updateCameraPos();
699 | if (config.use_camera_ortho3d_projection_matrix) ResizeGL(current_width,current_height); // Needed because in Helper_Orho3D(...) cameraTargetDistance changes
700 | break;
701 | }
702 | }
703 | else if (mod&GLUT_ACTIVE_SHIFT) {
704 | switch (key) {
705 | case GLUT_KEY_LEFT:
706 | case GLUT_KEY_RIGHT:
707 | lightYaw+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_LEFT ? -4.0f : 4.0f);
708 | if (lightYaw>M_PI) lightYaw-=2*M_PI;
709 | else if (lightYaw<=-M_PI) lightYaw+=2*M_PI;
710 | updateDirectionalLight();
711 | break;
712 | case GLUT_KEY_UP:
713 | case GLUT_KEY_DOWN:
714 | case GLUT_KEY_PAGE_UP:
715 | case GLUT_KEY_PAGE_DOWN:
716 | lightPitch+= instantFrameTime*cameraSpeed*( (key==GLUT_KEY_UP || key==GLUT_KEY_PAGE_UP) ? 2.f : -2.f);
717 | if (lightPitch>M_PI-0.001f) lightPitch=M_PI-0.001f;
718 | else if (lightPitch<-M_PI*0.05f) lightPitch=-M_PI*0.05f;
719 | updateDirectionalLight();
720 | break;
721 | case GLUT_KEY_HOME:
722 | // Reset the light
723 | resetLight();
724 | break;
725 | }
726 | }
727 | }
728 |
729 | void GlutMouse(int a,int b,int c,int d) {
730 |
731 | }
732 |
733 | // Note that we have used GlutFakeDrawGL() so that at startup
734 | // the calling order is: InitGL(),ResizeGL(...),DrawGL()
735 | // Also note that glutSwapBuffers() must NOT be called inside DrawGL()
736 | static void GlutDrawGL(void) {DrawGL();glutSwapBuffers();}
737 | static void GlutIdle(void) {glutPostRedisplay();}
738 | static void GlutFakeDrawGL(void) {glutDisplayFunc(GlutDrawGL);}
739 | void GlutDestroyWindow(void) {
740 | if (gameModeWindowId || windowId) {
741 | DestroyGL();
742 |
743 | if (gameModeWindowId) {
744 | glutLeaveGameMode();
745 | gameModeWindowId = 0;
746 | }
747 | if (windowId) {
748 | glutDestroyWindow(windowId);
749 | windowId=0;
750 | }
751 | }
752 | }
753 | void GlutCreateWindow() {
754 | GlutDestroyWindow();
755 | if (config.fullscreen_enabled) {
756 | char gms[16]="";
757 | if (config.fullscreen_width>0 && config.fullscreen_height>0) {
758 | sprintf(gms,"%dx%d:32",config.fullscreen_width,config.fullscreen_height);
759 | glutGameModeString(gms);
760 | if (glutGameModeGet (GLUT_GAME_MODE_POSSIBLE)) gameModeWindowId = glutEnterGameMode();
761 | else config.fullscreen_width=config.fullscreen_height=0;
762 | }
763 | if (gameModeWindowId==0) {
764 | const int screenWidth = glutGet(GLUT_SCREEN_WIDTH);
765 | const int screenHeight = glutGet(GLUT_SCREEN_HEIGHT);
766 | sprintf(gms,"%dx%d:32",screenWidth,screenHeight);
767 | glutGameModeString(gms);
768 | if (glutGameModeGet (GLUT_GAME_MODE_POSSIBLE)) gameModeWindowId = glutEnterGameMode();
769 | }
770 | }
771 | if (!gameModeWindowId) {
772 | char windowTitle[1024] = PROGRAM_NAME".c\t";
773 | /*
774 | # ifdef USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
775 | strcat(windowTitle,"[Unstable]\t");
776 | # endif //USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
777 | */
778 | strcat(windowTitle,"("XSTR_MACRO(SHADOW_MAP_HEIGHT)"x2)");
779 | config.fullscreen_enabled = 0;
780 | glutInitWindowPosition(100,100);
781 | glutInitWindowSize(config.windowed_width,config.windowed_height);
782 | windowId = glutCreateWindow(windowTitle);
783 | }
784 |
785 | glutKeyboardFunc(GlutNormalKeys);
786 | glutSpecialFunc(GlutSpecialKeys);
787 | glutMouseFunc(GlutMouse);
788 | glutIdleFunc(GlutIdle);
789 | glutReshapeFunc(ResizeGL);
790 | glutDisplayFunc(GlutFakeDrawGL);
791 | # ifdef __FREEGLUT_STD_H__
792 | glutWMCloseFunc(GlutCloseWindow);
793 | # endif //__FREEGLUT_STD_H__
794 |
795 | #ifdef USE_GLEW
796 | {
797 | GLenum err = glewInit();
798 | if( GLEW_OK != err ) {
799 | fprintf(stderr, "Error initializing GLEW: %s\n", glewGetErrorString(err) );
800 | return;
801 | }
802 | }
803 | #endif //USE_GLEW
804 |
805 | InitGL();
806 |
807 | }
808 |
809 |
810 | int main(int argc, char** argv)
811 | {
812 | glutInit(&argc, argv);
813 | glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
814 | //glutInitContextFlags(GLUT_FORWARD_COMPATIBLE);
815 | #ifdef __FREEGLUT_STD_H__
816 | glutSetOption ( GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION ) ;
817 | #endif //__FREEGLUT_STD_H__
818 |
819 | Config_Init(&config);
820 | Config_Load(&config,ConfigFileName);
821 |
822 | GlutCreateWindow();
823 |
824 | //OpenGL info
825 | printf("\nGL Vendor: %s\n", glGetString( GL_VENDOR ));
826 | printf("GL Renderer : %s\n", glGetString( GL_RENDERER ));
827 | printf("GL Version (string) : %s\n", glGetString( GL_VERSION ));
828 | printf("GLSL Version : %s\n", glGetString( GL_SHADING_LANGUAGE_VERSION ));
829 | //printf("GL Extensions:\n%s\n",(char *) glGetString(GL_EXTENSIONS));
830 | {int maxTextureSize=0;glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);printf("Max Texture Size: %d\n", maxTextureSize);}
831 | // Many GPUs return 16384 as their max size.
832 | // For 4 bytes per pixel: 16384*16384*4 bytes = 1 Gb
833 | // For 4 float per pixel: 16384*16384*4 floats (a floating point texture) = 4Gb.
834 | // Many GPUs don't even have 4Gb of memory
835 |
836 | printf("\nKEYS:\n");
837 | printf("AROW KEYS + PAGE_UP/PAGE_DOWN:\tmove camera (optionally with CTRL down)\n");
838 | printf("HOME KEY:\t\t\treset camera\n");
839 | printf("ARROW KEYS + SHIFT:\tmove directional light\n");
840 | printf("CTRL+RETURN:\t\ttoggle fullscreen on/off\n");
841 | printf("F2:\t\t\tdisplay FPS\n");
842 | printf("F1:\t\t\ttoggle camera ortho mode on and off\n");
843 | printf("\n");
844 |
845 | resetCamera(); // Mandatory
846 | resetLight(); // Mandatory
847 |
848 | glutMainLoop();
849 |
850 |
851 | return 0;
852 | }
853 |
854 |
855 |
856 |
857 |
858 |
859 |
860 |
861 |
862 |
863 |
--------------------------------------------------------------------------------
/shadow_mapping_cascade_horizontal_and_vertical.c:
--------------------------------------------------------------------------------
1 | // https://github.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples
2 | /** License
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 | * SOFTWARE.
18 | */
19 |
20 | // DEPENDENCIES:
21 | /*
22 | -> glut or freeglut (the latter is recommended)
23 | -> glew (Windows only)
24 | */
25 |
26 | // HOW TO COMPILE:
27 | /*
28 | // LINUX:
29 | gcc -O2 -std=gnu89 -no-pie shadow_mapping_cascade_horizontal_and_vertical.c -o shadow_mapping_cascade_horizontal_and_vertical -I"../" -lglut -lGL -lX11 -lm
30 | // WINDOWS (here we use the static version of glew, and glut32.lib, that can be replaced by freeglut.lib):
31 | cl /O2 /MT /Tc shadow_mapping_cascade_horizontal_and_vertical.c /D"GLEW_STATIC" /I"../" /link /out:shadow_mapping_cascade_horizontal_and_vertical.exe glut32.lib glew32s.lib opengl32.lib gdi32.lib Shell32.lib comdlg32.lib user32.lib kernel32.lib
32 |
33 |
34 | // IN ADDITION:
35 | By default the source file assumes that every OpenGL-related header is in "GL/".
36 | But you can define in the command-line the correct paths you use in your system
37 | for glut.h, glew.h, etc. with something like:
38 | -DGLUT_PATH=\"Glut/glut.h\"
39 | -DGLEW_PATH=\"Glew/glew.h\"
40 | (this syntax works on Linux, don't know about Windows)
41 | */
42 |
43 | //#define USE_GLEW // By default it's only defined for Windows builds (but can be defined in Linux/Mac builds too)
44 |
45 |
46 | #define PROGRAM_NAME "shadow_mapping_cascade_horizontal_and_vertical"
47 | #define VISUALIZE_DEPTH_TEXTURE
48 | #define VISUALIZE_CASCADE_SPLITS
49 | #define SHADOW_MAP_HEIGHT 512 //SHADOW_MAP_WIDTH = 4*SHADOW_MAP_HEIGHT
50 |
51 | #define SHADOW_MAP_CLAMP_MODE GL_CLAMP_TO_EDGE // GL_CLAMP or GL_CLAMP_TO_EDGE or GL_CLAMP_TO_BORDER
52 | // GL_CLAMP; // sampling outside of the shadow map gives always shadowed pixels
53 | // GL_CLAMP_TO_EDGE; // sampling outside of the shadow map can give shadowed or unshadowed pixels (it depends on the edge of the shadow map)
54 | // GL_CLAMP_TO_BORDER; // sampling outside of the shadow map gives always non-shadowed pixels (if we set the border color correctly)
55 | #define SHADOW_MAP_FILTER GL_LINEAR // GL_LINEAR or GL_NEAREST (GL_LINEAR is more useful with a sampler2DShadow, that cannot be used with esponential shadow mapping)
56 |
57 |
58 | // These path definitions can be passed to the compiler command-line
59 | #ifndef GLUT_PATH
60 | # define GLUT_PATH "GL/glut.h" // Mandatory
61 | #endif //GLEW_PATH
62 | #ifndef FREEGLUT_EXT_PATH
63 | # define FREEGLUT_EXT_PATH "GL/freeglut_ext.h" // Optional (used only if glut.h comes from the freeglut library)
64 | #endif //GLEW_PATH
65 | #ifndef GLEW_PATH
66 | # define GLEW_PATH "GL/glew.h" // Mandatory for Windows only
67 | #endif //GLEW_PATH
68 |
69 | #ifdef _WIN32
70 | # include "windows.h"
71 | # define USE_GLEW
72 | #endif //_WIN32
73 |
74 | #ifdef USE_GLEW
75 | # include GLEW_PATH
76 | #else //USE_GLEW
77 | # define GL_GLEXT_PROTOTYPES
78 | #endif //USE_GLEW
79 |
80 | #include GLUT_PATH
81 | #ifdef __FREEGLUT_STD_H__
82 | # include FREEGLUT_EXT_PATH
83 | #endif //__FREEGLUT_STD_H__
84 |
85 | // These derived definitions can't be touched [XSTR_MACRO(...) is used to insert an 'integer definition' between double quotes]
86 | #define STR_MACRO(s) #s
87 | #define XSTR_MACRO(s) STR_MACRO(s)
88 | //#define SHADOW_MAP_NUM_CASCADES_STRING XSTR_MACRO(SHADOW_MAP_NUM_CASCADES)
89 | #define SHADOW_MAP_WIDTH (SHADOW_MAP_HEIGHT*4) // Fixed
90 |
91 |
92 | #include "helper_functions.h" // please search this .c file for "Helper_":
93 | // only very few of its functions are used.
94 |
95 | #include
96 | #include
97 | #include
98 |
99 |
100 | // Config file handling: basically there's an .ini file next to the
101 | // exe that you can tweak. (it's just an extra)
102 | const char* ConfigFileName = PROGRAM_NAME".ini";
103 | typedef struct {
104 | int fullscreen_width,fullscreen_height;
105 | int windowed_width,windowed_height;
106 | int fullscreen_enabled;
107 | int show_fps;
108 | int use_camera_ortho3d_projection_matrix;
109 | } Config;
110 | void Config_Init(Config* c) {
111 | c->fullscreen_width=c->fullscreen_height=0;
112 | c->windowed_width=960;c->windowed_height=540;
113 | c->fullscreen_enabled=0;
114 | c->show_fps = 0;
115 | c->use_camera_ortho3d_projection_matrix = 0;
116 | }
117 | int Config_Load(Config* c,const char* filePath) {
118 | FILE* f = fopen(filePath, "rt");
119 | char ch='\0';char buf[256]="";
120 | size_t nread=0;
121 | int numParsedItem=0;
122 | if (!f) return -1;
123 | while ((ch = fgetc(f)) !=EOF) {
124 | buf[nread]=ch;
125 | nread++;
126 | if (nread>255) {
127 | nread=0;
128 | continue;
129 | }
130 | if (ch=='\n') {
131 | buf[nread]='\0';
132 | if (nread<2 || buf[0]=='[' || buf[0]=='#') {nread = 0;continue;}
133 | if (nread>2 && buf[0]=='/' && buf[1]=='/') {nread = 0;continue;}
134 | // Parse
135 | switch (numParsedItem) {
136 | case 0:
137 | sscanf(buf, "%d %d", &c->fullscreen_width,&c->fullscreen_height);
138 | break;
139 | case 1:
140 | sscanf(buf, "%d %d", &c->windowed_width,&c->windowed_height);
141 | break;
142 | case 2:
143 | sscanf(buf, "%d", &c->fullscreen_enabled);
144 | break;
145 | case 3:
146 | sscanf(buf, "%d", &c->show_fps);
147 | break;
148 | case 4:
149 | sscanf(buf, "%d", &c->use_camera_ortho3d_projection_matrix);
150 | break;
151 | }
152 | nread=0;
153 | ++numParsedItem;
154 | }
155 | }
156 | fclose(f);
157 | if (c->windowed_width<=0) c->windowed_width=720;
158 | if (c->windowed_height<=0) c->windowed_height=405;
159 | return 0;
160 | }
161 | int Config_Save(Config* c,const char* filePath) {
162 | FILE* f = fopen(filePath, "wt");
163 | if (!f) return -1;
164 | fprintf(f, "[Size In Fullscreen Mode (zero means desktop size)]\n%d %d\n",c->fullscreen_width,c->fullscreen_height);
165 | fprintf(f, "[Size In Windowed Mode]\n%d %d\n",c->windowed_width,c->windowed_height);
166 | fprintf(f, "[Fullscreen Enabled (0 or 1) (CTRL+RETURN)]\n%d\n", c->fullscreen_enabled);
167 | fprintf(f, "[Show FPS (0 or 1) (F2)]\n%d\n", c->show_fps);
168 | fprintf(f, "[Use camera ortho3d projection matrix (0 or 1) (F1)]\n%d\n", c->use_camera_ortho3d_projection_matrix);
169 | fprintf(f,"\n");
170 | fclose(f);
171 | return 0;
172 | }
173 |
174 | Config config;
175 |
176 | // glut has a special fullscreen GameMode that you can toggle with CTRL+RETURN (not in WebGL)
177 | int windowId = 0; // window Id when not in fullscreen mode
178 | int gameModeWindowId = 0; // window Id when in fullscreen mode
179 |
180 | // Now we can start with our program
181 |
182 | // camera data:
183 | float targetPos[3]; // please set it in resetCamera()
184 | float cameraYaw; // please set it in resetCamera()
185 | float cameraPitch; // please set it in resetCamera()
186 | float cameraDistance; // please set it in resetCamera()
187 | float cameraPos[3]; // Derived value (do not edit)
188 | float vMatrix[16]; // view matrix
189 | float cameraSpeed = 0.5f; // When moving it
190 |
191 | // light data
192 | float lightYaw = M_PI*0.425f,lightPitch = M_PI*0.235f; // must be copied to resetLight() too
193 | float lightDirection[4] = {0,1,0,0}; // Derived value (do not edit) [lightDirection[3]==0]
194 |
195 | // pMatrix data:
196 | float pMatrix[16]; // projection matrix
197 | const float pMatrixFovyDeg = 80.f; // we have increased fov (it was 45), because horizontal_and_vertical splits are usually useful for large fovs.
198 | const float pMatrixNearPlane = 0.5f;
199 | const float pMatrixFarPlane = 20.0f;
200 |
201 |
202 | float instantFrameTime = 16.2f;
203 |
204 | // Optional (to speed up Helper_GlutDrawGeometry(...) a bit)
205 | GLuint gDisplayListBase = 0;GLuint* pgDisplayListBase = &gDisplayListBase; // Can be set to 0 as a fallback.
206 |
207 |
208 | static const char* ShadowPassVertexShader[] = {
209 | " void main() {\n"
210 | " gl_Position = ftransform();\n"
211 | " }\n"
212 | };
213 | static const char* ShadowPassFragmentShader[] = {
214 | " void main() {\n"
215 | " //gl_FragColor = gl_Color;\n"
216 | " }\n"
217 | };
218 | typedef struct {
219 | GLuint fbo;
220 | GLuint textureId;
221 | GLuint program;
222 | } ShadowPass;
223 |
224 | ShadowPass shadowPass;
225 | void InitShadowPass(ShadowPass* sp) {
226 | sp->program = Helper_LoadShaderProgramFromSource(*ShadowPassVertexShader,*ShadowPassFragmentShader);
227 |
228 | // create depth texture
229 | glGenTextures(1, &sp->textureId);
230 | glBindTexture(GL_TEXTURE_2D, sp->textureId);
231 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, SHADOW_MAP_FILTER);
232 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, SHADOW_MAP_FILTER);
233 | # ifndef __EMSCRIPTEN__
234 | glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_MAP_WIDTH, SHADOW_MAP_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
235 | # else //__EMSCRIPTEN__
236 | glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_MAP_WIDTH, SHADOW_MAP_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0);
237 | # undef SHADOW_MAP_CLAMP_MODE
238 | # define SHADOW_MAP_CLAMP_MODE GL_CLAMP_TO_EDGE
239 | # endif //__EMSCRIPTEN__
240 | if (SHADOW_MAP_CLAMP_MODE==GL_CLAMP_TO_BORDER) {
241 | const GLfloat border[] = {1.0f,1.0f,1.0f,0.0f };
242 | glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
243 | }
244 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, SHADOW_MAP_CLAMP_MODE );
245 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, SHADOW_MAP_CLAMP_MODE );
246 | glBindTexture(GL_TEXTURE_2D, 0);
247 |
248 | // create depth fbo
249 | glGenFramebuffers(1, &sp->fbo);
250 | glBindFramebuffer(GL_FRAMEBUFFER, sp->fbo);
251 | # ifndef __EMSCRIPTEN__
252 | glDrawBuffer(GL_NONE); // Instruct openGL that we won't bind a color texture with the currently bound FBO
253 | glReadBuffer(GL_NONE);
254 | # endif //__EMSCRIPTEN__
255 | glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,sp->textureId, 0);
256 | {
257 | //Does the GPU support current FBO configuration?
258 | GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
259 | if (status!=GL_FRAMEBUFFER_COMPLETE) printf("glCheckFramebufferStatus(...) FAILED for shadowPass.fbo.\n");
260 | }
261 | glBindFramebuffer(GL_FRAMEBUFFER, 0);
262 | }
263 | void DestroyShadowPass(ShadowPass* sp) {
264 | if (sp->program) {glDeleteProgram(sp->program);sp->program=0;}
265 | if (sp->fbo) {glDeleteBuffers(1,&sp->fbo);sp->fbo=0;}
266 | if (sp->textureId) {glDeleteTextures(1,&sp->textureId);}
267 | }
268 |
269 |
270 | static const char* DefaultPassVertexShader[] = {
271 | "varying vec4 v_diffuse;\n"
272 | "varying vec4 v_vertexModelViewSpace;\n"
273 | "\n"
274 | "void main() {\n"
275 | " gl_Position = ftransform();\n"
276 | "\n"
277 | " vec3 normal = gl_NormalMatrix * gl_Normal;\n"
278 | " vec3 lightVector = gl_LightSource[0].position.xyz\n;// - gl_Vertex.xyz;\n"
279 | " float nxDir = max(0.0, dot(normal, lightVector));\n"
280 | " v_diffuse = gl_LightSource[0].diffuse * nxDir; \n"
281 | "\n"
282 | " gl_FrontColor = gl_Color;\n"
283 | "\n"
284 | " v_vertexModelViewSpace = gl_ModelViewMatrix*gl_Vertex;\n"
285 | "}\n"
286 | };
287 | static const char* DefaultPassFragmentShader[] = {
288 | # ifdef VISUALIZE_CASCADE_SPLITS
289 | "#version 120\n"
290 | "#extension GL_EXT_gpu_shader4 : enable\n"
291 | "const vec3 dbgSplitColors[4] = vec3[4](vec3(0.5,0.0,0.0),vec3(0.0,0.5,0.0),vec3(0.0,0.0,0.5),vec3(0.5,0.5,0.0));\n"
292 | # endif //VISUALIZE_CASCADE_SPLITS
293 | "\n"
294 | "uniform sampler2D u_shadowMap;\n"
295 | "uniform vec2 u_shadowDarkening;\n" // .x = fDarkeningFactor [10.0-80.0], .y = min value clamp [0.0-1.0]
296 | "uniform mat4 u_biasedShadowMvpMatrix[4];\n" // Actually they are: (u_biasedShadowMvpMatrix[4] * vMatrixInverseCamera) please see the code.
297 | "\n"
298 | "varying vec4 v_diffuse;\n"
299 | "varying vec4 v_vertexModelViewSpace;\n"
300 | "\n"
301 | "float CalcShadowFactor(int CascadeIndex, vec4 shadowCoord) {\n"
302 | " vec4 shadowCoordinateWdivide = shadowCoord/shadowCoord.w;\n"
303 | " shadowCoordinateWdivide.x+= float(CascadeIndex);shadowCoordinateWdivide.x/= 4.0;\n"
304 | " return clamp(exp(u_shadowDarkening.x*(texture2D(u_shadowMap,(shadowCoordinateWdivide.st)).r - shadowCoordinateWdivide.z)),u_shadowDarkening.y,1.0);\n"
305 | " }\n"
306 | "\n"
307 | "void main() {\n"
308 | " // Figure out which cascade to sample from\n"
309 | " float cascadeIdxFloat = max(sign(v_vertexModelViewSpace.x),0.0) + 2.0*max(sign(v_vertexModelViewSpace.y),0.0);\n" // is it +v_vertexModelViewSpace.y or -v_vertexModelViewSpace.y?
310 | "\n"
311 | " int cascadeIdx = int(cascadeIdxFloat);\n"
312 | " vec4 lightSpacePos = u_biasedShadowMvpMatrix[cascadeIdx]*v_vertexModelViewSpace;\n" // There's a hidden vMatrixInverseCamera multiplication that removes the view component, moving the mMatrix from the camera space to the light space
313 | " float shadowFactor = CalcShadowFactor(cascadeIdx, lightSpacePos);\n"
314 | "\n"
315 | # ifdef VISUALIZE_CASCADE_SPLITS
316 | " vec3 color = dbgSplitColors[cascadeIdx%4];\n"
317 | # else //VISUALIZE_CASCADE_SPLITS
318 | " vec3 color = gl_Color.rgb;\n"
319 | # endif //VISUALIZE_CASCADE_SPLITS
320 | "\n"
321 | " gl_FragColor = gl_LightSource[0].ambient + (v_diffuse * vec4(color*shadowFactor,1.0));\n"
322 | "}\n"
323 | };
324 |
325 |
326 | typedef struct {
327 | GLuint program;
328 | GLint uniform_location_biasedShadowMvpMatrix;
329 | GLint uniform_location_shadowMap;
330 | GLint uniform_location_shadowDarkening;
331 | } DefaultPass;
332 |
333 | DefaultPass defaultPass;
334 | void InitDefaultPass(DefaultPass* dp) {
335 | dp->program = Helper_LoadShaderProgramFromSource(*DefaultPassVertexShader,*DefaultPassFragmentShader);
336 | dp->uniform_location_biasedShadowMvpMatrix = glGetUniformLocation(dp->program,"u_biasedShadowMvpMatrix");
337 | dp->uniform_location_shadowMap = glGetUniformLocation(dp->program,"u_shadowMap");
338 | dp->uniform_location_shadowDarkening = glGetUniformLocation(dp->program,"u_shadowDarkening");
339 |
340 | glUseProgram(dp->program);
341 | glUniform1i(dp->uniform_location_shadowMap,0);
342 | glUniform2f(dp->uniform_location_shadowDarkening,80.0,0.45); // Default values are (40.0f,0.75f) in [0-80] and [0-1]
343 | //glUniformMatrix4fv(dp->uniform_location_biasedShadowMvpMatrix, 4 /*only setting 1 matrix*/, GL_FALSE /*transpose?*/, Matrix);
344 | glUseProgram(0);
345 | }
346 | void DestroyDefaultPass(DefaultPass* dp) {
347 | if (dp->program) {glDeleteProgram(dp->program);dp->program=0;}
348 | }
349 |
350 | float current_width=0,current_height=0,current_aspect_ratio=1; // Not sure when I've used these...
351 | void ResizeGL(int w,int h) {
352 | current_width = (float) w;
353 | current_height = (float) h;
354 | if (current_height!=0) current_aspect_ratio = current_width/current_height;
355 | if (h>0) {
356 | // We set our pMatrix
357 | if (!config.use_camera_ortho3d_projection_matrix)
358 | Helper_Perspective(pMatrix,pMatrixFovyDeg,(float)w/(float)h,pMatrixNearPlane,pMatrixFarPlane);
359 | else
360 | Helper_Ortho3D(pMatrix,cameraDistance,pMatrixFovyDeg,(float)w/(float)h,pMatrixNearPlane,pMatrixFarPlane);
361 |
362 | glMatrixMode(GL_PROJECTION);glLoadMatrixf(pMatrix);glMatrixMode(GL_MODELVIEW);
363 | }
364 |
365 |
366 | if (w>0 && h>0 && !config.fullscreen_enabled) {
367 | // On exiting we'll like to save these data back
368 | config.windowed_width=w;
369 | config.windowed_height=h;
370 | }
371 |
372 | glViewport(0,0,w,h); // This is what people often call in ResizeGL()
373 |
374 | }
375 |
376 |
377 | void InitGL(void) {
378 |
379 | // These are important, but often overlooked OpenGL calls
380 | glEnable(GL_DEPTH_TEST);
381 | glEnable(GL_CULL_FACE);
382 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Otherwise transparent objects are not displayed correctly
383 | glClearColor(0.3f, 0.6f, 1.0f, 1.0f);
384 | glEnable(GL_TEXTURE_2D); // Only needed for ffp, when VISUALIZE_DEPTH_TEXTURE is defined
385 |
386 |
387 | // ffp stuff
388 | glEnable(GL_LIGHTING);
389 | glEnable(GL_LIGHT0);
390 | glEnable(GL_COLOR_MATERIAL);
391 | glEnable(GL_NORMALIZE);
392 |
393 | // New
394 | InitShadowPass(&shadowPass);
395 | InitDefaultPass(&defaultPass);
396 |
397 | // Please note that after InitGL(), this implementation calls ResizeGL(...,...).
398 | // If you copy/paste this code you can call it explicitly...
399 | }
400 |
401 | void DestroyGL() {
402 | // New
403 | DestroyShadowPass(&shadowPass);
404 | DestroyDefaultPass(&defaultPass);
405 | // 40 display lists are generated by Helper_GlutDrawGeometry(...) if pgDisplayListBase!=0
406 | if (pgDisplayListBase && *pgDisplayListBase) {glDeleteLists(*pgDisplayListBase,40);*pgDisplayListBase=0;}
407 | }
408 |
409 |
410 | void DrawGL(void)
411 | {
412 | // We need to calculate the "instantFrameTime", because it's necessary to "dynamic_resolution.h"
413 | static unsigned begin = 0;
414 | static unsigned cur_time = 0;
415 | unsigned elapsed_time,delta_time;
416 |
417 | static float vMatrixInverse[16];
418 | static float lvpMatrices[4*16]; // = light_pMatrix*light_vMatrix
419 | static float biasedShadowMvpMatrices[4*16]; // multiplied per vMatrixInverse
420 |
421 | float elapsedMs;float cosAlpha,sinAlpha; // used to move objects around
422 |
423 | int i;
424 |
425 | if (begin==0) begin = glutGet(GLUT_ELAPSED_TIME);
426 | elapsed_time = glutGet(GLUT_ELAPSED_TIME) - begin;
427 | delta_time = elapsed_time - cur_time;
428 | instantFrameTime = (float)delta_time*0.001f;
429 | cur_time = elapsed_time;
430 |
431 | elapsedMs = (float)elapsed_time;
432 | cosAlpha = cos(elapsedMs*0.0005f);
433 | sinAlpha = sin(elapsedMs*0.00075f);
434 |
435 | // view Matrix
436 | Helper_LookAt(vMatrix,cameraPos[0],cameraPos[1],cameraPos[2],targetPos[0],targetPos[1],targetPos[2],0,1,0);
437 | glLoadMatrixf(vMatrix);
438 | glLightfv(GL_LIGHT0,GL_POSITION,lightDirection); // Important: the ffp must recalculate internally lightDirectionEyeSpace based on vMatrix [=> every frame]
439 |
440 | // view Matrix inverse (it's the camera matrix). Used twice below. So it's better to keep it here.
441 | Helper_InvertMatrixFast(vMatrixInverse,vMatrix);
442 |
443 |
444 | // Draw to Shadow Map------------------------------------------------------------------------------------------
445 | {
446 | // Experimental: it's currently wrong.
447 | Helper_GetLightViewProjectionMatricesHorizontalAndVertical(lvpMatrices,pMatrixNearPlane,pMatrixFarPlane,
448 | vMatrixInverse,
449 | pMatrixFovyDeg,current_aspect_ratio,
450 | lightDirection,1.0f/(float)SHADOW_MAP_HEIGHT
451 | );
452 |
453 | // Draw to shadow map texture
454 | glMatrixMode(GL_PROJECTION);glPushMatrix();glLoadIdentity();glMatrixMode(GL_MODELVIEW); // We'll set the combined light view-projection matrix in GL_MODELVIEW (do you know that it's the same?)
455 | glBindFramebuffer(GL_FRAMEBUFFER, shadowPass.fbo);
456 | glViewport(0, 0, SHADOW_MAP_WIDTH,SHADOW_MAP_HEIGHT);
457 | glClear(GL_DEPTH_BUFFER_BIT); // Clears all the shadow map
458 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
459 | glCullFace(GL_FRONT);
460 | glEnable(GL_DEPTH_CLAMP);
461 | glUseProgram(shadowPass.program);
462 | for (i=0;i<4;i++) {
463 | glViewport(SHADOW_MAP_HEIGHT*i, 0, SHADOW_MAP_HEIGHT,SHADOW_MAP_HEIGHT);
464 | glPushMatrix();glLoadMatrixf(&lvpMatrices[i*16]); // we load both (light) projection and view matrices here (it's the same after all)
465 | Helper_GlutDrawGeometry(elapsedMs,cosAlpha,sinAlpha,targetPos,pgDisplayListBase); // Done 4 times!
466 | glPopMatrix();
467 | }
468 | glUseProgram(0);
469 | glDisable(GL_DEPTH_CLAMP);
470 | glCullFace(GL_BACK);
471 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
472 | glBindFramebuffer(GL_FRAMEBUFFER,0);
473 | glMatrixMode(GL_PROJECTION);glPopMatrix();glMatrixMode(GL_MODELVIEW);
474 |
475 | }
476 |
477 | // Draw world
478 | {
479 | // biasedShadowMvpMatrix is used only in the DefaultPass:
480 | static float bias[16] = {0.5,0,0,0, 0,0.5,0,0, 0,0,0.5,0, 0.5,0.5,0.5,1}; // Moving from unit cube [-1,1] to [0,1]
481 | for (i=0;i<4;i++) {
482 | Helper_MultMatrix(&biasedShadowMvpMatrices[i*16],bias,&lvpMatrices[i*16]);
483 | Helper_MultMatrix(&biasedShadowMvpMatrices[i*16],&biasedShadowMvpMatrices[i*16],vMatrixInverse); // We do this, so that when in the vs we multiply it with the camera mvMatrix, we get: biasedShadowMvpMatrix * mMatrix (using mMatrices directly in the shaders prevents the usage of double precision matrices: mvMatrices are good when converted to float to feed the shader, mMatrices are bad)
484 | }
485 |
486 | // Draw to world
487 | glViewport(0, 0, current_width,current_height);
488 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
489 | glBindTexture(GL_TEXTURE_2D,shadowPass.textureId);
490 | glUseProgram(defaultPass.program);
491 | glUniformMatrix4fv(defaultPass.uniform_location_biasedShadowMvpMatrix, 4 /*setting many matrices*/, GL_FALSE /*transpose?*/,biasedShadowMvpMatrices);
492 | Helper_GlutDrawGeometry(elapsedMs,cosAlpha,sinAlpha,targetPos,pgDisplayListBase);
493 | glUseProgram(0);
494 | glBindTexture(GL_TEXTURE_2D,0);
495 | }
496 |
497 |
498 |
499 | if (config.show_fps && instantFrameTime>0) {
500 | if ((elapsed_time/1000)%2==0) {
501 | printf("FPS=%1.0f\n",1.f/instantFrameTime);fflush(stdout);
502 | config.show_fps=0;
503 | }
504 | }
505 |
506 |
507 | # ifdef VISUALIZE_DEPTH_TEXTURE
508 | {
509 | glDisable(GL_DEPTH_TEST);
510 | glDisable(GL_CULL_FACE);
511 | glDepthMask(GL_FALSE);
512 |
513 | glMatrixMode(GL_PROJECTION);
514 | glPushMatrix();
515 | glLoadIdentity();
516 |
517 | glMatrixMode(GL_MODELVIEW);
518 | glPushMatrix();
519 | glLoadIdentity();
520 |
521 | glColor3f(1,1,1);
522 | glDisable(GL_LIGHTING);
523 | glEnable(GL_BLEND);
524 | glBindTexture(GL_TEXTURE_2D,shadowPass.textureId);
525 | glColor4f(1,1,1,0.9f);
526 | glBegin(GL_QUADS);
527 | glTexCoord2f(0,0);glVertex2f(-1, 0.75);
528 | glTexCoord2f(1,0);glVertex2f(-1+0.25*(float)4/current_aspect_ratio, 0.75);
529 | glTexCoord2f(1,1);glVertex2f(-1+0.25*(float)4/current_aspect_ratio, 1.0);
530 | glTexCoord2f(0,1);glVertex2f(-1, 1.0);
531 | glEnd();
532 | glBindTexture(GL_TEXTURE_2D,0);
533 | glDisable(GL_BLEND);
534 | glEnable(GL_LIGHTING);
535 |
536 | glPopMatrix();
537 | glMatrixMode(GL_PROJECTION);
538 | glPopMatrix();
539 | glMatrixMode(GL_MODELVIEW);
540 |
541 | glEnable(GL_DEPTH_TEST);
542 | glEnable(GL_CULL_FACE);
543 | glDepthMask(GL_TRUE);
544 | }
545 | # endif //VISUALIZE_DEPTH_TEXTURE
546 |
547 |
548 | }
549 |
550 | static void GlutDestroyWindow(void);
551 | static void GlutCreateWindow();
552 |
553 | void GlutCloseWindow(void) {Config_Save(&config,ConfigFileName);}
554 |
555 | void GlutNormalKeys(unsigned char key, int x, int y) {
556 | const int mod = glutGetModifiers();
557 | switch (key) {
558 | case 27: // esc key
559 | Config_Save(&config,ConfigFileName);
560 | GlutDestroyWindow();
561 | # ifdef __FREEGLUT_STD_H__
562 | glutLeaveMainLoop();
563 | # else
564 | exit(0);
565 | # endif
566 | break;
567 | case 13: // return key
568 | {
569 | if (mod&GLUT_ACTIVE_CTRL) {
570 | config.fullscreen_enabled = gameModeWindowId ? 0 : 1;
571 | GlutDestroyWindow();
572 | GlutCreateWindow();
573 | }
574 | }
575 | break;
576 | }
577 |
578 | }
579 |
580 | static void updateCameraPos() {
581 | const float distanceY = sin(cameraPitch)*cameraDistance;
582 | const float distanceXZ = cos(cameraPitch)*cameraDistance;
583 | cameraPos[0] = targetPos[0] + sin(cameraYaw)*distanceXZ;
584 | cameraPos[1] = targetPos[1] + distanceY;
585 | cameraPos[2] = targetPos[2] + cos(cameraYaw)*distanceXZ;
586 | }
587 |
588 | static void updateDirectionalLight() {
589 | const float distanceY = sin(lightPitch);
590 | const float distanceXZ = cos(lightPitch);
591 | lightDirection[0] = sin(lightYaw)*distanceXZ;
592 | lightDirection[1] = distanceY;
593 | lightDirection[2] = cos(lightYaw)*distanceXZ;
594 | Helper_Vector3Normalize(lightDirection);
595 | lightDirection[3]=0.f;
596 | }
597 |
598 | static void resetCamera() {
599 | // You can set the initial camera position here through:
600 | targetPos[0]=0; targetPos[1]=0; targetPos[2]=0; // The camera target point
601 | cameraYaw = 2*M_PI; // The camera rotation around the Y axis
602 | cameraPitch = M_PI*0.125f; // The camera rotation around the XZ plane
603 | cameraDistance = 5; // The distance between the camera position and the camera target point
604 |
605 | updateCameraPos();
606 | if (config.use_camera_ortho3d_projection_matrix) ResizeGL(current_width,current_height); // Needed because in Helper_Orho3D(...) cameraTargetDistance changes
607 | }
608 |
609 | static void resetLight() {
610 | lightYaw = M_PI*0.425f;
611 | lightPitch = M_PI*0.235f;
612 | updateDirectionalLight();
613 | }
614 |
615 | void GlutSpecialKeys(int key,int x,int y)
616 | {
617 | const int mod = glutGetModifiers();
618 | if (!(mod&GLUT_ACTIVE_CTRL) && !(mod&GLUT_ACTIVE_SHIFT)) {
619 | switch (key) {
620 | case GLUT_KEY_LEFT:
621 | case GLUT_KEY_RIGHT:
622 | cameraYaw+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_LEFT ? -4.0f : 4.0f);
623 | if (cameraYaw>M_PI) cameraYaw-=2*M_PI;
624 | else if (cameraYaw<=-M_PI) cameraYaw+=2*M_PI;
625 | updateCameraPos(); break;
626 | case GLUT_KEY_UP:
627 | case GLUT_KEY_DOWN:
628 | cameraPitch+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_UP ? 2.f : -2.f);
629 | if (cameraPitch>M_PI-0.001f) cameraPitch=M_PI-0.001f;
630 | else if (cameraPitch<-M_PI*0.05f) cameraPitch=-M_PI*0.05f;
631 | updateCameraPos();
632 | break;
633 | case GLUT_KEY_PAGE_UP:
634 | case GLUT_KEY_PAGE_DOWN:
635 | cameraDistance+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_PAGE_DOWN ? 25.0f : -25.0f);
636 | if (cameraDistance<1.f) cameraDistance=1.f;
637 | updateCameraPos();
638 | if (config.use_camera_ortho3d_projection_matrix) ResizeGL(current_width,current_height); // Needed because in Helper_Orho3D(...) cameraTargetDistance changes
639 | break;
640 | case GLUT_KEY_F2:
641 | config.show_fps = !config.show_fps;
642 | //printf("showFPS: %s.\n",config.show_fps?"ON":"OFF");fflush(stdout);
643 | break;
644 | case GLUT_KEY_F1:
645 | config.use_camera_ortho3d_projection_matrix = !config.use_camera_ortho3d_projection_matrix;
646 | //printf("camera ortho mode: %s.\n",config.use_camera_ortho3d_projection_matrix?"ON":"OFF");fflush(stdout);
647 | ResizeGL(current_width,current_height);
648 | break;
649 | case GLUT_KEY_HOME:
650 | // Reset the camera
651 | resetCamera();
652 | break;
653 | }
654 | }
655 | else if (mod&GLUT_ACTIVE_CTRL) {
656 | switch (key) {
657 | case GLUT_KEY_LEFT:
658 | case GLUT_KEY_RIGHT:
659 | case GLUT_KEY_UP:
660 | case GLUT_KEY_DOWN:
661 | {
662 | // Here we move targetPos and cameraPos at the same time
663 |
664 | // We must find a pivot relative to the camera here (ignoring Y)
665 | float forward[3] = {targetPos[0]-cameraPos[0],0,targetPos[2]-cameraPos[2]};
666 | float up[3] = {0,1,0};
667 | float left[3];
668 |
669 | Helper_Vector3Normalize(forward);
670 | Helper_Vector3Cross(left,up,forward);
671 | {
672 | float delta[3] = {0,0,0};int i;
673 | if (key==GLUT_KEY_LEFT || key==GLUT_KEY_RIGHT) {
674 | float amount = instantFrameTime*cameraSpeed*(key==GLUT_KEY_RIGHT ? -25.0f : 25.0f);
675 | for (i=0;i<3;i++) delta[i]+=amount*left[i];
676 | }
677 | else {
678 | float amount = instantFrameTime*cameraSpeed*(key==GLUT_KEY_DOWN ? -25.0f : 25.0f);
679 | for ( i=0;i<3;i++) delta[i]+=amount*forward[i];
680 | }
681 | for ( i=0;i<3;i++) {
682 | targetPos[i]+=delta[i];
683 | cameraPos[i]+=delta[i];
684 | }
685 | }
686 | }
687 | break;
688 | case GLUT_KEY_PAGE_UP:
689 | case GLUT_KEY_PAGE_DOWN:
690 | // We use world space coords here.
691 | targetPos[1]+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_PAGE_DOWN ? -25.0f : 25.0f);
692 | if (targetPos[1]<-50.f) targetPos[1]=-50.f;
693 | else if (targetPos[1]>500.f) targetPos[1]=500.f;
694 | updateCameraPos();
695 | if (config.use_camera_ortho3d_projection_matrix) ResizeGL(current_width,current_height); // Needed because in Helper_Orho3D(...) cameraTargetDistance changes
696 | break;
697 | }
698 | }
699 | else if (mod&GLUT_ACTIVE_SHIFT) {
700 | switch (key) {
701 | case GLUT_KEY_LEFT:
702 | case GLUT_KEY_RIGHT:
703 | lightYaw+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_LEFT ? -4.0f : 4.0f);
704 | if (lightYaw>M_PI) lightYaw-=2*M_PI;
705 | else if (lightYaw<=-M_PI) lightYaw+=2*M_PI;
706 | updateDirectionalLight();
707 | break;
708 | case GLUT_KEY_UP:
709 | case GLUT_KEY_DOWN:
710 | case GLUT_KEY_PAGE_UP:
711 | case GLUT_KEY_PAGE_DOWN:
712 | lightPitch+= instantFrameTime*cameraSpeed*( (key==GLUT_KEY_UP || key==GLUT_KEY_PAGE_UP) ? 2.f : -2.f);
713 | if (lightPitch>M_PI-0.001f) lightPitch=M_PI-0.001f;
714 | else if (lightPitch<-M_PI*0.05f) lightPitch=-M_PI*0.05f;
715 | updateDirectionalLight();
716 | break;
717 | case GLUT_KEY_HOME:
718 | // Reset the light
719 | resetLight();
720 | break;
721 | }
722 | }
723 | }
724 |
725 | void GlutMouse(int a,int b,int c,int d) {
726 |
727 | }
728 |
729 | // Note that we have used GlutFakeDrawGL() so that at startup
730 | // the calling order is: InitGL(),ResizeGL(...),DrawGL()
731 | // Also note that glutSwapBuffers() must NOT be called inside DrawGL()
732 | static void GlutDrawGL(void) {DrawGL();glutSwapBuffers();}
733 | static void GlutIdle(void) {glutPostRedisplay();}
734 | static void GlutFakeDrawGL(void) {glutDisplayFunc(GlutDrawGL);}
735 | void GlutDestroyWindow(void) {
736 | if (gameModeWindowId || windowId) {
737 | DestroyGL();
738 |
739 | if (gameModeWindowId) {
740 | glutLeaveGameMode();
741 | gameModeWindowId = 0;
742 | }
743 | if (windowId) {
744 | glutDestroyWindow(windowId);
745 | windowId=0;
746 | }
747 | }
748 | }
749 | void GlutCreateWindow() {
750 | GlutDestroyWindow();
751 | if (config.fullscreen_enabled) {
752 | char gms[16]="";
753 | if (config.fullscreen_width>0 && config.fullscreen_height>0) {
754 | sprintf(gms,"%dx%d:32",config.fullscreen_width,config.fullscreen_height);
755 | glutGameModeString(gms);
756 | if (glutGameModeGet (GLUT_GAME_MODE_POSSIBLE)) gameModeWindowId = glutEnterGameMode();
757 | else config.fullscreen_width=config.fullscreen_height=0;
758 | }
759 | if (gameModeWindowId==0) {
760 | const int screenWidth = glutGet(GLUT_SCREEN_WIDTH);
761 | const int screenHeight = glutGet(GLUT_SCREEN_HEIGHT);
762 | sprintf(gms,"%dx%d:32",screenWidth,screenHeight);
763 | glutGameModeString(gms);
764 | if (glutGameModeGet (GLUT_GAME_MODE_POSSIBLE)) gameModeWindowId = glutEnterGameMode();
765 | }
766 | }
767 | if (!gameModeWindowId) {
768 | char windowTitle[1024] = PROGRAM_NAME".c\t";
769 | /*
770 | # ifdef USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
771 | strcat(windowTitle,"[Unstable]\t");
772 | # endif //USE_UNSTABLE_SHADOW_MAPPING_TECHNIQUE
773 | */
774 | strcat(windowTitle,"("XSTR_MACRO(SHADOW_MAP_HEIGHT)"x4)");
775 | config.fullscreen_enabled = 0;
776 | glutInitWindowPosition(100,100);
777 | glutInitWindowSize(config.windowed_width,config.windowed_height);
778 | windowId = glutCreateWindow(windowTitle);
779 | }
780 |
781 | glutKeyboardFunc(GlutNormalKeys);
782 | glutSpecialFunc(GlutSpecialKeys);
783 | glutMouseFunc(GlutMouse);
784 | glutIdleFunc(GlutIdle);
785 | glutReshapeFunc(ResizeGL);
786 | glutDisplayFunc(GlutFakeDrawGL);
787 | # ifdef __FREEGLUT_STD_H__
788 | glutWMCloseFunc(GlutCloseWindow);
789 | # endif //__FREEGLUT_STD_H__
790 |
791 | #ifdef USE_GLEW
792 | {
793 | GLenum err = glewInit();
794 | if( GLEW_OK != err ) {
795 | fprintf(stderr, "Error initializing GLEW: %s\n", glewGetErrorString(err) );
796 | return;
797 | }
798 | }
799 | #endif //USE_GLEW
800 |
801 | InitGL();
802 |
803 | }
804 |
805 |
806 | int main(int argc, char** argv)
807 | {
808 | glutInit(&argc, argv);
809 | glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
810 | //glutInitContextFlags(GLUT_FORWARD_COMPATIBLE);
811 | #ifdef __FREEGLUT_STD_H__
812 | glutSetOption ( GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION ) ;
813 | #endif //__FREEGLUT_STD_H__
814 |
815 | Config_Init(&config);
816 | Config_Load(&config,ConfigFileName);
817 |
818 | GlutCreateWindow();
819 |
820 | //OpenGL info
821 | printf("\nGL Vendor: %s\n", glGetString( GL_VENDOR ));
822 | printf("GL Renderer : %s\n", glGetString( GL_RENDERER ));
823 | printf("GL Version (string) : %s\n", glGetString( GL_VERSION ));
824 | printf("GLSL Version : %s\n", glGetString( GL_SHADING_LANGUAGE_VERSION ));
825 | //printf("GL Extensions:\n%s\n",(char *) glGetString(GL_EXTENSIONS));
826 | {int maxTextureSize=0;glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);printf("Max Texture Size: %d\n", maxTextureSize);}
827 | // Many GPUs return 16384 as their max size.
828 | // For 4 bytes per pixel: 16384*16384*4 bytes = 1 Gb
829 | // For 4 float per pixel: 16384*16384*4 floats (a floating point texture) = 4Gb.
830 | // Many GPUs don't even have 4Gb of memory
831 |
832 | printf("\nKEYS:\n");
833 | printf("AROW KEYS + PAGE_UP/PAGE_DOWN:\tmove camera (optionally with CTRL down)\n");
834 | printf("HOME KEY:\t\t\treset camera\n");
835 | printf("ARROW KEYS + SHIFT:\tmove directional light\n");
836 | printf("CTRL+RETURN:\t\ttoggle fullscreen on/off\n");
837 | printf("F2:\t\t\tdisplay FPS\n");
838 | printf("F1:\t\t\ttoggle camera ortho mode on and off\n");
839 | printf("\n");
840 |
841 | resetCamera(); // Mandatory
842 | resetLight(); // Mandatory
843 |
844 | glutMainLoop();
845 |
846 |
847 | return 0;
848 | }
849 |
850 |
851 |
852 |
853 |
854 |
855 |
856 |
857 |
858 |
859 |
--------------------------------------------------------------------------------
/shadow_mapping_pcf.c:
--------------------------------------------------------------------------------
1 | // https://github.com/Flix01/Tiny-OpenGL-Shadow-Mapping-Examples
2 | /** License
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 | * SOFTWARE.
18 | */
19 |
20 | // DEPENDENCIES:
21 | /*
22 | -> glut or freeglut (the latter is recommended)
23 | -> glew (Windows only)
24 | */
25 |
26 | // HOW TO COMPILE:
27 | /*
28 | // LINUX:
29 | gcc -O2 -std=gnu89 -no-pie shadow_mapping_pcf.c -o shadow_mapping_pcf -I"../" -lglut -lGL -lX11 -lm
30 | // WINDOWS (here we use the static version of glew, and glut32.lib, that can be replaced by freeglut.lib):
31 | cl /O2 /MT /Tc shadow_mapping_pcf.c /D"GLEW_STATIC" /I"../" /link /out:shadow_mapping_pcf.exe glut32.lib glew32s.lib opengl32.lib gdi32.lib Shell32.lib comdlg32.lib user32.lib kernel32.lib
32 |
33 |
34 | // IN ADDITION:
35 | By default the source file assumes that every OpenGL-related header is in "GL/".
36 | But you can define in the command-line the correct paths you use in your system
37 | for glut.h, glew.h, etc. with something like:
38 | -DGLUT_PATH=\"Glut/glut.h\"
39 | -DGLEW_PATH=\"Glew/glew.h\"
40 | (this syntax works on Linux, don't know about Windows)
41 | */
42 |
43 | //#define USE_GLEW // By default it's only defined for Windows builds (but can be defined in Linux/Mac builds too)
44 |
45 |
46 | #define PROGRAM_NAME "shadow_mapping_pcf"
47 | #define VISUALIZE_DEPTH_TEXTURE
48 | #define SHADOW_MAP_RESOLUTION 1024 //1024
49 | #define SHADOW_MAP_FILTER GL_LINEAR // GL_LINEAR or GL_NEAREST (GL_LINEAR with a sampler2DShadow has a free 2x2-taps pcf on NVIDIA and recent AMD graphic cards)
50 | #define SHADOW_MAP_PCF_NUM_TAPS_SQRT 4 // (e.g: 1x1 (no extra taps), 2x2, 3x3, 4x4, 5x5, 6x6, 7x7,...)
51 | #define SHADOW_MAP_CLAMP_MODE GL_CLAMP_TO_EDGE // GL_CLAMP or GL_CLAMP_TO_EDGE or GL_CLAMP_TO_BORDER
52 | // GL_CLAMP; // sampling outside of the shadow map gives always shadowed pixels
53 | // GL_CLAMP_TO_EDGE; // sampling outside of the shadow map can give shadowed or unshadowed pixels (it depends on the edge of the shadow map)
54 | // GL_CLAMP_TO_BORDER; // sampling outside of the shadow map gives always non-shadowed pixels (if we set the border color correctly)
55 |
56 |
57 | // These path definitions can be passed to the compiler command-line
58 | #ifndef GLUT_PATH
59 | # define GLUT_PATH "GL/glut.h" // Mandatory
60 | #endif //GLEW_PATH
61 | #ifndef FREEGLUT_EXT_PATH
62 | # define FREEGLUT_EXT_PATH "GL/freeglut_ext.h" // Optional (used only if glut.h comes from the freeglut library)
63 | #endif //GLEW_PATH
64 | #ifndef GLEW_PATH
65 | # define GLEW_PATH "GL/glew.h" // Mandatory for Windows only
66 | #endif //GLEW_PATH
67 |
68 | #ifdef _WIN32
69 | # include "windows.h"
70 | # define USE_GLEW
71 | #endif //_WIN32
72 |
73 | #ifdef USE_GLEW
74 | # include GLEW_PATH
75 | #else //USE_GLEW
76 | # define GL_GLEXT_PROTOTYPES
77 | #endif //USE_GLEW
78 |
79 | #include GLUT_PATH
80 | #ifdef __FREEGLUT_STD_H__
81 | # include FREEGLUT_EXT_PATH
82 | #endif //__FREEGLUT_STD_H__
83 |
84 | #define STR_MACRO(s) #s
85 | #define XSTR_MACRO(s) STR_MACRO(s)
86 |
87 |
88 | #if (SHADOW_MAP_PCF_NUM_TAPS_SQRT==0 || SHADOW_MAP_PCF_NUM_TAPS_SQRT==1)
89 | # define SHADOW_MAP_PCF_NO_EXTRA_TAPS
90 | #endif //SHADOW_MAP_PCF_EXTRA_TAPS_SQRT
91 |
92 | #include "helper_functions.h" // please search this .c file for "Helper_":
93 | // only very few of its functions are used.
94 |
95 | #include
96 | #include
97 | #include
98 |
99 |
100 | // Config file handling: basically there's an .ini file next to the
101 | // exe that you can tweak. (it's just an extra)
102 | const char* ConfigFileName = PROGRAM_NAME".ini";
103 | typedef struct {
104 | int fullscreen_width,fullscreen_height;
105 | int windowed_width,windowed_height;
106 | int fullscreen_enabled;
107 | int show_fps;
108 | } Config;
109 | void Config_Init(Config* c) {
110 | c->fullscreen_width=c->fullscreen_height=0;
111 | c->windowed_width=960;c->windowed_height=540;
112 | c->fullscreen_enabled=0;
113 | c->show_fps = 0;
114 | }
115 | int Config_Load(Config* c,const char* filePath) {
116 | FILE* f = fopen(filePath, "rt");
117 | char ch='\0';char buf[256]="";
118 | size_t nread=0;
119 | int numParsedItem=0;
120 | if (!f) return -1;
121 | while ((ch = fgetc(f)) !=EOF) {
122 | buf[nread]=ch;
123 | nread++;
124 | if (nread>255) {
125 | nread=0;
126 | continue;
127 | }
128 | if (ch=='\n') {
129 | buf[nread]='\0';
130 | if (nread<2 || buf[0]=='[' || buf[0]=='#') {nread = 0;continue;}
131 | if (nread>2 && buf[0]=='/' && buf[1]=='/') {nread = 0;continue;}
132 | // Parse
133 | switch (numParsedItem) {
134 | case 0:
135 | sscanf(buf, "%d %d", &c->fullscreen_width,&c->fullscreen_height);
136 | break;
137 | case 1:
138 | sscanf(buf, "%d %d", &c->windowed_width,&c->windowed_height);
139 | break;
140 | case 2:
141 | sscanf(buf, "%d", &c->fullscreen_enabled);
142 | break;
143 | case 4:
144 | sscanf(buf, "%d", &c->show_fps);
145 | break;
146 | }
147 | nread=0;
148 | ++numParsedItem;
149 | }
150 | }
151 | fclose(f);
152 | if (c->windowed_width<=0) c->windowed_width=720;
153 | if (c->windowed_height<=0) c->windowed_height=405;
154 | return 0;
155 | }
156 | int Config_Save(Config* c,const char* filePath) {
157 | FILE* f = fopen(filePath, "wt");
158 | if (!f) return -1;
159 | fprintf(f, "[Size In Fullscreen Mode (zero means desktop size)]\n%d %d\n",c->fullscreen_width,c->fullscreen_height);
160 | fprintf(f, "[Size In Windowed Mode]\n%d %d\n",c->windowed_width,c->windowed_height);
161 | fprintf(f, "[Fullscreen Enabled (0 or 1) (CTRL+RETURN)]\n%d\n", c->fullscreen_enabled);
162 | fprintf(f, "[Show FPS (0 or 1) (F2)]\n%d\n", c->show_fps);
163 | fprintf(f,"\n");
164 | fclose(f);
165 | return 0;
166 | }
167 |
168 | Config config;
169 |
170 | // glut has a special fullscreen GameMode that you can toggle with CTRL+RETURN (not in WebGL)
171 | int windowId = 0; // window Id when not in fullscreen mode
172 | int gameModeWindowId = 0; // window Id when in fullscreen mode
173 |
174 | // Now we can start with our program
175 |
176 | // camera data:
177 | float targetPos[3]; // please set it in resetCamera()
178 | float cameraYaw; // please set it in resetCamera()
179 | float cameraPitch; // please set it in resetCamera()
180 | float cameraDistance; // please set it in resetCamera()
181 | float cameraPos[3]; // Derived value (do not edit)
182 | float vMatrix[16]; // view matrix
183 | float cameraSpeed = 0.5f; // When moving it
184 |
185 | // light data
186 | float lightYaw = M_PI*0.425f,lightPitch = M_PI*0.235f; // must be copied to resetLight() too
187 | float lightDirection[4] = {0,1,0,0}; // Derived value (do not edit) [lightDirection[3]==0]
188 |
189 | // pMatrix data:
190 | float pMatrix[16]; // projection matrix
191 | const float pMatrixFovyDeg = 45.f; // smaller => better shadow resolution
192 | const float pMatrixNearPlane = 0.5f; // bigger => better shadow resolution
193 | const float pMatrixFarPlane = 20.f; // smaller => better shadow resolution
194 |
195 | float instantFrameTime = 16.2f;
196 |
197 | // Optional (to speed up Helper_GlutDrawGeometry(...) a bit)
198 | GLuint gDisplayListBase = 0;GLuint* pgDisplayListBase = &gDisplayListBase; // Can be set to 0 as a fallback.
199 |
200 |
201 | static const char* ShadowPassVertexShader[] = {
202 | " void main() {\n"
203 | " gl_Position = ftransform();\n"
204 | " }\n"
205 | };
206 | static const char* ShadowPassFragmentShader[] = {
207 | " void main() {\n"
208 | " //gl_FragColor = gl_Color;\n"
209 | " }\n"
210 | };
211 | typedef struct {
212 | GLuint fbo;
213 | GLuint textureId;
214 | GLuint program;
215 | } ShadowPass;
216 |
217 | ShadowPass shadowPass;
218 | void InitShadowPass(ShadowPass* sp) {
219 | sp->program = Helper_LoadShaderProgramFromSource(*ShadowPassVertexShader,*ShadowPassFragmentShader);
220 |
221 | // create depth texture
222 | glGenTextures(1, &sp->textureId);
223 | glBindTexture(GL_TEXTURE_2D, sp->textureId);
224 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, SHADOW_MAP_FILTER);
225 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, SHADOW_MAP_FILTER);
226 | # ifndef __EMSCRIPTEN__
227 | glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_MAP_RESOLUTION, SHADOW_MAP_RESOLUTION, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
228 | # else //__EMSCRIPTEN__
229 | glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_MAP_RESOLUTION, SHADOW_MAP_RESOLUTION, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0);
230 | # undef SHADOW_MAP_CLAMP_MODE
231 | # define SHADOW_MAP_CLAMP_MODE GL_CLAMP_TO_EDGE
232 | # endif //__EMSCRIPTEN__
233 | if (SHADOW_MAP_CLAMP_MODE==GL_CLAMP_TO_BORDER) {
234 | const GLfloat border[] = {1.0f,1.0f,1.0f,0.0f };
235 | glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
236 | }
237 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, SHADOW_MAP_CLAMP_MODE );
238 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, SHADOW_MAP_CLAMP_MODE );
239 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
240 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
241 | glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
242 | glBindTexture(GL_TEXTURE_2D, 0);
243 |
244 | // create depth fbo
245 | glGenFramebuffers(1, &sp->fbo);
246 | glBindFramebuffer(GL_FRAMEBUFFER, sp->fbo);
247 | # ifndef __EMSCRIPTEN__
248 | glDrawBuffer(GL_NONE); // Instruct openGL that we won't bind a color texture with the currently bound FBO
249 | glReadBuffer(GL_NONE);
250 | # endif //__EMSCRIPTEN__
251 | glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,sp->textureId, 0);
252 | {
253 | //Does the GPU support current FBO configuration?
254 | GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
255 | if (status!=GL_FRAMEBUFFER_COMPLETE) printf("glCheckFramebufferStatus(...) FAILED for shadowPass.fbo.\n");
256 | }
257 | glBindFramebuffer(GL_FRAMEBUFFER, 0);
258 | }
259 | void DestroyShadowPass(ShadowPass* sp) {
260 | if (sp->program) {glDeleteProgram(sp->program);sp->program=0;}
261 | if (sp->fbo) {glDeleteBuffers(1,&sp->fbo);sp->fbo=0;}
262 | if (sp->textureId) {glDeleteTextures(1,&sp->textureId);}
263 | }
264 |
265 | static const char* DefaultPassVertexShader[] = {
266 | "uniform mat4 u_biasedShadowMvpMatrix;\n" // (*) Actually it's already multiplied with vMatrixInverse (in C code, so that the multiplication can be easily done with doubles)
267 | "uniform vec2 u_shadowBiasAndDarkening;\n" // .x = bias (e.g. 0.01) .y = darkening min value clamp [0.0-1.0]
268 | "varying vec4 v_shadowCoord;\n"
269 | "varying vec4 v_diffuse;\n"
270 | "\n"
271 | "void main() {\n"
272 | " gl_Position = ftransform();\n"
273 | "\n"
274 | " vec3 normal = gl_NormalMatrix * gl_Normal;\n"
275 | " normalize(normal);\n"
276 | " vec3 lightVector = gl_LightSource[0].position.xyz\n;// - gl_Vertex.xyz;\n"
277 | " float nxDir = max(0.0, dot(normal, lightVector));\n"
278 | " v_diffuse = gl_LightSource[0].diffuse * nxDir; \n"
279 | "\n"
280 | " gl_FrontColor = gl_Color;\n"
281 | "\n"
282 | " v_shadowCoord = u_biasedShadowMvpMatrix*(gl_ModelViewMatrix*gl_Vertex);\n" // (*) We don't pass a 'naked' mMatrix in shaders (not robust to double precision usage). We dress it in a mvMatrix. So here we're passing a mMatrix from camera space to light space (through a mvMatrix).
283 | "}\n" // (the bias just converts clip space to texture space)
284 | };
285 | static const char* DefaultPassFragmentShader[] = {
286 | "#define TABSSQRT "XSTR_MACRO(SHADOW_MAP_PCF_NUM_TAPS_SQRT)"\n"
287 | "uniform sampler2DShadow u_shadowMap;\n"
288 | "uniform vec2 u_shadowBiasAndDarkening;\n" // .x = bias (e.g. 0.01) .y = darkening min value clamp [0.0-1.0]
289 | "uniform vec2 u_texelIncrements;\n" // used only with extra taps
290 | "varying vec4 v_shadowCoord;\n"
291 | "varying vec4 v_diffuse;\n"
292 | "\n"
293 | "void main() {\n"
294 | " float shadowFactor = 0.0;\n"
295 | " vec4 shadowCoordinateWdivide = v_shadowCoord/v_shadowCoord.w;\n"
296 | # ifdef SHADOW_MAP_PCF_NO_EXTRA_TAPS
297 | " shadowFactor = shadow2D(u_shadowMap,vec3(shadowCoordinateWdivide.st,shadowCoordinateWdivide.z-u_shadowBiasAndDarkening.x));\n"
298 | # else //SHADOW_MAP_PCF_NO_EXTRA_TAPS
299 | # if (SHADOW_MAP_PCF_NUM_TAPS_SQRT== (SHADOW_MAP_PCF_NUM_TAPS_SQRT/2)*2) // even
300 | " const float edgeVal = 0.5+float((TABSSQRT-1)/2);\n"
301 | " const float startVal = -edgeVal;\n"
302 | " const float endVal = edgeVal+0.5;\n" // we use +0.5 and < instead of <= in the for loop (more robust)
303 | # else // odd
304 | " const float edgeVal = float((TABSSQRT-1)/2);\n"
305 | " const float startVal = -edgeVal;\n"
306 | " const float endVal = edgeVal+0.5;\n" // we use +0.5 and < instead of <= in the for loop (more robust)
307 | # endif
308 | " float x,y;\n;"
309 | " float biasedShadowCoordinateZ = shadowCoordinateWdivide.z-u_shadowBiasAndDarkening.x;\n;"
310 | " for (y=startVal; yprogram = Helper_LoadShaderProgramFromSource(*DefaultPassVertexShader,*DefaultPassFragmentShader);
333 | dp->uniform_location_biasedShadowMvpMatrix = glGetUniformLocation(dp->program,"u_biasedShadowMvpMatrix");
334 | dp->uniform_location_shadowMap = glGetUniformLocation(dp->program,"u_shadowMap");
335 | dp->uniform_location_shadowBiasAndDarkening = glGetUniformLocation(dp->program,"u_shadowBiasAndDarkening");
336 | dp->uniform_location_texelIncrements = glGetUniformLocation(dp->program,"u_texelIncrements");
337 |
338 | glUseProgram(dp->program);
339 | glUniform1i(dp->uniform_location_shadowMap,0);
340 | glUniform2f(dp->uniform_location_shadowBiasAndDarkening,0.0/*085*/,0.45); // Default values are (0.01f,0.75f). The second value must be in [0-1]. If you use glPolyfonOffset(...) in DrawGL(), you can set the first one (the bias) to zero.
341 | glUniform2f(dp->uniform_location_texelIncrements,1.f/(float)SHADOW_MAP_RESOLUTION,1.f/(float)SHADOW_MAP_RESOLUTION);
342 | //glUniformMatrix4fv(dp->uniform_location_biasedShadowMvpMatrix, 1 /*only setting 1 matrix*/, GL_FALSE /*transpose?*/, Matrix);
343 | glUseProgram(0);
344 | }
345 | void DestroyDefaultPass(DefaultPass* dp) {
346 | if (dp->program) {glDeleteProgram(dp->program);dp->program=0;}
347 | }
348 |
349 | float current_width=0,current_height=0,current_aspect_ratio=1; // Not sure when I've used these...
350 | void ResizeGL(int w,int h) {
351 | current_width = (float) w;
352 | current_height = (float) h;
353 | if (current_height!=0) current_aspect_ratio = current_width/current_height;
354 | if (h>0) {
355 | // We set our pMatrix
356 | Helper_Perspective(pMatrix,pMatrixFovyDeg,(float)w/(float)h,pMatrixNearPlane,pMatrixFarPlane);
357 |
358 | glMatrixMode(GL_PROJECTION);glLoadMatrixf(pMatrix);glMatrixMode(GL_MODELVIEW);
359 | }
360 |
361 |
362 | if (w>0 && h>0 && !config.fullscreen_enabled) {
363 | // On exiting we'll like to save these data back
364 | config.windowed_width=w;
365 | config.windowed_height=h;
366 | }
367 |
368 | glViewport(0,0,w,h); // This is what people often call in ResizeGL()
369 |
370 | }
371 |
372 |
373 | void InitGL(void) {
374 |
375 | // These are important, but often overlooked OpenGL calls
376 | glEnable(GL_DEPTH_TEST);
377 | glEnable(GL_CULL_FACE);
378 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Otherwise transparent objects are not displayed correctly
379 | glClearColor(0.3f, 0.6f, 1.0f, 1.0f);
380 | glEnable(GL_TEXTURE_2D); // Only needed for ffp, when VISUALIZE_DEPTH_TEXTURE is defined
381 |
382 |
383 | // ffp stuff
384 | glEnable(GL_LIGHTING);
385 | glEnable(GL_LIGHT0);
386 | glEnable(GL_COLOR_MATERIAL);
387 | glEnable(GL_NORMALIZE);
388 |
389 | // New
390 | InitShadowPass(&shadowPass);
391 | InitDefaultPass(&defaultPass);
392 |
393 | // Please note that after InitGL(), this implementation calls ResizeGL(...,...).
394 | // If you copy/paste this code you can call it explicitly...
395 | }
396 |
397 | void DestroyGL() {
398 | // New
399 | DestroyShadowPass(&shadowPass);
400 | DestroyDefaultPass(&defaultPass);
401 | // 40 display lists are generated by Helper_GlutDrawGeometry(...) if pgDisplayListBase!=0
402 | if (pgDisplayListBase && *pgDisplayListBase) {glDeleteLists(*pgDisplayListBase,40);*pgDisplayListBase=0;}
403 | }
404 |
405 |
406 |
407 | void DrawGL(void)
408 | {
409 | // All the things about time are just used to display FPS (F2)
410 | // or to move objects around (NOT for shadow)
411 | static unsigned begin = 0;
412 | static unsigned cur_time = 0;
413 | unsigned elapsed_time,delta_time;
414 | float elapsedMs;float cosAlpha,sinAlpha; // used to move objects around
415 |
416 | // These two instead are necessary for shadow mapping
417 | static float vMatrixInverse[16]; // view Matrix inverse (it's the camera matrix).
418 | static float lvpMatrix[16]; // = light_pMatrix*light_vMatrix
419 |
420 | // Just some time stuff here
421 | if (begin==0) begin = glutGet(GLUT_ELAPSED_TIME);
422 | elapsed_time = glutGet(GLUT_ELAPSED_TIME) - begin;
423 | delta_time = elapsed_time - cur_time;
424 | instantFrameTime = (float)delta_time*0.001f;
425 | cur_time = elapsed_time;
426 |
427 | elapsedMs = (float)elapsed_time;
428 | cosAlpha = cos(elapsedMs*0.0005f);
429 | sinAlpha = sin(elapsedMs*0.00075f);
430 |
431 |
432 | // view Matrix
433 | Helper_LookAt(vMatrix,cameraPos[0],cameraPos[1],cameraPos[2],targetPos[0],targetPos[1],targetPos[2],0,1,0);
434 | glLoadMatrixf(vMatrix);
435 | glLightfv(GL_LIGHT0,GL_POSITION,lightDirection); // Important: the ffp must recalculate internally lightDirectionEyeSpace based on vMatrix [=> every frame]
436 |
437 | // view Matrix inverse (it's the camera matrix). Used twice below (and very important to keep in any case).
438 | Helper_InvertMatrixFast(vMatrixInverse,vMatrix); // We can use Helper_InvertMatrixFast(...) instead of Helper_InvertMatrix(...) here [No scaling inside and no projection matrix]
439 |
440 |
441 | // Draw to Shadow Map------------------------------------------------------------------------------------------
442 | {
443 | Helper_GetLightViewProjectionMatrix(lvpMatrix,
444 | vMatrixInverse,pMatrixNearPlane,pMatrixFarPlane,pMatrixFovyDeg,current_aspect_ratio,
445 | lightDirection,1.0f/(float)SHADOW_MAP_RESOLUTION);
446 |
447 |
448 | // Draw to shadow map texture
449 | glMatrixMode(GL_PROJECTION);glPushMatrix();glLoadIdentity();glMatrixMode(GL_MODELVIEW); // We'll set the combined light view-projection matrix in GL_MODELVIEW (do you know that it's the same?)
450 | glBindFramebuffer(GL_FRAMEBUFFER, shadowPass.fbo);
451 | glViewport(0, 0, SHADOW_MAP_RESOLUTION,SHADOW_MAP_RESOLUTION);
452 | glClear(GL_DEPTH_BUFFER_BIT);
453 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
454 | //glCullFace(GL_FRONT); // Well, if objects are open (like the Teapot mesh), maybe glPolygonOffset(...) is better (with adjusted values)
455 | glEnable(GL_POLYGON_OFFSET_FILL);glPolygonOffset(-2.0f, -2.0f);
456 | glEnable(GL_DEPTH_CLAMP);
457 | glUseProgram(shadowPass.program); // we can just use glUseProgram(0) here
458 | glPushMatrix();glLoadMatrixf(lvpMatrix); // we load both (light) projection and view matrices here (it's the same after all)
459 | Helper_GlutDrawGeometry(elapsedMs,cosAlpha,sinAlpha,targetPos,pgDisplayListBase); // Done SHADOW_MAP_NUM_CASCADES times!
460 | glPopMatrix();
461 | glUseProgram(0);
462 | glDisable(GL_DEPTH_CLAMP);
463 | glDisable(GL_POLYGON_OFFSET_FILL);
464 | //glCullFace(GL_BACK);
465 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
466 | glBindFramebuffer(GL_FRAMEBUFFER,0);
467 | glMatrixMode(GL_PROJECTION);glPopMatrix();glMatrixMode(GL_MODELVIEW);
468 |
469 | }
470 |
471 | // Draw world
472 | {
473 | // biasedShadowMvpMatrix is used only in the DefaultPass:
474 | static float bias[16] = {0.5,0,0,0, 0,0.5,0,0, 0,0,0.5,0, 0.5,0.5,0.5,1}; // Moving from unit cube [-1,1] to [0,1]
475 | static float biasedShadowMvpMatrix[16]; // multiplied per vMatrixInverse
476 | Helper_MultMatrix(biasedShadowMvpMatrix,bias,lvpMatrix);
477 | Helper_MultMatrix(biasedShadowMvpMatrix,biasedShadowMvpMatrix,vMatrixInverse); // We do this, so that when in the vertex shader we multiply it with the camera mvMatrix, we get: biasedShadowMvpMatrix * mMatrix (using mMatrices directly in the shaders prevents the usage of double precision matrices: mvMatrices are good when converted to float to feed the shader, mMatrices are bad)
478 |
479 | // Draw to world
480 | glViewport(0, 0, current_width,current_height);
481 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
482 | glBindTexture(GL_TEXTURE_2D,shadowPass.textureId);
483 | glUseProgram(defaultPass.program);
484 | glUniformMatrix4fv(defaultPass.uniform_location_biasedShadowMvpMatrix, 1 /*only setting 1 matrix*/, GL_FALSE /*transpose?*/,biasedShadowMvpMatrix);
485 | Helper_GlutDrawGeometry(elapsedMs,cosAlpha,sinAlpha,targetPos,pgDisplayListBase); // Done SHADOW_MAP_NUM_CASCADES times!
486 | glUseProgram(0);
487 | glBindTexture(GL_TEXTURE_2D,0);
488 | }
489 |
490 |
491 | if (config.show_fps && instantFrameTime>0) {
492 | if ((elapsed_time/1000)%2==0) {
493 | printf("FPS=%1.0f\n",1.f/instantFrameTime);fflush(stdout);
494 | config.show_fps=0;
495 | }
496 | }
497 |
498 |
499 | # ifdef VISUALIZE_DEPTH_TEXTURE
500 | {
501 | glDisable(GL_DEPTH_TEST);
502 | glDisable(GL_CULL_FACE);
503 | glDepthMask(GL_FALSE);
504 |
505 | glMatrixMode(GL_PROJECTION);
506 | glPushMatrix();
507 | glLoadIdentity();
508 |
509 | glMatrixMode(GL_MODELVIEW);
510 | glPushMatrix();
511 | glLoadIdentity();
512 |
513 | glColor3f(1,1,1);
514 | glDisable(GL_LIGHTING);
515 | glEnable(GL_BLEND);
516 | glBindTexture(GL_TEXTURE_2D,shadowPass.textureId);
517 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
518 | glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE );
519 | glColor4f(1,1,1,0.9f);
520 | glBegin(GL_QUADS);
521 | glTexCoord2f(0,0);glVertex2f(-1, -1);
522 | glTexCoord2f(1,0);glVertex2f(-0.25*current_aspect_ratio, -1);
523 | glTexCoord2f(1,1);glVertex2f(-0.25*current_aspect_ratio, -0.25/current_aspect_ratio);
524 | glTexCoord2f(0,1);glVertex2f(-1, -0.25/current_aspect_ratio);
525 | glEnd();
526 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
527 | glTexParameteri( GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY );
528 | glBindTexture(GL_TEXTURE_2D,0);
529 | glDisable(GL_BLEND);
530 | glEnable(GL_LIGHTING);
531 |
532 | glPopMatrix();
533 | glMatrixMode(GL_PROJECTION);
534 | glPopMatrix();
535 | glMatrixMode(GL_MODELVIEW);
536 |
537 | glEnable(GL_DEPTH_TEST);
538 | glEnable(GL_CULL_FACE);
539 | glDepthMask(GL_TRUE);
540 | }
541 | # endif //VISUALIZE_DEPTH_TEXTURE
542 |
543 |
544 | }
545 |
546 | static void GlutDestroyWindow(void);
547 | static void GlutCreateWindow();
548 |
549 | void GlutCloseWindow(void) {Config_Save(&config,ConfigFileName);}
550 |
551 | void GlutNormalKeys(unsigned char key, int x, int y) {
552 | const int mod = glutGetModifiers();
553 | switch (key) {
554 | case 27: // esc key
555 | Config_Save(&config,ConfigFileName);
556 | GlutDestroyWindow();
557 | # ifdef __FREEGLUT_STD_H__
558 | glutLeaveMainLoop();
559 | # else
560 | exit(0);
561 | # endif
562 | break;
563 | case 13: // return key
564 | {
565 | if (mod&GLUT_ACTIVE_CTRL) {
566 | config.fullscreen_enabled = gameModeWindowId ? 0 : 1;
567 | GlutDestroyWindow();
568 | GlutCreateWindow();
569 | }
570 | }
571 | break;
572 | }
573 |
574 | }
575 |
576 | static void updateCameraPos() {
577 | const float distanceY = sin(cameraPitch)*cameraDistance;
578 | const float distanceXZ = cos(cameraPitch)*cameraDistance;
579 | cameraPos[0] = targetPos[0] + sin(cameraYaw)*distanceXZ;
580 | cameraPos[1] = targetPos[1] + distanceY;
581 | cameraPos[2] = targetPos[2] + cos(cameraYaw)*distanceXZ;
582 | }
583 |
584 | static void updateDirectionalLight() {
585 | const float distanceY = sin(lightPitch);
586 | const float distanceXZ = cos(lightPitch);
587 | lightDirection[0] = sin(lightYaw)*distanceXZ;
588 | lightDirection[1] = distanceY;
589 | lightDirection[2] = cos(lightYaw)*distanceXZ;
590 | Helper_Vector3Normalize(lightDirection);
591 | lightDirection[3]=0.f;
592 | }
593 |
594 | static void resetCamera() {
595 | // You can set the initial camera position here through:
596 | targetPos[0]=0; targetPos[1]=0; targetPos[2]=0; // The camera target point
597 | cameraYaw = 2*M_PI; // The camera rotation around the Y axis
598 | cameraPitch = M_PI*0.125f; // The camera rotation around the XZ plane
599 | cameraDistance = 5; // The distance between the camera position and the camera target point
600 |
601 | updateCameraPos();
602 | }
603 |
604 | static void resetLight() {
605 | lightYaw = M_PI*0.425f;
606 | lightPitch = M_PI*0.235f;
607 | updateDirectionalLight();
608 | }
609 |
610 | void GlutSpecialKeys(int key,int x,int y)
611 | {
612 | const int mod = glutGetModifiers();
613 | if (!(mod&GLUT_ACTIVE_CTRL) && !(mod&GLUT_ACTIVE_SHIFT)) {
614 | switch (key) {
615 | case GLUT_KEY_LEFT:
616 | case GLUT_KEY_RIGHT:
617 | cameraYaw+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_LEFT ? -4.0f : 4.0f);
618 | if (cameraYaw>M_PI) cameraYaw-=2*M_PI;
619 | else if (cameraYaw<=-M_PI) cameraYaw+=2*M_PI;
620 | updateCameraPos(); break;
621 | case GLUT_KEY_UP:
622 | case GLUT_KEY_DOWN:
623 | cameraPitch+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_UP ? 2.f : -2.f);
624 | if (cameraPitch>M_PI-0.001f) cameraPitch=M_PI-0.001f;
625 | else if (cameraPitch<-M_PI*0.05f) cameraPitch=-M_PI*0.05f;
626 | updateCameraPos();
627 | break;
628 | case GLUT_KEY_PAGE_UP:
629 | case GLUT_KEY_PAGE_DOWN:
630 | cameraDistance+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_PAGE_DOWN ? 25.0f : -25.0f);
631 | if (cameraDistance<1.f) cameraDistance=1.f;
632 | updateCameraPos();
633 | break;
634 | case GLUT_KEY_F1:
635 | case GLUT_KEY_F2:
636 | config.show_fps = !config.show_fps;
637 | //printf("showFPS: %s.\n",config.show_fps?"ON":"OFF");fflush(stdout);
638 | break;
639 | case GLUT_KEY_HOME:
640 | // Reset the camera
641 | resetCamera();
642 | break;
643 | }
644 | }
645 | else if (mod&GLUT_ACTIVE_CTRL) {
646 | switch (key) {
647 | case GLUT_KEY_LEFT:
648 | case GLUT_KEY_RIGHT:
649 | case GLUT_KEY_UP:
650 | case GLUT_KEY_DOWN:
651 | {
652 | // Here we move targetPos and cameraPos at the same time
653 |
654 | // We must find a pivot relative to the camera here (ignoring Y)
655 | float forward[3] = {targetPos[0]-cameraPos[0],0,targetPos[2]-cameraPos[2]};
656 | float up[3] = {0,1,0};
657 | float left[3];
658 |
659 | Helper_Vector3Normalize(forward);
660 | Helper_Vector3Cross(left,up,forward);
661 | {
662 | float delta[3] = {0,0,0};int i;
663 | if (key==GLUT_KEY_LEFT || key==GLUT_KEY_RIGHT) {
664 | float amount = instantFrameTime*cameraSpeed*(key==GLUT_KEY_RIGHT ? -25.0f : 25.0f);
665 | for (i=0;i<3;i++) delta[i]+=amount*left[i];
666 | }
667 | else {
668 | float amount = instantFrameTime*cameraSpeed*(key==GLUT_KEY_DOWN ? -25.0f : 25.0f);
669 | for ( i=0;i<3;i++) delta[i]+=amount*forward[i];
670 | }
671 | for ( i=0;i<3;i++) {
672 | targetPos[i]+=delta[i];
673 | cameraPos[i]+=delta[i];
674 | }
675 | }
676 | }
677 | break;
678 | case GLUT_KEY_PAGE_UP:
679 | case GLUT_KEY_PAGE_DOWN:
680 | // We use world space coords here.
681 | targetPos[1]+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_PAGE_DOWN ? -25.0f : 25.0f);
682 | if (targetPos[1]<-50.f) targetPos[1]=-50.f;
683 | else if (targetPos[1]>500.f) targetPos[1]=500.f;
684 | updateCameraPos();
685 | break;
686 | }
687 | }
688 | else if (mod&GLUT_ACTIVE_SHIFT) {
689 | switch (key) {
690 | case GLUT_KEY_LEFT:
691 | case GLUT_KEY_RIGHT:
692 | lightYaw+= instantFrameTime*cameraSpeed*(key==GLUT_KEY_LEFT ? -4.0f : 4.0f);
693 | if (lightYaw>M_PI) lightYaw-=2*M_PI;
694 | else if (lightYaw<=-M_PI) lightYaw+=2*M_PI;
695 | updateDirectionalLight();
696 | break;
697 | case GLUT_KEY_UP:
698 | case GLUT_KEY_DOWN:
699 | case GLUT_KEY_PAGE_UP:
700 | case GLUT_KEY_PAGE_DOWN:
701 | lightPitch+= instantFrameTime*cameraSpeed*( (key==GLUT_KEY_UP || key==GLUT_KEY_PAGE_UP) ? 2.f : -2.f);
702 | if (lightPitch>M_PI-0.001f) lightPitch=M_PI-0.001f;
703 | else if (lightPitch<-M_PI*0.05f) lightPitch=-M_PI*0.05f;
704 | updateDirectionalLight();
705 | break;
706 | case GLUT_KEY_HOME:
707 | // Reset the light
708 | resetLight();
709 | break;
710 | }
711 | }
712 | }
713 |
714 | void GlutMouse(int a,int b,int c,int d) {
715 |
716 | }
717 |
718 | // Note that we have used GlutFakeDrawGL() so that at startup
719 | // the calling order is: InitGL(),ResizeGL(...),DrawGL()
720 | // Also note that glutSwapBuffers() must NOT be called inside DrawGL()
721 | static void GlutDrawGL(void) {DrawGL();glutSwapBuffers();}
722 | static void GlutIdle(void) {glutPostRedisplay();}
723 | static void GlutFakeDrawGL(void) {glutDisplayFunc(GlutDrawGL);}
724 | void GlutDestroyWindow(void) {
725 | if (gameModeWindowId || windowId) {
726 | DestroyGL();
727 |
728 | if (gameModeWindowId) {
729 | glutLeaveGameMode();
730 | gameModeWindowId = 0;
731 | }
732 | if (windowId) {
733 | glutDestroyWindow(windowId);
734 | windowId=0;
735 | }
736 | }
737 | }
738 | void GlutCreateWindow() {
739 | GlutDestroyWindow();
740 | if (config.fullscreen_enabled) {
741 | char gms[16]="";
742 | if (config.fullscreen_width>0 && config.fullscreen_height>0) {
743 | sprintf(gms,"%dx%d:32",config.fullscreen_width,config.fullscreen_height);
744 | glutGameModeString(gms);
745 | if (glutGameModeGet (GLUT_GAME_MODE_POSSIBLE)) gameModeWindowId = glutEnterGameMode();
746 | else config.fullscreen_width=config.fullscreen_height=0;
747 | }
748 | if (gameModeWindowId==0) {
749 | const int screenWidth = glutGet(GLUT_SCREEN_WIDTH);
750 | const int screenHeight = glutGet(GLUT_SCREEN_HEIGHT);
751 | sprintf(gms,"%dx%d:32",screenWidth,screenHeight);
752 | glutGameModeString(gms);
753 | if (glutGameModeGet (GLUT_GAME_MODE_POSSIBLE)) gameModeWindowId = glutEnterGameMode();
754 | }
755 | }
756 | if (!gameModeWindowId) {
757 | char windowTitle[1024] = PROGRAM_NAME".c\t("XSTR_MACRO(SHADOW_MAP_RESOLUTION)")\tTaps: "XSTR_MACRO(SHADOW_MAP_PCF_NUM_TAPS_SQRT)"x"XSTR_MACRO(SHADOW_MAP_PCF_NUM_TAPS_SQRT);
758 | # if (SHADOW_MAP_FILTER==GL_LINEAR)
759 | strcat(windowTitle,"\tfilter: GL_LINEAR");
760 | # elif (SHADOW_MAP_FILTER==GL_NEAREST)
761 | strcat(windowTitle,"\tfilter: GL_NEAREST");
762 | # endif // SHADOW_MAP_FILTER
763 | config.fullscreen_enabled = 0;
764 | glutInitWindowPosition(100,100);
765 | glutInitWindowSize(config.windowed_width,config.windowed_height);
766 | windowId = glutCreateWindow(windowTitle);
767 | }
768 |
769 | glutKeyboardFunc(GlutNormalKeys);
770 | glutSpecialFunc(GlutSpecialKeys);
771 | glutMouseFunc(GlutMouse);
772 | glutIdleFunc(GlutIdle);
773 | glutReshapeFunc(ResizeGL);
774 | glutDisplayFunc(GlutFakeDrawGL);
775 | # ifdef __FREEGLUT_STD_H__
776 | glutWMCloseFunc(GlutCloseWindow);
777 | # endif //__FREEGLUT_STD_H__
778 |
779 | #ifdef USE_GLEW
780 | {
781 | GLenum err = glewInit();
782 | if( GLEW_OK != err ) {
783 | fprintf(stderr, "Error initializing GLEW: %s\n", glewGetErrorString(err) );
784 | return;
785 | }
786 | }
787 | #endif //USE_GLEW
788 |
789 | InitGL();
790 |
791 | }
792 |
793 |
794 | int main(int argc, char** argv)
795 | {
796 |
797 | glutInit(&argc, argv);
798 | glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
799 | //glutInitContextFlags(GLUT_FORWARD_COMPATIBLE);
800 | #ifdef __FREEGLUT_STD_H__
801 | glutSetOption ( GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION ) ;
802 | #endif //__FREEGLUT_STD_H__
803 |
804 | Config_Init(&config);
805 | Config_Load(&config,ConfigFileName);
806 |
807 | GlutCreateWindow();
808 |
809 | //OpenGL info
810 | printf("\nGL Vendor: %s\n", glGetString( GL_VENDOR ));
811 | printf("GL Renderer : %s\n", glGetString( GL_RENDERER ));
812 | printf("GL Version (string) : %s\n", glGetString( GL_VERSION ));
813 | printf("GLSL Version : %s\n", glGetString( GL_SHADING_LANGUAGE_VERSION ));
814 | //printf("GL Extensions:\n%s\n",(char *) glGetString(GL_EXTENSIONS));
815 |
816 | printf("\nKEYS:\n");
817 | printf("AROW KEYS + PAGE_UP/PAGE_DOWN:\tmove camera (optionally with CTRL down)\n");
818 | printf("HOME KEY:\t\t\treset camera\n");
819 | printf("ARROW KEYS + SHIFT:\tmove directional light\n");
820 | printf("CTRL+RETURN:\t\ttoggle fullscreen on/off\n");
821 | printf("F2:\t\t\tdisplay FPS\n");
822 | printf("\n");
823 |
824 | resetCamera(); // Mandatory
825 | resetLight(); // Mandatory
826 |
827 | glutMainLoop();
828 |
829 |
830 | return 0;
831 | }
832 |
833 | void DrawGeometry(float elapsedMs,float cosAlpha,float sinAlpha) {
834 | int i,j;
835 |
836 | // ground
837 | glColor3f(0.2,0.4,0.2);
838 | glPushMatrix();
839 |
840 | glTranslatef(0,-0.5,0);
841 |
842 | glPushMatrix();
843 | glScalef(11.f,0.5f,14.f);
844 | glutSolidCube(1.0);
845 | glPopMatrix();
846 |
847 | glPopMatrix();
848 |
849 | // sphere
850 | glColor3f(0.8,0.8,0);
851 | glPushMatrix();
852 |
853 | glTranslatef(-1+2.5*cosAlpha,0.25,-1+sinAlpha);
854 |
855 | glPushMatrix();
856 | glRotatef(-elapsedMs*0.05f,0,1,0);
857 | glScalef(1.f,1.f,1.f);
858 | glutSolidSphere(0.5,16,16);
859 | glPopMatrix();
860 |
861 | glPopMatrix();
862 |
863 | // tours
864 | glColor3f(0.4,0.4,0.8);
865 | glPushMatrix();
866 |
867 | glTranslatef(-0.5+2.5*cosAlpha,0.5,2-sinAlpha);
868 |
869 | glPushMatrix();
870 | glRotatef(elapsedMs*0.05f,0,1,0);
871 | glScalef(1.f,1.f,1.f);
872 | glutSolidTorus(0.25,0.5,16,16);
873 | glPopMatrix();
874 |
875 | glPopMatrix();
876 |
877 | // teapot
878 | glColor3f(0.4,0.2,0.0);
879 | glPushMatrix();
880 |
881 | glTranslatef(-0.4,0.1,-4);
882 |
883 | glPushMatrix();
884 | glRotatef(elapsedMs*0.1f,0,1,0);
885 | glScalef(1.f,1.f,1.f);
886 | glFrontFace(GL_CW);glutSolidTeapot(0.5);glFrontFace(GL_CCW);
887 | //glutSolidCube(0.75);
888 | glPopMatrix();
889 |
890 | glPopMatrix();
891 |
892 | // cube
893 | glColor3f(0.5,0.5,1.0);
894 | glPushMatrix();
895 | glTranslatef(-2,0.3,0.25);
896 | glScalef(0.5f,1.5f,0.5f);
897 | glutSolidCube(0.75);
898 | glPopMatrix();
899 |
900 |
901 | // columns
902 | glColor3f(0.35,0.35,0.35);
903 | for (j=0;j<2;j++) {
904 | for (i=0;i<=10;i++) {
905 | glPushMatrix();
906 |
907 | glTranslatef(4.75-j*2.0,2.7+0.31,-5.f+(float)i*1.0);
908 | glPushMatrix();
909 | glRotatef(90,1,0,0);
910 |
911 | glPushMatrix();
912 | glScalef(0.2f,0.2f,2.8f);
913 |
914 | // central part
915 | glutSolidCylinder(0.5,1.0,8,8);
916 |
917 | // higher part
918 | glutSolidCone(0.8f,0.1f,8,8);
919 | glTranslatef(0.f,0.f,-0.025f);
920 | glutSolidCylinder(0.8,0.025,8,8);
921 |
922 | // lower part
923 | glTranslatef(0.f,0.f,1.05);
924 | glFrontFace(GL_CW);glutSolidCone(0.8f,-0.1f,8,8);glFrontFace(GL_CCW);
925 | glTranslatef(0.f,0.f,0.0f);
926 | glutSolidCylinder(0.8,0.025,8,8);
927 |
928 | glPopMatrix();
929 |
930 |
931 | glPopMatrix();
932 |
933 | glPopMatrix();
934 | }
935 | }
936 |
937 |
938 | // column plane under roof
939 | glColor3f(0.8,0.8,0.8);
940 | glPushMatrix();
941 | glTranslatef(3.75,3.16,0.f);
942 | glScalef(2.5f,0.155f,10.75f);
943 | glutSolidCube(1.0);
944 | glPopMatrix();
945 |
946 | // column roof
947 | glColor3f(0.4,0.0,0.0);
948 | glPushMatrix();
949 | glTranslatef(3.75,3.02+0.48,-10.75*0.5);
950 | glScalef(0.825f,0.3f,1.f);
951 | glRotatef(90,0,0,1);
952 | glutSolidCylinder(1.75,10.75,3,3);
953 | glPopMatrix();
954 |
955 | // column base
956 | glColor3f(0.2,0.2,0.2);
957 | glPushMatrix();
958 |
959 | glTranslatef(3.75,-0.01f,0.f);
960 | glScalef(2.8f,0.155f,10.5f);
961 | glutSolidCube(1.0);
962 |
963 | glTranslatef(0,-1,0);
964 | glScalef(1.15f,1,1.05f);
965 | glutSolidCube(1.0);
966 |
967 | glPopMatrix();
968 |
969 | // camera target pos:
970 | // Center
971 | glColor3f(0,0,0);
972 | glPushMatrix();
973 | glTranslatef(targetPos[0],targetPos[1],targetPos[2]);
974 | glPushMatrix();
975 | glutSolidSphere(0.04,8,8);
976 |
977 | // X Axis
978 | glPushMatrix();
979 | glColor3f(1,0,0);
980 | glRotatef(90,0,1,0);
981 | glutSolidCylinder(0.04,0.25,8,8);
982 | glTranslatef(0,0,0.25);
983 | glutSolidCone(0.06,0.1,8,8);
984 | glPopMatrix();
985 |
986 | // Y Axis
987 | glPushMatrix();
988 | glColor3f(0,1,0);
989 | glRotatef(-90,1,0,0);
990 | glutSolidCylinder(0.04,0.25,8,8);
991 | glTranslatef(0,0,0.25);
992 | glutSolidCone(0.06,0.1,8,8);
993 | glPopMatrix();
994 |
995 | // Z Axis
996 | glPushMatrix();
997 | glColor3f(0,0,1);
998 | glutSolidCylinder(0.04,0.25,8,8);
999 | glTranslatef(0,0,0.25);
1000 | glutSolidCone(0.06,0.1,8,8);
1001 | glPopMatrix();
1002 |
1003 | glPopMatrix();
1004 | glPopMatrix();
1005 | // End camera target position
1006 |
1007 | //# define DBG_BIG_OCCLUDING_WALL
1008 | # ifdef DBG_BIG_OCCLUDING_WALL
1009 | // big occluding wall
1010 | glColor3f(0.5,0.1,0.1);
1011 | glPushMatrix();
1012 |
1013 | glTranslatef(10,0,0);
1014 |
1015 | glPushMatrix();
1016 | glScalef(0.25f,2.f*pMatrixFarPlane,2.f*pMatrixFarPlane);
1017 | glutSolidCube(1.0);
1018 | glPopMatrix();
1019 |
1020 | glPopMatrix();
1021 | # endif // DBG_BIG_OCCLUDING_WALL
1022 |
1023 | }
1024 |
1025 |
1026 |
1027 |
1028 |
1029 |
1030 |
1031 |
1032 |
1033 |
1034 |
--------------------------------------------------------------------------------