From 421fd7e6f5c9111415919d381e378b7ced248f49 Mon Sep 17 00:00:00 2001 From: JeffMyers Date: Thu, 6 Aug 2026 15:26:07 -0700 Subject: [PATCH 1/2] Update the camera before sending it to the shader --- .../shaders/shaders_shadowmap_rendering.c | 390 +++++++++--------- 1 file changed, 196 insertions(+), 194 deletions(-) diff --git a/examples/shaders/shaders_shadowmap_rendering.c b/examples/shaders/shaders_shadowmap_rendering.c index 3b98ab5b633d..6c18cafe4d56 100644 --- a/examples/shaders/shaders_shadowmap_rendering.c +++ b/examples/shaders/shaders_shadowmap_rendering.c @@ -21,9 +21,9 @@ #include "rlgl.h" #if defined(PLATFORM_DESKTOP) - #define GLSL_VERSION 330 +#define GLSL_VERSION 330 #else // PLATFORM_ANDROID, PLATFORM_WEB - #define GLSL_VERSION 100 +#define GLSL_VERSION 100 #endif #define SHADOWMAP_RESOLUTION 1024 @@ -37,167 +37,169 @@ static void DrawScene(Model cube, Model robot); //------------------------------------------------------------------------------------ int main(void) { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - // 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 - - SetConfigFlags(FLAG_MSAA_4X_HINT); - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shadowmap rendering"); - - Camera3D camera = (Camera3D){ 0 }; - camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; - camera.target = Vector3Zero(); - camera.projection = CAMERA_PERSPECTIVE; - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; - camera.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 *anims = 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 lightCamera = { 0 }; - lightCamera.position = Vector3Scale(lightDir, -15.0f); - lightCamera.target = Vector3Zero(); - lightCamera.projection = CAMERA_ORTHOGRAPHIC; // Use an orthographic projection for directional lights - lightCamera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; - lightCamera.fovy = 20.0f; - - int frameCounter = 0; - - // Store the light matrices - Matrix lightView = { 0 }; - Matrix lightProj = { 0 }; - Matrix lightViewProj = { 0 }; - int textureActiveSlot = 10; // Can be anything 0 to 15, but 0 will probably be taken up - - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - float deltaTime = GetFrameTime(); - - Vector3 cameraPos = camera.position; - SetShaderValue(shadowShader, shadowShader.locs[SHADER_LOC_VECTOR_VIEW], &cameraPos, SHADER_UNIFORM_VEC3); - UpdateCamera(&camera, CAMERA_ORBITAL); - - frameCounter++; - frameCounter %= (anims[0].keyframeCount); - UpdateModelAnimation(robot, anims[0], (float)frameCounter); - - // Move light with arrow keys - const float cameraSpeed = 0.05f; - if (IsKeyDown(KEY_LEFT)) - { - if (lightDir.x < 0.6f) lightDir.x += cameraSpeed*60.0f*deltaTime; - } - if (IsKeyDown(KEY_RIGHT)) - { - if (lightDir.x > -0.6f) lightDir.x -= cameraSpeed*60.0f*deltaTime; - } - if (IsKeyDown(KEY_UP)) - { - if (lightDir.z < 0.6f) lightDir.z += cameraSpeed*60.0f*deltaTime; - } - if (IsKeyDown(KEY_DOWN)) - { - if (lightDir.z > -0.6f) lightDir.z -= cameraSpeed*60.0f*deltaTime; - } - - lightDir = Vector3Normalize(lightDir); - lightCamera.position = Vector3Scale(lightDir, -15.0f); - SetShaderValue(shadowShader, lightDirLoc, &lightDir, SHADER_UNIFORM_VEC3); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - // PASS 01: Render all objects into the shadowmap render texture - // 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 - BeginTextureMode(shadowMap); - ClearBackground(WHITE); - - BeginMode3D(lightCamera); - lightView = rlGetMatrixModelview(); - lightProj = rlGetMatrixProjection(); - DrawScene(cube, robot); - EndMode3D(); - - EndTextureMode(); - lightViewProj = MatrixMultiply(lightView, lightProj); - - // PASS 02: Draw the scene into main framebuffer, using the generated shadowmap - BeginDrawing(); - ClearBackground(RAYWHITE); - - SetShaderValueMatrix(shadowShader, lightVPLoc, lightViewProj); - rlEnableShader(shadowShader.id); - - rlActiveTextureSlot(textureActiveSlot); - rlEnableTexture(shadowMap.depth.id); - rlSetUniform(shadowMapLoc, &textureActiveSlot, SHADER_UNIFORM_INT, 1); - - BeginMode3D(camera); - DrawScene(cube, robot); // Draw the same exact things as we drew in the shadowmap! - EndMode3D(); - - DrawText("Use the arrow keys to rotate the light!", 10, 10, 30, RED); - DrawText("Shadows in raylib using the shadowmapping algorithm!", screenWidth - 280, screenHeight - 20, 10, GRAY); - - EndDrawing(); - - if (IsKeyPressed(KEY_F)) TakeScreenshot("shaders_shadowmap.png"); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadShader(shadowShader); - UnloadModel(cube); - UnloadModel(robot); - UnloadModelAnimations(anims, animCount); - UnloadShadowmapRenderTexture(shadowMap); - - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + // 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 + + SetConfigFlags(FLAG_MSAA_4X_HINT); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shadowmap rendering"); + + Camera3D camera = (Camera3D){ 0 }; + camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; + camera.target = Vector3Zero(); + camera.projection = CAMERA_PERSPECTIVE; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.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* anims = 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 lightCamera = { 0 }; + lightCamera.position = Vector3Scale(lightDir, -15.0f); + lightCamera.target = Vector3Zero(); + lightCamera.projection = CAMERA_ORTHOGRAPHIC; // Use an orthographic projection for directional lights + lightCamera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + lightCamera.fovy = 20.0f; + + int frameCounter = 0; + + // Store the light matrices + Matrix lightView = { 0 }; + Matrix lightProj = { 0 }; + Matrix lightViewProj = { 0 }; + int textureActiveSlot = 10; // Can be anything 0 to 15, but 0 will probably be taken up + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + float deltaTime = GetFrameTime(); + + UpdateCamera(&camera, CAMERA_ORBITAL); + + // send the updated camera position to the shader so that it can calculate the correct lighting for the scene + Vector3 cameraPos = camera.position; + SetShaderValue(shadowShader, shadowShader.locs[SHADER_LOC_VECTOR_VIEW], &cameraPos, SHADER_UNIFORM_VEC3); + + frameCounter++; + frameCounter %= (anims[0].keyframeCount); + UpdateModelAnimation(robot, anims[0], (float)frameCounter); + + // Move light with arrow keys + const float cameraSpeed = 0.05f; + if (IsKeyDown(KEY_LEFT)) + { + if (lightDir.x < 0.6f) lightDir.x += cameraSpeed * 60.0f * deltaTime; + } + if (IsKeyDown(KEY_RIGHT)) + { + if (lightDir.x > -0.6f) lightDir.x -= cameraSpeed * 60.0f * deltaTime; + } + if (IsKeyDown(KEY_UP)) + { + if (lightDir.z < 0.6f) lightDir.z += cameraSpeed * 60.0f * deltaTime; + } + if (IsKeyDown(KEY_DOWN)) + { + if (lightDir.z > -0.6f) lightDir.z -= cameraSpeed * 60.0f * deltaTime; + } + + lightDir = Vector3Normalize(lightDir); + lightCamera.position = Vector3Scale(lightDir, -15.0f); + SetShaderValue(shadowShader, lightDirLoc, &lightDir, SHADER_UNIFORM_VEC3); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + // PASS 01: Render all objects into the shadowmap render texture + // 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 + BeginTextureMode(shadowMap); + ClearBackground(WHITE); + + BeginMode3D(lightCamera); + lightView = rlGetMatrixModelview(); + lightProj = rlGetMatrixProjection(); + DrawScene(cube, robot); + EndMode3D(); + + EndTextureMode(); + lightViewProj = MatrixMultiply(lightView, lightProj); + + // PASS 02: Draw the scene into main framebuffer, using the generated shadowmap + BeginDrawing(); + ClearBackground(RAYWHITE); + + SetShaderValueMatrix(shadowShader, lightVPLoc, lightViewProj); + rlEnableShader(shadowShader.id); + + rlActiveTextureSlot(textureActiveSlot); + rlEnableTexture(shadowMap.depth.id); + rlSetUniform(shadowMapLoc, &textureActiveSlot, SHADER_UNIFORM_INT, 1); + + BeginMode3D(camera); + DrawScene(cube, robot); // Draw the same exact things as we drew in the shadowmap! + EndMode3D(); + + DrawText("Use the arrow keys to rotate the light!", 10, 10, 30, RED); + DrawText("Shadows in raylib using the shadowmapping algorithm!", screenWidth - 280, screenHeight - 20, 10, GRAY); + + EndDrawing(); + + if (IsKeyPressed(KEY_F)) TakeScreenshot("shaders_shadowmap.png"); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadShader(shadowShader); + UnloadModel(cube); + UnloadModel(robot); + UnloadModelAnimations(anims, animCount); + UnloadShadowmapRenderTexture(shadowMap); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; } // Load render texture for shadowmap projection @@ -205,53 +207,53 @@ int main(void) // no color attachment required for shadowmap static RenderTexture2D LoadShadowmapRenderTexture(int width, int height) { - RenderTexture2D target = { 0 }; + RenderTexture2D target = { 0 }; - target.id = rlLoadFramebuffer(); // Load an empty framebuffer - target.texture.width = width; - target.texture.height = height; + target.id = rlLoadFramebuffer(); // Load an empty framebuffer + target.texture.width = width; + target.texture.height = height; - if (target.id > 0) - { - rlEnableFramebuffer(target.id); + if (target.id > 0) + { + rlEnableFramebuffer(target.id); - // Create depth texture - // NOTE: No need a color texture attachment 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; + // Create depth texture + // NOTE: No need a color texture attachment 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); + // 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); + // 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"); + rlDisableFramebuffer(); + } + else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created"); - return target; + return target; } // Unload shadowmap render texture from GPU memory (VRAM) static void UnloadShadowmapRenderTexture(RenderTexture2D target) { - if (target.id > 0) - { - // NOTE: Depth texture/renderbuffer is automatically - // queried and deleted before deleting framebuffer - rlUnloadFramebuffer(target.id); - } + if (target.id > 0) + { + // NOTE: Depth texture/renderbuffer is automatically + // queried and deleted before deleting framebuffer + rlUnloadFramebuffer(target.id); + } } // Draw full scene projecting shadows // NOTE: Required to be called several time to generate shadowmap static 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); + 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); } From eaf19a971abb8793fdcb1b8a8ae19644e9422999 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Fri, 7 Aug 2026 21:03:27 -0700 Subject: [PATCH 2/2] tabs to spaces (oops) --- .../shaders/shaders_shadowmap_rendering.c | 388 +++++++++--------- 1 file changed, 194 insertions(+), 194 deletions(-) diff --git a/examples/shaders/shaders_shadowmap_rendering.c b/examples/shaders/shaders_shadowmap_rendering.c index 6c18cafe4d56..e5470940a1b2 100644 --- a/examples/shaders/shaders_shadowmap_rendering.c +++ b/examples/shaders/shaders_shadowmap_rendering.c @@ -37,169 +37,169 @@ static void DrawScene(Model cube, Model robot); //------------------------------------------------------------------------------------ int main(void) { - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - // 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 - - SetConfigFlags(FLAG_MSAA_4X_HINT); - InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shadowmap rendering"); - - Camera3D camera = (Camera3D){ 0 }; - camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; - camera.target = Vector3Zero(); - camera.projection = CAMERA_PERSPECTIVE; - camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; - camera.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* anims = 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 lightCamera = { 0 }; - lightCamera.position = Vector3Scale(lightDir, -15.0f); - lightCamera.target = Vector3Zero(); - lightCamera.projection = CAMERA_ORTHOGRAPHIC; // Use an orthographic projection for directional lights - lightCamera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; - lightCamera.fovy = 20.0f; - - int frameCounter = 0; - - // Store the light matrices - Matrix lightView = { 0 }; - Matrix lightProj = { 0 }; - Matrix lightViewProj = { 0 }; - int textureActiveSlot = 10; // Can be anything 0 to 15, but 0 will probably be taken up - - SetTargetFPS(60); - //-------------------------------------------------------------------------------------- - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - float deltaTime = GetFrameTime(); - - UpdateCamera(&camera, CAMERA_ORBITAL); - - // send the updated camera position to the shader so that it can calculate the correct lighting for the scene - Vector3 cameraPos = camera.position; - SetShaderValue(shadowShader, shadowShader.locs[SHADER_LOC_VECTOR_VIEW], &cameraPos, SHADER_UNIFORM_VEC3); - - frameCounter++; - frameCounter %= (anims[0].keyframeCount); - UpdateModelAnimation(robot, anims[0], (float)frameCounter); - - // Move light with arrow keys - const float cameraSpeed = 0.05f; - if (IsKeyDown(KEY_LEFT)) - { - if (lightDir.x < 0.6f) lightDir.x += cameraSpeed * 60.0f * deltaTime; - } - if (IsKeyDown(KEY_RIGHT)) - { - if (lightDir.x > -0.6f) lightDir.x -= cameraSpeed * 60.0f * deltaTime; - } - if (IsKeyDown(KEY_UP)) - { - if (lightDir.z < 0.6f) lightDir.z += cameraSpeed * 60.0f * deltaTime; - } - if (IsKeyDown(KEY_DOWN)) - { - if (lightDir.z > -0.6f) lightDir.z -= cameraSpeed * 60.0f * deltaTime; - } - - lightDir = Vector3Normalize(lightDir); - lightCamera.position = Vector3Scale(lightDir, -15.0f); - SetShaderValue(shadowShader, lightDirLoc, &lightDir, SHADER_UNIFORM_VEC3); - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - // PASS 01: Render all objects into the shadowmap render texture - // 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 - BeginTextureMode(shadowMap); - ClearBackground(WHITE); - - BeginMode3D(lightCamera); - lightView = rlGetMatrixModelview(); - lightProj = rlGetMatrixProjection(); - DrawScene(cube, robot); - EndMode3D(); - - EndTextureMode(); - lightViewProj = MatrixMultiply(lightView, lightProj); - - // PASS 02: Draw the scene into main framebuffer, using the generated shadowmap - BeginDrawing(); - ClearBackground(RAYWHITE); - - SetShaderValueMatrix(shadowShader, lightVPLoc, lightViewProj); - rlEnableShader(shadowShader.id); - - rlActiveTextureSlot(textureActiveSlot); - rlEnableTexture(shadowMap.depth.id); - rlSetUniform(shadowMapLoc, &textureActiveSlot, SHADER_UNIFORM_INT, 1); - - BeginMode3D(camera); - DrawScene(cube, robot); // Draw the same exact things as we drew in the shadowmap! - EndMode3D(); - - DrawText("Use the arrow keys to rotate the light!", 10, 10, 30, RED); - DrawText("Shadows in raylib using the shadowmapping algorithm!", screenWidth - 280, screenHeight - 20, 10, GRAY); - - EndDrawing(); - - if (IsKeyPressed(KEY_F)) TakeScreenshot("shaders_shadowmap.png"); - //---------------------------------------------------------------------------------- - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadShader(shadowShader); - UnloadModel(cube); - UnloadModel(robot); - UnloadModelAnimations(anims, animCount); - UnloadShadowmapRenderTexture(shadowMap); - - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + // 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 + + SetConfigFlags(FLAG_MSAA_4X_HINT); + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shadowmap rendering"); + + Camera3D camera = (Camera3D){ 0 }; + camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; + camera.target = Vector3Zero(); + camera.projection = CAMERA_PERSPECTIVE; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.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* anims = 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 lightCamera = { 0 }; + lightCamera.position = Vector3Scale(lightDir, -15.0f); + lightCamera.target = Vector3Zero(); + lightCamera.projection = CAMERA_ORTHOGRAPHIC; // Use an orthographic projection for directional lights + lightCamera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + lightCamera.fovy = 20.0f; + + int frameCounter = 0; + + // Store the light matrices + Matrix lightView = { 0 }; + Matrix lightProj = { 0 }; + Matrix lightViewProj = { 0 }; + int textureActiveSlot = 10; // Can be anything 0 to 15, but 0 will probably be taken up + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + float deltaTime = GetFrameTime(); + + UpdateCamera(&camera, CAMERA_ORBITAL); + + // send the updated camera position to the shader so that it can calculate the correct lighting for the scene + Vector3 cameraPos = camera.position; + SetShaderValue(shadowShader, shadowShader.locs[SHADER_LOC_VECTOR_VIEW], &cameraPos, SHADER_UNIFORM_VEC3); + + frameCounter++; + frameCounter %= (anims[0].keyframeCount); + UpdateModelAnimation(robot, anims[0], (float)frameCounter); + + // Move light with arrow keys + const float cameraSpeed = 0.05f; + if (IsKeyDown(KEY_LEFT)) + { + if (lightDir.x < 0.6f) lightDir.x += cameraSpeed * 60.0f * deltaTime; + } + if (IsKeyDown(KEY_RIGHT)) + { + if (lightDir.x > -0.6f) lightDir.x -= cameraSpeed * 60.0f * deltaTime; + } + if (IsKeyDown(KEY_UP)) + { + if (lightDir.z < 0.6f) lightDir.z += cameraSpeed * 60.0f * deltaTime; + } + if (IsKeyDown(KEY_DOWN)) + { + if (lightDir.z > -0.6f) lightDir.z -= cameraSpeed * 60.0f * deltaTime; + } + + lightDir = Vector3Normalize(lightDir); + lightCamera.position = Vector3Scale(lightDir, -15.0f); + SetShaderValue(shadowShader, lightDirLoc, &lightDir, SHADER_UNIFORM_VEC3); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + // PASS 01: Render all objects into the shadowmap render texture + // 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 + BeginTextureMode(shadowMap); + ClearBackground(WHITE); + + BeginMode3D(lightCamera); + lightView = rlGetMatrixModelview(); + lightProj = rlGetMatrixProjection(); + DrawScene(cube, robot); + EndMode3D(); + + EndTextureMode(); + lightViewProj = MatrixMultiply(lightView, lightProj); + + // PASS 02: Draw the scene into main framebuffer, using the generated shadowmap + BeginDrawing(); + ClearBackground(RAYWHITE); + + SetShaderValueMatrix(shadowShader, lightVPLoc, lightViewProj); + rlEnableShader(shadowShader.id); + + rlActiveTextureSlot(textureActiveSlot); + rlEnableTexture(shadowMap.depth.id); + rlSetUniform(shadowMapLoc, &textureActiveSlot, SHADER_UNIFORM_INT, 1); + + BeginMode3D(camera); + DrawScene(cube, robot); // Draw the same exact things as we drew in the shadowmap! + EndMode3D(); + + DrawText("Use the arrow keys to rotate the light!", 10, 10, 30, RED); + DrawText("Shadows in raylib using the shadowmapping algorithm!", screenWidth - 280, screenHeight - 20, 10, GRAY); + + EndDrawing(); + + if (IsKeyPressed(KEY_F)) TakeScreenshot("shaders_shadowmap.png"); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadShader(shadowShader); + UnloadModel(cube); + UnloadModel(robot); + UnloadModelAnimations(anims, animCount); + UnloadShadowmapRenderTexture(shadowMap); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; } // Load render texture for shadowmap projection @@ -207,53 +207,53 @@ int main(void) // no color attachment required for shadowmap static RenderTexture2D LoadShadowmapRenderTexture(int width, int height) { - RenderTexture2D target = { 0 }; + RenderTexture2D target = { 0 }; - target.id = rlLoadFramebuffer(); // Load an empty framebuffer - target.texture.width = width; - target.texture.height = height; + target.id = rlLoadFramebuffer(); // Load an empty framebuffer + target.texture.width = width; + target.texture.height = height; - if (target.id > 0) - { - rlEnableFramebuffer(target.id); + if (target.id > 0) + { + rlEnableFramebuffer(target.id); - // Create depth texture - // NOTE: No need a color texture attachment 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; + // Create depth texture + // NOTE: No need a color texture attachment 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); + // 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); + // 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"); + rlDisableFramebuffer(); + } + else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created"); - return target; + return target; } // Unload shadowmap render texture from GPU memory (VRAM) static void UnloadShadowmapRenderTexture(RenderTexture2D target) { - if (target.id > 0) - { - // NOTE: Depth texture/renderbuffer is automatically - // queried and deleted before deleting framebuffer - rlUnloadFramebuffer(target.id); - } + if (target.id > 0) + { + // NOTE: Depth texture/renderbuffer is automatically + // queried and deleted before deleting framebuffer + rlUnloadFramebuffer(target.id); + } } // Draw full scene projecting shadows // NOTE: Required to be called several time to generate shadowmap static 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); + 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); }