-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
Copy pathshaders_shadowmap.c
255 lines (214 loc) · 10.1 KB
/
shaders_shadowmap.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/*******************************************************************************************
*
* raylib [shaders] example - Shadowmap
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 5.0, last time updated with raylib 5.0
*
* Example contributed by TheManTheMythTheGameDev (@TheManTheMythTheGameDev) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2023-2025 TheManTheMythTheGameDev (@TheManTheMythTheGameDev)
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#include "rlgl.h"
#if defined(PLATFORM_DESKTOP)
#define GLSL_VERSION 330
#else // PLATFORM_ANDROID, PLATFORM_WEB
#define GLSL_VERSION 120
#endif
#define SHADOWMAP_RESOLUTION 1024
RenderTexture2D LoadShadowmapRenderTexture(int width, int height);
void UnloadShadowmapRenderTexture(RenderTexture2D target);
void DrawScene(Model cube, Model robot);
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
SetConfigFlags(FLAG_MSAA_4X_HINT);
// Shadows are a HUGE topic, and this example shows an extremely simple implementation of the shadowmapping algorithm,
// which is the industry standard for shadows. This algorithm can be extended in a ridiculous number of ways to improve
// realism and also adapt it for different scenes. This is pretty much the simplest possible implementation.
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shadowmap");
Camera3D cam = (Camera3D){ 0 };
cam.position = (Vector3){ 10.0f, 10.0f, 10.0f };
cam.target = Vector3Zero();
cam.projection = CAMERA_PERSPECTIVE;
cam.up = (Vector3){ 0.0f, 1.0f, 0.0f };
cam.fovy = 45.0f;
Shader shadowShader = LoadShader(TextFormat("resources/shaders/glsl%i/shadowmap.vs", GLSL_VERSION),
TextFormat("resources/shaders/glsl%i/shadowmap.fs", GLSL_VERSION));
shadowShader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shadowShader, "viewPos");
Vector3 lightDir = Vector3Normalize((Vector3){ 0.35f, -1.0f, -0.35f });
Color lightColor = WHITE;
Vector4 lightColorNormalized = ColorNormalize(lightColor);
int lightDirLoc = GetShaderLocation(shadowShader, "lightDir");
int lightColLoc = GetShaderLocation(shadowShader, "lightColor");
SetShaderValue(shadowShader, lightDirLoc, &lightDir, SHADER_UNIFORM_VEC3);
SetShaderValue(shadowShader, lightColLoc, &lightColorNormalized, SHADER_UNIFORM_VEC4);
int ambientLoc = GetShaderLocation(shadowShader, "ambient");
float ambient[4] = {0.1f, 0.1f, 0.1f, 1.0f};
SetShaderValue(shadowShader, ambientLoc, ambient, SHADER_UNIFORM_VEC4);
int lightVPLoc = GetShaderLocation(shadowShader, "lightVP");
int shadowMapLoc = GetShaderLocation(shadowShader, "shadowMap");
int shadowMapResolution = SHADOWMAP_RESOLUTION;
SetShaderValue(shadowShader, GetShaderLocation(shadowShader, "shadowMapResolution"), &shadowMapResolution, SHADER_UNIFORM_INT);
Model cube = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f));
cube.materials[0].shader = shadowShader;
Model robot = LoadModel("resources/models/robot.glb");
for (int i = 0; i < robot.materialCount; i++)
{
robot.materials[i].shader = shadowShader;
}
int animCount = 0;
ModelAnimation* robotAnimations = LoadModelAnimations("resources/models/robot.glb", &animCount);
RenderTexture2D shadowMap = LoadShadowmapRenderTexture(SHADOWMAP_RESOLUTION, SHADOWMAP_RESOLUTION);
// For the shadowmapping algorithm, we will be rendering everything from the light's point of view
Camera3D lightCam = (Camera3D){ 0 };
lightCam.position = Vector3Scale(lightDir, -15.0f);
lightCam.target = Vector3Zero();
// Use an orthographic projection for directional lights
lightCam.projection = CAMERA_ORTHOGRAPHIC;
lightCam.up = (Vector3){ 0.0f, 1.0f, 0.0f };
lightCam.fovy = 20.0f;
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
int fc = 0;
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
float dt = GetFrameTime();
Vector3 cameraPos = cam.position;
SetShaderValue(shadowShader, shadowShader.locs[SHADER_LOC_VECTOR_VIEW], &cameraPos, SHADER_UNIFORM_VEC3);
UpdateCamera(&cam, CAMERA_ORBITAL);
fc++;
fc %= (robotAnimations[0].frameCount);
UpdateModelAnimation(robot, robotAnimations[0], fc);
const float cameraSpeed = 0.05f;
if (IsKeyDown(KEY_LEFT))
{
if (lightDir.x < 0.6f)
lightDir.x += cameraSpeed * 60.0f * dt;
}
if (IsKeyDown(KEY_RIGHT))
{
if (lightDir.x > -0.6f)
lightDir.x -= cameraSpeed * 60.0f * dt;
}
if (IsKeyDown(KEY_UP))
{
if (lightDir.z < 0.6f)
lightDir.z += cameraSpeed * 60.0f * dt;
}
if (IsKeyDown(KEY_DOWN))
{
if (lightDir.z > -0.6f)
lightDir.z -= cameraSpeed * 60.0f * dt;
}
lightDir = Vector3Normalize(lightDir);
lightCam.position = Vector3Scale(lightDir, -15.0f);
SetShaderValue(shadowShader, lightDirLoc, &lightDir, SHADER_UNIFORM_VEC3);
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
// First, render all objects into the shadowmap
// The idea is, we record all the objects' depths (as rendered from the light source's point of view) in a buffer
// Anything that is "visible" to the light is in light, anything that isn't is in shadow
// We can later use the depth buffer when rendering everything from the player's point of view
// to determine whether a given point is "visible" to the light
// Record the light matrices for future use!
Matrix lightView;
Matrix lightProj;
BeginTextureMode(shadowMap);
ClearBackground(WHITE);
BeginMode3D(lightCam);
lightView = rlGetMatrixModelview();
lightProj = rlGetMatrixProjection();
DrawScene(cube, robot);
EndMode3D();
EndTextureMode();
Matrix lightViewProj = MatrixMultiply(lightView, lightProj);
ClearBackground(RAYWHITE);
SetShaderValueMatrix(shadowShader, lightVPLoc, lightViewProj);
rlEnableShader(shadowShader.id);
int slot = 10; // Can be anything 0 to 15, but 0 will probably be taken up
rlActiveTextureSlot(10);
rlEnableTexture(shadowMap.depth.id);
rlSetUniform(shadowMapLoc, &slot, SHADER_UNIFORM_INT, 1);
BeginMode3D(cam);
// Draw the same exact things as we drew in the shadowmap!
DrawScene(cube, robot);
EndMode3D();
DrawText("Shadows in raylib using the shadowmapping algorithm!", screenWidth - 320, screenHeight - 20, 10, GRAY);
DrawText("Use the arrow keys to rotate the light!", 10, 10, 30, RED);
EndDrawing();
if (IsKeyPressed(KEY_F))
{
TakeScreenshot("shaders_shadowmap.png");
}
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadShader(shadowShader);
UnloadModel(cube);
UnloadModel(robot);
UnloadModelAnimations(robotAnimations, animCount);
UnloadShadowmapRenderTexture(shadowMap);
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
RenderTexture2D LoadShadowmapRenderTexture(int width, int height)
{
RenderTexture2D target = { 0 };
target.id = rlLoadFramebuffer(); // Load an empty framebuffer
target.texture.width = width;
target.texture.height = height;
if (target.id > 0)
{
rlEnableFramebuffer(target.id);
// Create depth texture
// We don't need a color texture for the shadowmap
target.depth.id = rlLoadTextureDepth(width, height, false);
target.depth.width = width;
target.depth.height = height;
target.depth.format = 19; //DEPTH_COMPONENT_24BIT?
target.depth.mipmaps = 1;
// Attach depth texture to FBO
rlFramebufferAttach(target.id, target.depth.id, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_TEXTURE2D, 0);
// Check if fbo is complete with attachments (valid)
if (rlFramebufferComplete(target.id)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", target.id);
rlDisableFramebuffer();
}
else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created");
return target;
}
// Unload shadowmap render texture from GPU memory (VRAM)
void UnloadShadowmapRenderTexture(RenderTexture2D target)
{
if (target.id > 0)
{
// NOTE: Depth texture/renderbuffer is automatically
// queried and deleted before deleting framebuffer
rlUnloadFramebuffer(target.id);
}
}
void DrawScene(Model cube, Model robot)
{
DrawModelEx(cube, Vector3Zero(), (Vector3) { 0.0f, 1.0f, 0.0f }, 0.0f, (Vector3) { 10.0f, 1.0f, 10.0f }, BLUE);
DrawModelEx(cube, (Vector3) { 1.5f, 1.0f, -1.5f }, (Vector3) { 0.0f, 1.0f, 0.0f }, 0.0f, Vector3One(), WHITE);
DrawModelEx(robot, (Vector3) { 0.0f, 0.5f, 0.0f }, (Vector3) { 0.0f, 1.0f, 0.0f }, 0.0f, (Vector3) { 1.0f, 1.0f, 1.0f }, RED);
}