From a44157c2a834c4956e98afafbced96e25177b1a5 Mon Sep 17 00:00:00 2001 From: Jack Boakes <163384444+jackboakes@users.noreply.github.com> Date: Sun, 4 Jan 2026 08:41:52 +1100 Subject: [PATCH] [example] Added textures_frame_buffer_rendering (#5468) --- .../textures_frame_buffer_rendering.png | Bin 0 -> 31588 bytes .../textures/textures_framebuffer_rendering.c | 208 ++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 examples/textures/textures_frame_buffer_rendering.png create mode 100644 examples/textures/textures_framebuffer_rendering.c diff --git a/examples/textures/textures_frame_buffer_rendering.png b/examples/textures/textures_frame_buffer_rendering.png new file mode 100644 index 0000000000000000000000000000000000000000..e6829f0bd20ef747b29a0fb868ca5f2c4cc635e5 GIT binary patch literal 31588 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 Duq`3E literal 0 HcmV?d00001 diff --git a/examples/textures/textures_framebuffer_rendering.c b/examples/textures/textures_framebuffer_rendering.c new file mode 100644 index 000000000..a8b466187 --- /dev/null +++ b/examples/textures/textures_framebuffer_rendering.c @@ -0,0 +1,208 @@ +/******************************************************************************************* +* +* raylib [textures] example - framebuffer rendering +* +* Example complexity rating: [★★☆☆] 2/4 +* +* Example originally created with raylib 5.6, last time updated with raylib 5.6 +* +* Example contributed by Jack Boakes (@jackboakes) 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) 2026-2026 Jack Boakes (@jackboakes) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" + +//------------------------------------------------------------------------------------ +// Module Functions Declaration +//------------------------------------------------------------------------------------ +static void DrawCameraPrism(Camera3D camera, float aspect, Color color); + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + const int splitWidth = screenWidth/2; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - framebuffer rendering"); + + // Camera to look at the 3D world + Camera3D subjectCamera = { 0 }; + subjectCamera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; + subjectCamera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + subjectCamera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + subjectCamera.fovy = 45.0f; + subjectCamera.projection = CAMERA_PERSPECTIVE; + + // Camera to observe the subject camera and 3D world + Camera3D observerCamera = { 0 }; + observerCamera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; + observerCamera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + observerCamera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + observerCamera.fovy = 45.0f; + observerCamera.projection = CAMERA_PERSPECTIVE; + + // Set up render textures + RenderTexture2D observerTarget = LoadRenderTexture(splitWidth, screenHeight); + Rectangle observerSource = { 0.0f, 0.0f, (float)observerTarget.texture.width, -(float)observerTarget.texture.height }; + Rectangle observerDest = { 0.0f, 0.0f, (float)splitWidth, (float)screenHeight }; + + RenderTexture2D subjectTarget = LoadRenderTexture(splitWidth, screenHeight); + Rectangle subjectSource = { 0.0f, 0.0f, (float)subjectTarget.texture.width, -(float)subjectTarget.texture.height }; + Rectangle subjectDest = { (float)splitWidth, 0.0f, (float)splitWidth, (float)screenHeight }; + const float textureAspectRatio = (float)subjectTarget.texture.width/(float)subjectTarget.texture.height; + + // Rectangles for cropping render texture + const float captureSize = 128.0f; + Rectangle cropSource = { (subjectTarget.texture.width - captureSize)/2.0f, (subjectTarget.texture.height - captureSize)/2.0f, captureSize, -captureSize }; + Rectangle cropDest = { splitWidth + 20, 20, captureSize, captureSize}; + + SetTargetFPS(60); + DisableCursor(); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&observerCamera, CAMERA_FREE); + UpdateCamera(&subjectCamera, CAMERA_ORBITAL); + + if (IsKeyPressed(KEY_R)) observerCamera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + + // Build LHS observer view texture + BeginTextureMode(observerTarget); + + ClearBackground(RAYWHITE); + + BeginMode3D(observerCamera); + + DrawGrid(10, 1.0f); + DrawCube((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, GOLD); + DrawCubeWires((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, PINK); + DrawCameraPrism(subjectCamera, textureAspectRatio, GREEN); + + EndMode3D(); + + DrawText("Observer View", 10, observerTarget.texture.height - 30, 20, BLACK); + DrawText("WASD + Mouse to Move", 10, 10, 20, DARKGRAY); + DrawText("Scroll to Zoom", 10, 30, 20, DARKGRAY); + DrawText("R to Reset Observer Target", 10, 50, 20, DARKGRAY); + + EndTextureMode(); + + // Build RHS subject view texture + BeginTextureMode(subjectTarget); + + ClearBackground(RAYWHITE); + + BeginMode3D(subjectCamera); + + DrawCube((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, GOLD); + DrawCubeWires((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, PINK); + DrawGrid(10, 1.0f); + + EndMode3D(); + + DrawRectangleLines((subjectTarget.texture.width - captureSize)/2, (subjectTarget.texture.height - captureSize)/2, captureSize, captureSize, GREEN); + DrawText("Subject View", 10, subjectTarget.texture.height - 30, 20, BLACK); + + EndTextureMode(); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(BLACK); + + // Draw observer texture LHS + DrawTexturePro(observerTarget.texture, observerSource, observerDest, (Vector2){0.0f, 0.0f }, 0.0f, WHITE); + + // Draw subject texture RHS + DrawTexturePro(subjectTarget.texture, subjectSource, subjectDest, (Vector2){ 0.0f, 0.0f }, 0.0f, WHITE); + + // Draw the small crop overlay on top + DrawTexturePro(subjectTarget.texture, cropSource, cropDest, (Vector2){ 0.0f, 0.0f }, 0.0f, WHITE); + DrawRectangleLinesEx(cropDest, 2, BLACK); + + // Draw split screen divider line + DrawLine(splitWidth, 0, splitWidth, screenHeight, BLACK); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTexture(observerTarget); + UnloadRenderTexture(subjectTarget); + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +static void DrawCameraPrism(Camera3D camera, float aspect, Color color) +{ + float length = Vector3Distance(camera.position, camera.target); + // Define the 4 corners of the camera's prism plane sliced at the target in Normalized Device Coordinates + Vector3 planeNDC[4] = { + { -1.0f, -1.0f, 1.0f }, // Bottom Left + { 1.0f, -1.0f, 1.0f }, // Bottom Right + { 1.0f, 1.0f, 1.0f }, // Top Right + { -1.0f, 1.0f, 1.0f } // Top Left + }; + + // Build the matrices + Matrix view = GetCameraMatrix(camera); + Matrix proj = MatrixPerspective(camera.fovy * DEG2RAD, aspect, 0.05f, length); + // Combine view and projection so we can reverse the full camera transform + Matrix viewProj = MatrixMultiply(view, proj); + // Invert the view-projection matrix to unproject points from NDC space back into world space + Matrix inverseViewProj = MatrixInvert(viewProj); + + // Transform the 4 plane corners from NDC into world space + Vector3 corners[4]; + for (int i = 0; i < 4; i++) + { + float x = planeNDC[i].x; + float y = planeNDC[i].y; + float z = planeNDC[i].z; + + // Multiply NDC position by the inverse view-projection matrix + // This produces a homogeneous (x, y, z, w) position in world space + float vx = inverseViewProj.m0*x + inverseViewProj.m4*y + inverseViewProj.m8*z + inverseViewProj.m12; + float vy = inverseViewProj.m1*x + inverseViewProj.m5*y + inverseViewProj.m9*z + inverseViewProj.m13; + float vz = inverseViewProj.m2*x + inverseViewProj.m6*y + inverseViewProj.m10*z + inverseViewProj.m14; + float vw = inverseViewProj.m3*x + inverseViewProj.m7*y + inverseViewProj.m11*z + inverseViewProj.m15; + + corners[i] = (Vector3){ vx/vw, vy/vw, vz/vw }; + } + + // Draw the far plane sliced at the target + DrawLine3D(corners[0], corners[1], color); + DrawLine3D(corners[1], corners[2], color); + DrawLine3D(corners[2], corners[3], color); + DrawLine3D(corners[3], corners[0], color); + + // Draw the prism lines from the far plane to the camera position + for (int i = 0; i < 4; i++) + { + DrawLine3D(camera.position, corners[i], color); + } +} \ No newline at end of file