Watch
2
0
Fork
You've already forked raylib-cs
0
raylib-cs/Examples/Textures/FramebufferRendering.cs

248 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 System.Numerics;
using static Raylib_cs.Raylib;
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;
}
}