246 lines
9.8 KiB
C#
246 lines
9.8 KiB
C#
/*******************************************************************************************
|
|
*
|
|
* 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 Jack Boakes (@jackboakes)
|
|
*
|
|
********************************************************************************************/
|
|
|
|
using static Raylib_cs.Raymath;
|
|
|
|
namespace Examples.Textures;
|
|
|
|
public partial class FramebufferRendering : IExample
|
|
{
|
|
private const int screenWidth = 800;
|
|
private const int screenHeight = 450;
|
|
private const int splitWidth = screenWidth / 2;
|
|
|
|
public string Name => "Textures / Framebuffer Rendering";
|
|
|
|
public string Title => "raylib [textures] example - framebuffer rendering";
|
|
|
|
public bool CursorDisabled => true;
|
|
|
|
private Camera3D subjectCamera;
|
|
private Camera3D observerCamera;
|
|
|
|
private RenderTexture2D observerTarget;
|
|
private Rectangle observerSource;
|
|
private Rectangle observerDest;
|
|
|
|
private RenderTexture2D subjectTarget;
|
|
private Rectangle subjectSource;
|
|
private Rectangle subjectDest;
|
|
private float textureAspectRatio;
|
|
|
|
private const float captureSize = 128.0f;
|
|
private Rectangle cropSource;
|
|
private Rectangle cropDest;
|
|
|
|
public void Init()
|
|
{
|
|
// Camera to look at the 3D world
|
|
subjectCamera = new Camera3D();
|
|
subjectCamera.Position = new Vector3(5.0f, 5.0f, 5.0f);
|
|
subjectCamera.Target = new Vector3(0.0f, 0.0f, 0.0f);
|
|
subjectCamera.Up = new Vector3(0.0f, 1.0f, 0.0f);
|
|
subjectCamera.FovY = 45.0f;
|
|
subjectCamera.Projection = CameraProjection.Perspective;
|
|
|
|
// Camera to observe the subject camera and 3D world
|
|
observerCamera = new Camera3D();
|
|
observerCamera.Position = new Vector3(10.0f, 10.0f, 10.0f);
|
|
observerCamera.Target = new Vector3(0.0f, 0.0f, 0.0f);
|
|
observerCamera.Up = new Vector3(0.0f, 1.0f, 0.0f);
|
|
observerCamera.FovY = 45.0f;
|
|
observerCamera.Projection = CameraProjection.Perspective;
|
|
|
|
// Set up render textures
|
|
observerTarget = LoadRenderTexture(splitWidth, screenHeight);
|
|
observerSource = new Rectangle(0.0f, 0.0f, observerTarget.Texture.Width, -observerTarget.Texture.Height);
|
|
observerDest = new Rectangle(0.0f, 0.0f, splitWidth, screenHeight);
|
|
|
|
subjectTarget = LoadRenderTexture(splitWidth, screenHeight);
|
|
subjectSource = new Rectangle(0.0f, 0.0f, subjectTarget.Texture.Width, -subjectTarget.Texture.Height);
|
|
subjectDest = new Rectangle(splitWidth, 0.0f, splitWidth, screenHeight);
|
|
textureAspectRatio = (float)subjectTarget.Texture.Width / subjectTarget.Texture.Height;
|
|
|
|
// Rectangles for cropping render texture
|
|
cropSource = new Rectangle((subjectTarget.Texture.Width - captureSize) / 2.0f, (subjectTarget.Texture.Height - captureSize) / 2.0f, captureSize, -captureSize);
|
|
cropDest = new Rectangle(splitWidth + 20.0f, 20.0f, captureSize, captureSize);
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
// Update
|
|
//----------------------------------------------------------------------------------
|
|
UpdateCamera(ref observerCamera, CameraMode.Free);
|
|
UpdateCamera(ref subjectCamera, CameraMode.Orbital);
|
|
|
|
if (IsKeyPressed(KeyboardKey.R))
|
|
{
|
|
observerCamera.Target = new Vector3(0.0f, 0.0f, 0.0f);
|
|
}
|
|
|
|
// Build LHS observer view texture
|
|
BeginTextureMode(observerTarget);
|
|
|
|
ClearBackground(Color.RayWhite);
|
|
|
|
BeginMode3D(observerCamera);
|
|
|
|
DrawGrid(10, 1.0f);
|
|
DrawCube(new Vector3(0.0f, 0.0f, 0.0f), 2.0f, 2.0f, 2.0f, Color.Gold);
|
|
DrawCubeWires(new Vector3(0.0f, 0.0f, 0.0f), 2.0f, 2.0f, 2.0f, Color.Pink);
|
|
DrawCameraPrism(subjectCamera, textureAspectRatio, Color.Green);
|
|
|
|
EndMode3D();
|
|
|
|
DrawText("Observer View", 10, observerTarget.Texture.Height - 30, 20, Color.Black);
|
|
DrawText("WASD + Mouse to Move", 10, 10, 20, Color.DarkGray);
|
|
DrawText("Scroll to Zoom", 10, 30, 20, Color.DarkGray);
|
|
DrawText("R to Reset Observer Target", 10, 50, 20, Color.DarkGray);
|
|
|
|
EndTextureMode();
|
|
|
|
// Build RHS subject view texture
|
|
BeginTextureMode(subjectTarget);
|
|
|
|
ClearBackground(Color.RayWhite);
|
|
|
|
BeginMode3D(subjectCamera);
|
|
|
|
DrawCube(new Vector3(0.0f, 0.0f, 0.0f), 2.0f, 2.0f, 2.0f, Color.Gold);
|
|
DrawCubeWires(new Vector3(0.0f, 0.0f, 0.0f), 2.0f, 2.0f, 2.0f, Color.Pink);
|
|
DrawGrid(10, 1.0f);
|
|
|
|
EndMode3D();
|
|
|
|
DrawRectangleLines((int)((subjectTarget.Texture.Width - captureSize) / 2.0f), (int)((subjectTarget.Texture.Height - captureSize) / 2.0f), (int)captureSize, (int)captureSize, Color.Green);
|
|
DrawText("Subject View", 10, subjectTarget.Texture.Height - 30, 20, Color.Black);
|
|
|
|
EndTextureMode();
|
|
//----------------------------------------------------------------------------------
|
|
|
|
// Draw
|
|
//----------------------------------------------------------------------------------
|
|
BeginDrawing();
|
|
|
|
ClearBackground(Color.Black);
|
|
|
|
// Draw observer texture LHS
|
|
DrawTexturePro(observerTarget.Texture, observerSource, observerDest, new Vector2(0.0f, 0.0f), 0.0f, Color.White);
|
|
|
|
// Draw subject texture RHS
|
|
DrawTexturePro(subjectTarget.Texture, subjectSource, subjectDest, new Vector2(0.0f, 0.0f), 0.0f, Color.White);
|
|
|
|
// Draw the small crop overlay on top
|
|
DrawTexturePro(subjectTarget.Texture, cropSource, cropDest, new Vector2(0.0f, 0.0f), 0.0f, Color.White);
|
|
DrawRectangleLinesEx(cropDest, 2, Color.Black);
|
|
|
|
// Draw split screen divider line
|
|
DrawLine(splitWidth, 0, splitWidth, screenHeight, Color.Black);
|
|
|
|
EndDrawing();
|
|
//----------------------------------------------------------------------------------
|
|
}
|
|
|
|
public void Unload()
|
|
{
|
|
UnloadRenderTexture(observerTarget);
|
|
UnloadRenderTexture(subjectTarget);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// Module Functions Definition
|
|
//----------------------------------------------------------------------------------
|
|
private 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 =
|
|
{
|
|
new(-1.0f, -1.0f, 1.0f), // Bottom Left
|
|
new( 1.0f, -1.0f, 1.0f), // Bottom Right
|
|
new( 1.0f, 1.0f, 1.0f), // Top Right
|
|
new(-1.0f, 1.0f, 1.0f) // Top Left
|
|
};
|
|
|
|
// Build the matrices
|
|
Matrix4x4 view = GetCameraMatrix(camera);
|
|
Matrix4x4 proj = MatrixPerspective(camera.FovY * DEG2RAD, aspect, 0.05f, length);
|
|
// Combine view and projection so we can reverse the full camera transform
|
|
Matrix4x4 viewProj = MatrixMultiply(view, proj);
|
|
// Invert the view-projection matrix to unproject points from NDC space back into world space
|
|
Matrix4x4 inverseViewProj = MatrixInvert(viewProj);
|
|
|
|
// Transform the 4 plane corners from NDC into world space
|
|
Vector3[] corners = new Vector3[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.M11 * x + inverseViewProj.M12 * y + inverseViewProj.M13 * z + inverseViewProj.M14;
|
|
float vy = inverseViewProj.M21 * x + inverseViewProj.M22 * y + inverseViewProj.M23 * z + inverseViewProj.M24;
|
|
float vz = inverseViewProj.M31 * x + inverseViewProj.M32 * y + inverseViewProj.M33 * z + inverseViewProj.M34;
|
|
float vw = inverseViewProj.M41 * x + inverseViewProj.M42 * y + inverseViewProj.M43 * z + inverseViewProj.M44;
|
|
|
|
corners[i] = new 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);
|
|
}
|
|
}
|
|
|
|
public static int Main()
|
|
{
|
|
// Initialization
|
|
//--------------------------------------------------------------------------------------
|
|
InitWindow(screenWidth, screenHeight, "raylib [textures] example - framebuffer rendering");
|
|
|
|
SetTargetFPS(60);
|
|
DisableCursor();
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
var game = new FramebufferRendering();
|
|
game.Init();
|
|
|
|
// Main game loop
|
|
while (!WindowShouldClose()) // Detect window close button or ESC key
|
|
{
|
|
game.Update();
|
|
}
|
|
|
|
game.Unload();
|
|
|
|
// De-Initialization
|
|
//--------------------------------------------------------------------------------------
|
|
CloseWindow(); // Close window and OpenGL context
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
return 0;
|
|
}
|
|
}
|