Watch
2
0
Fork
You've already forked raylib-cs
0

chore: clean recommit

This commit is contained in:
tiger tiger tiger 2026-07-07 23:47:38 +02:00
commit 60ad2e7fb1
122 changed files with 23950 additions and 323 deletions

View file

@ -0,0 +1,166 @@
/*******************************************************************************************
*
* raylib [shaders] example - ascii rendering
*
* Example complexity rating: [] 2/4
*
* Example originally created with raylib 5.5, last time updated with raylib 6.0
*
* Example contributed by Maicon Santana (@maiconpintoabreu) 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) 2025 Maicon Santana (@maiconpintoabreu)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public partial class AsciiRendering : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Ascii Rendering";
public string Title => "raylib [shaders] example - ascii rendering";
private Texture2D fudesumi;
private Texture2D raysan;
private Shader shader;
private int resolutionLoc;
private int fontSizeLoc;
private float fontSize;
private Vector2 circlePos;
private float circleSpeed;
private RenderTexture2D target;
public void Init()
{
// Texture to test static drawing
fudesumi = LoadTexture("resources/fudesumi.png");
// Texture to test moving drawing
raysan = LoadTexture("resources/raysan.png");
// Load shader to be used on postprocessing
shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/ascii.fs");
// These locations are used to send data to the GPU
resolutionLoc = GetShaderLocation(shader, "resolution");
fontSizeLoc = GetShaderLocation(shader, "fontSize");
// Set the character size for the ASCII effect
// Fontsize should be 9 or more
fontSize = 9.0f;
// Send the updated values to the shader
var resolution = new[] { (float)screenWidth, (float)screenHeight };
Raylib.SetShaderValue(shader, resolutionLoc, resolution, ShaderUniformDataType.Vec2);
circlePos = new Vector2(40.0f, screenHeight * 0.5f);
circleSpeed = 1.0f;
// RenderTexture to apply the postprocessing later
target = LoadRenderTexture(screenWidth, screenHeight);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
circlePos.X += circleSpeed;
if ((circlePos.X > 200.0f) || (circlePos.X < 40.0f))
{
circleSpeed *= -1; // Revert speed
}
if (IsKeyPressed(KeyboardKey.Left) && (fontSize > 9.0))
{
fontSize -= 1; // Reduce fontSize
}
if (IsKeyPressed(KeyboardKey.Right) && (fontSize < 15.0))
{
fontSize += 1; // Increase fontSize
}
// Set fontsize for the shader
Raylib.SetShaderValue(shader, fontSizeLoc, fontSize, ShaderUniformDataType.Float);
// Draw
//----------------------------------------------------------------------------------
BeginTextureMode(target);
ClearBackground(Color.White);
// Draw scene in our render texture
DrawTexture(fudesumi, 500, -30, Color.White);
DrawTextureV(raysan, circlePos, Color.White);
EndTextureMode();
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginShaderMode(shader);
// Draw the scene texture (that we rendered earlier) to the screen
// The shader will process every pixel of this texture
DrawTextureRec(
target.Texture,
new Rectangle(0, 0, target.Texture.Width, -target.Texture.Height),
new Vector2(0, 0),
Color.White
);
EndShaderMode();
DrawRectangle(0, 0, screenWidth, 40, Color.Black);
DrawText($"Ascii effect - FontSize:{fontSize,2:F0} - [Left] -1 [Right] +1 ", 120, 10, 20, Color.LightGray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadRenderTexture(target); // Unload render texture
UnloadShader(shader); // Unload shader
UnloadTexture(fudesumi); // Unload texture
UnloadTexture(raysan); // Unload texture
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - ascii rendering");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new AsciiRendering();
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;
}
}

View file

@ -0,0 +1,252 @@
/*******************************************************************************************
*
* raylib [shaders] example - cel shading
*
* Example complexity rating: [] 3/4
*
* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
*
* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
*
* Example contributed by Gleb A (@ggrizzly) 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 Gleb A (@ggrizzly)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
using static Raylib_cs.Raymath;
using static Raylib_cs.Rlgl;
using Examples.Shared;
namespace Examples.Shaders;
public partial class CelShading : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int MaxLights = 4;
// rlgl cull face modes (rlgl.h: RL_CULL_FACE_FRONT = 0, RL_CULL_FACE_BACK = 1)
private const int CullFaceFront = 0;
private const int CullFaceBack = 1;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Cel Shading";
public string Title => "raylib [shaders] example - cel shading";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private Camera3D camera;
private Model model;
private Shader celShader;
private Shader defaultShader;
private Shader outlineShader;
private float numBands;
private int numBandsLoc;
private int outlineThicknessLoc;
private Light[] lights;
private bool celEnabled;
private bool outlineEnabled;
public unsafe void Init()
{
camera = new();
camera.Position = new Vector3(9.0f, 6.0f, 9.0f);
camera.Target = new Vector3(0.0f, 1.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
// Load model
model = LoadModel("resources/models/old_car_new.glb");
// Load cel shader
celShader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/cel.vs",
$"resources/shaders/glsl{GlslVersion}/cel.fs"
);
celShader.Locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(celShader, "viewPos");
// Apply cel shader to model, keep copy of default shader
defaultShader = model.Materials[0].Shader;
model.Materials[0].Shader = celShader;
// numBands: controls toon quantization steps (2 = hard binary, 20 = near-smooth)
numBands = 10.0f;
numBandsLoc = GetShaderLocation(celShader, "numBands");
Raylib.SetShaderValue(celShader, numBandsLoc, numBands, ShaderUniformDataType.Float);
// Inverted-hull outline shader: draws back faces extruded along normals
outlineShader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/outline_hull.vs",
$"resources/shaders/glsl{GlslVersion}/outline_hull.fs"
);
outlineThicknessLoc = GetShaderLocation(outlineShader, "outlineThickness");
// Single directional white light, angled so toon bands are visible on the model sides.
// Spins opposite to CAMERA_ORBITAL (0.5 rad/s) so lighting changes as you watch.
lights = new Light[MaxLights];
lights[0] = Rlights.CreateLight(
0,
LightType.Directorional,
new Vector3(50.0f, 50.0f, 50.0f),
Vector3.Zero,
Color.White,
celShader
);
celEnabled = true;
outlineEnabled = true;
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
Raylib.SetShaderValue(
celShader,
celShader.Locs[(int)ShaderLocationIndex.VectorView],
camera.Position,
ShaderUniformDataType.Vec3
);
// [Z] Toggle cel shading on/off
if (IsKeyPressed(KeyboardKey.Z))
{
celEnabled = !celEnabled;
if (celEnabled)
{
model.Materials[0].Shader = celShader; // Apply cel shader to model
}
else
{
model.Materials[0].Shader = defaultShader; // Apply default shader to model
}
}
// [C] Toggle outline on/off
if (IsKeyPressed(KeyboardKey.C))
{
outlineEnabled = !outlineEnabled;
}
// [Q/E] Decrease/increase toon band count (press or hold to repeat)
if (IsKeyPressed(KeyboardKey.E) || IsKeyPressedRepeat(KeyboardKey.E))
{
numBands = Clamp(numBands + 1.0f, 2.0f, 20.0f);
}
if (IsKeyPressed(KeyboardKey.Q) || IsKeyPressedRepeat(KeyboardKey.Q))
{
numBands = Clamp(numBands - 1.0f, 2.0f, 20.0f);
}
Raylib.SetShaderValue(celShader, numBandsLoc, numBands, ShaderUniformDataType.Float);
// Spin light opposite to CAMERA_ORBITAL (0.5 rad/s), angled 45 degrees off vertical
float t = (float)GetTime();
lights[0].Position = new Vector3(MathF.Sin(-t * 0.3f) * 5.0f, 5.0f, MathF.Cos(-t * 0.3f) * 5.0f);
for (var i = 0; i < MaxLights; i++)
{
Rlights.UpdateLightValues(celShader, lights[i]);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
if (outlineEnabled)
{
// Outline pass: cull front faces, draw extruded back faces as silhouette
float thickness = 0.005f;
Raylib.SetShaderValue(outlineShader, outlineThicknessLoc, thickness, ShaderUniformDataType.Float);
SetCullFace(CullFaceFront);
model.Materials[0].Shader = outlineShader;
DrawModel(model, Vector3.Zero, 0.75f, Color.White);
if (celEnabled)
{
model.Materials[0].Shader = celShader; // Apply cel shader to model
}
else
{
model.Materials[0].Shader = defaultShader; // Apply default shader to model
}
SetCullFace(CullFaceBack);
}
DrawModel(model, Vector3.Zero, 0.75f, Color.White);
DrawSphereEx(lights[0].Position, 0.2f, 50, 50, Color.Yellow); // Light position indicator
DrawGrid(10, 10.0f);
EndMode3D();
DrawFPS(10, 10);
DrawText($"Cel: {(celEnabled ? "ON" : "OFF")} [Z]", 10, 65, 20, celEnabled ? Color.DarkGreen : Color.DarkGray);
DrawText($"Outline: {(outlineEnabled ? "ON" : "OFF")} [C]", 10, 90, 20, outlineEnabled ? Color.DarkGreen : Color.DarkGray);
DrawText($"Bands: {numBands:0} [Q/E]", 10, 115, 20, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(model);
UnloadShader(celShader);
UnloadShader(outlineShader);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - cel shading");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new CelShading();
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;
}
}

View file

@ -0,0 +1,184 @@
/*******************************************************************************************
*
* raylib [shaders] example - color correction
*
* Example complexity rating: [] 2/4
*
* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Jordi Santonja (@JordSant) 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) 2025 Jordi Santonja (@JordSant)
*
********************************************************************************************/
using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public partial class ColorCorrection : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
private const int MaxTextures = 4;
public string Name => "Shaders / Color Correction";
public string Title => "raylib [shaders] example - color correction";
private Texture2D[] texture;
private Shader shdrColorCorrection;
private int imageIndex;
private int resetButtonClicked;
private float contrast;
private float saturation;
private float brightness;
private int contrastLoc;
private int saturationLoc;
private int brightnessLoc;
public void Init()
{
texture = new[]
{
LoadTexture("resources/parrots.png"),
LoadTexture("resources/cat.png"),
LoadTexture("resources/mandrill.png"),
LoadTexture("resources/fudesumi.png")
};
shdrColorCorrection = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/color_correction.fs");
imageIndex = 0;
resetButtonClicked = 0;
contrast = 0.0f;
saturation = 0.0f;
brightness = 0.0f;
// Get shader locations
contrastLoc = GetShaderLocation(shdrColorCorrection, "contrast");
saturationLoc = GetShaderLocation(shdrColorCorrection, "saturation");
brightnessLoc = GetShaderLocation(shdrColorCorrection, "brightness");
// Set shader values (they can be changed later)
Raylib.SetShaderValue(shdrColorCorrection, contrastLoc, contrast, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shdrColorCorrection, saturationLoc, saturation, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shdrColorCorrection, brightnessLoc, brightness, ShaderUniformDataType.Float);
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Select texture to draw
if (IsKeyPressed(KeyboardKey.One)) imageIndex = 0;
else if (IsKeyPressed(KeyboardKey.Two)) imageIndex = 1;
else if (IsKeyPressed(KeyboardKey.Three)) imageIndex = 2;
else if (IsKeyPressed(KeyboardKey.Four)) imageIndex = 3;
// Reset values to 0
if (IsKeyPressed(KeyboardKey.R) || resetButtonClicked != 0)
{
contrast = 0.0f;
saturation = 0.0f;
brightness = 0.0f;
}
// Send the values to the shader
Raylib.SetShaderValue(shdrColorCorrection, contrastLoc, contrast, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shdrColorCorrection, saturationLoc, saturation, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shdrColorCorrection, brightnessLoc, brightness, ShaderUniformDataType.Float);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginShaderMode(shdrColorCorrection);
DrawTexture(texture[imageIndex], 580 / 2 - texture[imageIndex].Width / 2, GetScreenHeight() / 2 - texture[imageIndex].Height / 2, Color.White);
EndShaderMode();
DrawLine(580, 0, 580, GetScreenHeight(), new Color(218, 218, 218, 255));
DrawRectangle(580, 0, GetScreenWidth(), GetScreenHeight(), new Color(232, 232, 232, 255));
// Draw UI info text
DrawText("Color Correction", 585, 40, 20, Color.Gray);
DrawText("Picture", 602, 75, 10, Color.Gray);
DrawText("Press [1] - [4] to Change Picture", 600, 230, 8, Color.Gray);
DrawText("Press [R] to Reset Values", 600, 250, 8, Color.Gray);
// Draw GUI controls
//------------------------------------------------------------------------------
// NOTE: raygui is not bound in raylib-cs; controls are kept for reference. Use
// keyboard shortcuts ([1]-[4], [R]) to interact with the example.
/*GuiToggleGroup(new Rectangle( 645, 70, 20, 20 ), "1;2;3;4", ref imageIndex);
GuiSliderBar(new Rectangle( 645, 100, 120, 20 ), "Contrast", TextFormat("%.0f", contrast), ref contrast, -100.0f, 100.0f);
GuiSliderBar(new Rectangle( 645, 130, 120, 20 ), "Saturation", TextFormat("%.0f", saturation), ref saturation, -100.0f, 100.0f);
GuiSliderBar(new Rectangle( 645, 160, 120, 20 ), "Brightness", TextFormat("%.0f", brightness), ref brightness, -100.0f, 100.0f);
resetButtonClicked = GuiButton(new Rectangle( 645, 190, 40, 20 ), "Reset");*/
//------------------------------------------------------------------------------
DrawFPS(710, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
for (var i = 0; i < MaxTextures; i++)
{
UnloadTexture(texture[i]);
}
UnloadShader(shdrColorCorrection);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - color correction");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new ColorCorrection();
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;
}
}

View file

@ -0,0 +1,400 @@
/*******************************************************************************************
*
* raylib [shaders] example - deferred rendering
*
* Example complexity rating: [] 4/4
*
* NOTE: This example requires raylib OpenGL 3.3 or OpenGL ES 3.0
*
* Example originally created with raylib 4.5, last time updated with raylib 4.5
*
* Example contributed by Justin Andreas Lacoste (@27justin) 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 Justin Andreas Lacoste (@27justin)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
using Examples.Shared;
namespace Examples.Shaders;
[ExcludeFromBrowser("multiple-render-target G-buffer, unsupported on WebGL1/GLSL100")]
public partial class DeferredRendering : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
private const int MaxCubes = 30;
private const int MaxLights = 4;
private const float CubeScale = 0.25f;
// GL_READ_FRAMEBUFFER / GL_DRAW_FRAMEBUFFER / GL_DEPTH_BUFFER_BIT
private const uint RlReadFramebuffer = 0x8CA8;
private const uint RlDrawFramebuffer = 0x8CA9;
private const int GlDepthBufferBit = 0x00000100;
public string Name => "Shaders / Deferred Rendering";
public string Title => "raylib [shaders] example - deferred rendering";
// GBuffer data
private struct GBuffer
{
public uint FramebufferId;
public uint PositionTextureId;
public uint NormalTextureId;
public uint AlbedoSpecTextureId;
public uint DepthRenderbufferId;
}
// Deferred mode passes
private enum DeferredMode
{
Position,
Normal,
Albedo,
Shading
}
private Camera3D camera;
private Model model;
private Model cube;
private Shader gbufferShader;
private Shader deferredShader;
private GBuffer gBuffer;
private Light[] lights;
private Vector3[] cubePositions;
private float[] cubeRotations;
private DeferredMode mode;
// Texture units our g-buffer textures are bound to
private const int TexUnitPosition = 0;
private const int TexUnitNormal = 1;
private const int TexUnitAlbedoSpec = 2;
public unsafe void Init()
{
camera = new();
camera.Position = new Vector3(5.0f, 4.0f, 5.0f); // Camera position
camera.Target = new Vector3(0.0f, 1.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 60.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Load plane model from a generated mesh
model = LoadModelFromMesh(GenMeshPlane(10.0f, 10.0f, 3, 3));
cube = LoadModelFromMesh(GenMeshCube(2.0f, 2.0f, 2.0f));
// Load geometry buffer (G-buffer) shader and deferred shader
gbufferShader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/gbuffer.vs",
$"resources/shaders/glsl{GlslVersion}/gbuffer.fs"
);
deferredShader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/deferred_shading.vs",
$"resources/shaders/glsl{GlslVersion}/deferred_shading.fs"
);
deferredShader.Locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(deferredShader, "viewPosition");
// Initialize the G-buffer
gBuffer = new();
gBuffer.FramebufferId = Rlgl.LoadFramebuffer();
if (gBuffer.FramebufferId == 0)
{
TraceLog(TraceLogLevel.Warning, "Failed to create framebufferId");
}
Rlgl.EnableFramebuffer(gBuffer.FramebufferId);
// NOTE: Vertex positions are stored in a texture for simplicity. A better approach would use a depth texture
// (instead of a detph renderbuffer) to reconstruct world positions in the final render shader via clip-space position,
// depth, and the inverse view/projection matrices
// 16-bit precision ensures OpenGL ES 3 compatibility, though it may lack precision for real scenarios
gBuffer.PositionTextureId = Rlgl.LoadTexture(null, screenWidth, screenHeight, PixelFormat.UncompressedR16G16B16, 1);
// Similarly, 16-bit precision is used for normals ensures OpenGL ES 3 compatibility
gBuffer.NormalTextureId = Rlgl.LoadTexture(null, screenWidth, screenHeight, PixelFormat.UncompressedR16G16B16, 1);
// Albedo (diffuse color) and specular strength can be combined into one texture
// The color in RGB, and the specular strength in the alpha channel
gBuffer.AlbedoSpecTextureId = Rlgl.LoadTexture(null, screenWidth, screenHeight, PixelFormat.UncompressedR8G8B8A8, 1);
// Activate the draw buffers for our framebufferId
Rlgl.ActiveDrawBuffers(3);
// Now we attach our textures to the framebufferId
Rlgl.FramebufferAttach(gBuffer.FramebufferId, gBuffer.PositionTextureId, FramebufferAttachType.ColorChannel0, FramebufferAttachTextureType.Texture2D, 0);
Rlgl.FramebufferAttach(gBuffer.FramebufferId, gBuffer.NormalTextureId, FramebufferAttachType.ColorChannel1, FramebufferAttachTextureType.Texture2D, 0);
Rlgl.FramebufferAttach(gBuffer.FramebufferId, gBuffer.AlbedoSpecTextureId, FramebufferAttachType.ColorChannel2, FramebufferAttachTextureType.Texture2D, 0);
// Finally we attach the depth buffer
gBuffer.DepthRenderbufferId = Rlgl.LoadTextureDepth(screenWidth, screenHeight, true);
Rlgl.FramebufferAttach(gBuffer.FramebufferId, gBuffer.DepthRenderbufferId, FramebufferAttachType.Depth, FramebufferAttachTextureType.Renderbuffer, 0);
// Make sure our framebufferId is complete
// NOTE: rlFramebufferComplete() automatically unbinds the framebufferId, so we don't have to rlDisableFramebuffer() here
if (Rlgl.FramebufferComplete(gBuffer.FramebufferId) == 0)
{
TraceLog(TraceLogLevel.Warning, "Framebuffer is not complete");
}
// Now we initialize the sampler2D uniform's in the deferred shader
// We do this by setting the uniform's values to the texture units that
// we later bind our g-buffer textures to
Rlgl.EnableShader(deferredShader.Id);
int texUnitPosition = TexUnitPosition;
int texUnitNormal = TexUnitNormal;
int texUnitAlbedoSpec = TexUnitAlbedoSpec;
Raylib.SetShaderValue(deferredShader, GetShaderLocation(deferredShader, "gPosition"), texUnitPosition, ShaderUniformDataType.Sampler2D);
Raylib.SetShaderValue(deferredShader, GetShaderLocation(deferredShader, "gNormal"), texUnitNormal, ShaderUniformDataType.Sampler2D);
Raylib.SetShaderValue(deferredShader, GetShaderLocation(deferredShader, "gAlbedoSpec"), texUnitAlbedoSpec, ShaderUniformDataType.Sampler2D);
Rlgl.DisableShader();
// Assign out lighting shader to model
model.Materials[0].Shader = gbufferShader;
cube.Materials[0].Shader = gbufferShader;
// Create lights
lights = new Light[MaxLights];
lights[0] = Rlights.CreateLight(0, LightType.Point, new Vector3(-2, 1, -2), Vector3.Zero, Color.Yellow, deferredShader);
lights[1] = Rlights.CreateLight(1, LightType.Point, new Vector3(2, 1, 2), Vector3.Zero, Color.Red, deferredShader);
lights[2] = Rlights.CreateLight(2, LightType.Point, new Vector3(-2, 1, 2), Vector3.Zero, Color.Green, deferredShader);
lights[3] = Rlights.CreateLight(3, LightType.Point, new Vector3(2, 1, -2), Vector3.Zero, Color.Blue, deferredShader);
var rand = new Random();
cubePositions = new Vector3[MaxCubes];
cubeRotations = new float[MaxCubes];
for (var i = 0; i < MaxCubes; i++)
{
cubePositions[i] = new Vector3(
(float)(rand.Next() % 10) - 5,
(float)(rand.Next() % 5),
(float)(rand.Next() % 10) - 5
);
cubeRotations[i] = (float)(rand.Next() % 360);
}
mode = DeferredMode.Shading;
Rlgl.EnableDepthTest();
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
Raylib.SetShaderValue(
deferredShader,
deferredShader.Locs[(int)ShaderLocationIndex.VectorView],
camera.Position,
ShaderUniformDataType.Vec3
);
// Check key inputs to enable/disable lights
if (IsKeyPressed(KeyboardKey.Y)) { lights[0].Enabled = !lights[0].Enabled; }
if (IsKeyPressed(KeyboardKey.R)) { lights[1].Enabled = !lights[1].Enabled; }
if (IsKeyPressed(KeyboardKey.G)) { lights[2].Enabled = !lights[2].Enabled; }
if (IsKeyPressed(KeyboardKey.B)) { lights[3].Enabled = !lights[3].Enabled; }
// Check key inputs to switch between G-buffer textures
if (IsKeyPressed(KeyboardKey.One)) mode = DeferredMode.Position;
if (IsKeyPressed(KeyboardKey.Two)) mode = DeferredMode.Normal;
if (IsKeyPressed(KeyboardKey.Three)) mode = DeferredMode.Albedo;
if (IsKeyPressed(KeyboardKey.Four)) mode = DeferredMode.Shading;
// Update light values (actually, only enable/disable them)
for (var i = 0; i < MaxLights; i++) Rlights.UpdateLightValues(deferredShader, lights[i]);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
// Draw to the geometry buffer by first activating it
Rlgl.EnableFramebuffer(gBuffer.FramebufferId);
Rlgl.ClearColor(0, 0, 0, 0);
Rlgl.ClearScreenBuffers(); // Clear color and depth buffer
Rlgl.DisableColorBlend();
BeginMode3D(camera);
// NOTE: We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader`
// will not work, as they won't immediately load the shader program
Rlgl.EnableShader(gbufferShader.Id);
// When drawing a model here, make sure that the material's shaders are set to the gbuffer shader!
DrawModel(model, Vector3.Zero, 1.0f, Color.White);
DrawModel(cube, new Vector3(0.0f, 1.0f, 0.0f), 1.0f, Color.White);
for (var i = 0; i < MaxCubes; i++)
{
var position = cubePositions[i];
DrawModelEx(cube, position, new Vector3(1, 1, 1), cubeRotations[i], new Vector3(CubeScale, CubeScale, CubeScale), Color.White);
}
Rlgl.DisableShader();
EndMode3D();
Rlgl.EnableColorBlend();
// Go back to the default framebufferId (0) and draw our deferred shading
Rlgl.DisableFramebuffer();
Rlgl.ClearScreenBuffers(); // Clear color & depth buffer
switch (mode)
{
case DeferredMode.Shading:
{
BeginMode3D(camera);
Rlgl.DisableColorBlend();
Rlgl.EnableShader(deferredShader.Id);
// Bind our g-buffer textures
// We are binding them to locations that we earlier set in sampler2D uniforms `gPosition`, `gNormal`,
// and `gAlbedoSpec`
Rlgl.ActiveTextureSlot(TexUnitPosition);
Rlgl.EnableTexture(gBuffer.PositionTextureId);
Rlgl.ActiveTextureSlot(TexUnitNormal);
Rlgl.EnableTexture(gBuffer.NormalTextureId);
Rlgl.ActiveTextureSlot(TexUnitAlbedoSpec);
Rlgl.EnableTexture(gBuffer.AlbedoSpecTextureId);
// Finally, we draw a fullscreen quad to our default framebufferId
// This will now be shaded using our deferred shader
Rlgl.LoadDrawQuad();
Rlgl.DisableShader();
Rlgl.EnableColorBlend();
EndMode3D();
// As a last step, we now copy over the depth buffer from our g-buffer to the default framebufferId
Rlgl.BindFramebuffer(RlReadFramebuffer, gBuffer.FramebufferId);
Rlgl.BindFramebuffer(RlDrawFramebuffer, 0);
Rlgl.BlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, GlDepthBufferBit);
Rlgl.DisableFramebuffer();
// Since our shader is now done and disabled, we can draw spheres
// that represent light positions in default forward rendering
BeginMode3D(camera);
Rlgl.EnableShader(Rlgl.GetShaderIdDefault());
for (var i = 0; i < MaxLights; i++)
{
if (lights[i].Enabled) DrawSphereEx(lights[i].Position, 0.2f, 8, 8, lights[i].Color);
else DrawSphereWires(lights[i].Position, 0.2f, 8, 8, ColorAlpha(lights[i].Color, 0.3f));
}
Rlgl.DisableShader();
EndMode3D();
DrawText("FINAL RESULT", 10, screenHeight - 30, 20, Color.DarkGreen);
}
break;
case DeferredMode.Position:
{
DrawTextureRec(
new Texture2D { Id = gBuffer.PositionTextureId, Width = screenWidth, Height = screenHeight },
new Rectangle(0, 0, screenWidth, -screenHeight),
Vector2.Zero,
Color.RayWhite
);
DrawText("POSITION TEXTURE", 10, screenHeight - 30, 20, Color.DarkGreen);
}
break;
case DeferredMode.Normal:
{
DrawTextureRec(
new Texture2D { Id = gBuffer.NormalTextureId, Width = screenWidth, Height = screenHeight },
new Rectangle(0, 0, screenWidth, -screenHeight),
Vector2.Zero,
Color.RayWhite
);
DrawText("NORMAL TEXTURE", 10, screenHeight - 30, 20, Color.DarkGreen);
}
break;
case DeferredMode.Albedo:
{
DrawTextureRec(
new Texture2D { Id = gBuffer.AlbedoSpecTextureId, Width = screenWidth, Height = screenHeight },
new Rectangle(0, 0, screenWidth, -screenHeight),
Vector2.Zero,
Color.RayWhite
);
DrawText("ALBEDO TEXTURE", 10, screenHeight - 30, 20, Color.DarkGreen);
}
break;
default: break;
}
DrawText("Toggle lights keys: [Y][R][G][B]", 10, 40, 20, Color.DarkGray);
DrawText("Switch G-buffer textures: [1][2][3][4]", 10, 70, 20, Color.DarkGray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
// Unload the models
UnloadModel(model);
UnloadModel(cube);
// Unload shaders
UnloadShader(deferredShader);
UnloadShader(gbufferShader);
// Unload geometry buffer and all attached textures
Rlgl.UnloadFramebuffer(gBuffer.FramebufferId);
Rlgl.UnloadTexture(gBuffer.PositionTextureId);
Rlgl.UnloadTexture(gBuffer.NormalTextureId);
Rlgl.UnloadTexture(gBuffer.AlbedoSpecTextureId);
Rlgl.UnloadTexture(gBuffer.DepthRenderbufferId);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - deferred rendering");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new DeferredRendering();
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;
}
}

View file

@ -0,0 +1,226 @@
/*******************************************************************************************
*
* raylib [shaders] example - depth rendering
*
* Example complexity rating: [] 3/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Luís Almeida (@luis605) 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) 2025 Luís Almeida (@luis605)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public class DepthRendering : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Depth Rendering";
public string Title => "raylib [shaders] example - depth rendering";
public bool CursorDisabled => true;
private Camera3D camera;
private RenderTexture2D target;
private Shader depthShader;
private int depthLoc;
private Model cube;
private Model floor;
public void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(4.0f, 1.0f, 5.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
// Load render texture with a depth texture attached
target = LoadRenderTextureDepthTex(screenWidth, screenHeight);
// Load depth shader and get depth texture shader location
depthShader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/depth_render.fs");
depthLoc = GetShaderLocation(depthShader, "depthTexture");
var flipTextureLoc = GetShaderLocation(depthShader, "flipY");
Raylib.SetShaderValue(depthShader, flipTextureLoc, 1, ShaderUniformDataType.Int); // Flip Y texture
// Load scene models
cube = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f));
floor = LoadModelFromMesh(GenMeshPlane(20.0f, 20.0f, 1, 1));
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginTextureMode(target);
ClearBackground(Color.White);
BeginMode3D(camera);
DrawModel(cube, new Vector3(0.0f, 0.0f, 0.0f), 3.0f, Color.Yellow);
DrawModel(floor, new Vector3(10.0f, 0.0f, 2.0f), 2.0f, Color.Red);
EndMode3D();
EndTextureMode();
// Draw into screen (main framebuffer)
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginShaderMode(depthShader);
SetShaderValueTexture(depthShader, depthLoc, target.Depth);
DrawTexture(target.Depth, 0, 0, Color.White);
EndShaderMode();
DrawRectangle(10, 10, 320, 93, Fade(Color.SkyBlue, 0.5f));
DrawRectangleLines(10, 10, 320, 93, Color.Blue);
DrawText("Camera Controls:", 20, 20, 10, Color.Black);
DrawText("- WASD to move", 40, 40, 10, Color.DarkGray);
DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, Color.DarkGray);
DrawText("- Z to zoom to (0, 0, 0)", 40, 80, 10, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(cube); // Unload model
UnloadModel(floor); // Unload model
UnloadRenderTextureDepthTex(target);
UnloadShader(depthShader); // Unload shader
}
// Load custom render texture, create a writable depth texture buffer
private static unsafe RenderTexture2D LoadRenderTextureDepthTex(int width, int height)
{
RenderTexture2D target = new();
// Load an empty framebuffer
target.Id = Rlgl.LoadFramebuffer();
if (target.Id > 0)
{
Rlgl.EnableFramebuffer(target.Id);
// Create color texture (default to RGBA)
target.Texture.Id = Rlgl.LoadTexture(
null,
width,
height,
PixelFormat.UncompressedR8G8B8A8,
1
);
target.Texture.Width = width;
target.Texture.Height = height;
target.Texture.Format = PixelFormat.UncompressedR8G8B8A8;
target.Texture.Mipmaps = 1;
// Create depth texture buffer (instead of raylib default renderbuffer)
target.Depth.Id = Rlgl.LoadTextureDepth(width, height, false);
target.Depth.Width = width;
target.Depth.Height = height;
target.Depth.Format = PixelFormat.CompressedPvrtRgba; // DEPTH_COMPONENT_24BIT: Not defined in raylib
target.Depth.Mipmaps = 1;
// Attach color texture and depth texture to FBO
Rlgl.FramebufferAttach(
target.Id,
target.Texture.Id,
FramebufferAttachType.ColorChannel0,
FramebufferAttachTextureType.Texture2D,
0
);
Rlgl.FramebufferAttach(
target.Id,
target.Depth.Id,
FramebufferAttachType.Depth,
FramebufferAttachTextureType.Texture2D,
0
);
// Check if fbo is complete with attachments (valid)
if (Rlgl.FramebufferComplete(target.Id) != 0)
{
TraceLog(TraceLogLevel.Info, $"FBO: [ID {target.Id}] Framebuffer object created successfully");
}
Rlgl.DisableFramebuffer();
}
else
{
TraceLog(TraceLogLevel.Warning, "FBO: Framebuffer object can not be created");
}
return target;
}
// Unload render texture from GPU memory (VRAM)
private static void UnloadRenderTextureDepthTex(RenderTexture2D target)
{
if (target.Id > 0)
{
// Color texture attached to FBO is deleted
Rlgl.UnloadTexture(target.Texture.Id);
Rlgl.UnloadTexture(target.Depth.Id);
// NOTE: Depth texture is automatically
// queried and deleted before deleting framebuffer
Rlgl.UnloadFramebuffer(target.Id);
}
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - depth rendering");
DisableCursor(); // Limit cursor to relative movement inside the window
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new DepthRendering();
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;
}
}

View file

@ -0,0 +1,433 @@
/*******************************************************************************************
*
* raylib [shaders] example - game of life
*
* Example complexity rating: [] 3/4
*
* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Jordi Santonja (@JordSant) 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) 2025 Jordi Santonja (@JordSant)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public partial class GameOfLife : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Game of Life";
public string Title => "raylib [shaders] example - game of life";
// Interaction mode
private enum InteractionMode
{
Run = 0,
Pause,
Draw,
}
// Struct to store example preset patterns
private struct PresetPattern
{
public string Name;
public Vector2 Position;
public PresetPattern(string name, Vector2 position)
{
Name = name;
Position = position;
}
}
private const int menuWidth = 100;
private const int windowWidth = screenWidth - menuWidth;
private const int windowHeight = screenHeight;
private const int worldWidth = 2048;
private const int worldHeight = 2048;
private const int randomTiles = 8; // Random preset: divide the world to compute random points in each tile
private static readonly PresetPattern[] presetPatterns =
{
new("Glider", new Vector2(0.5f, 0.5f)), new("R-pentomino", new Vector2(0.5f, 0.5f)), new("Acorn", new Vector2(0.5f, 0.5f)),
new("Spaceships", new Vector2(0.1f, 0.5f)), new("Still lifes", new Vector2(0.5f, 0.5f)), new("Oscillators", new Vector2(0.5f, 0.5f)),
new("Puffer train", new Vector2(0.1f, 0.5f)), new("Glider Gun", new Vector2(0.2f, 0.2f)), new("Breeder", new Vector2(0.1f, 0.5f)),
new("Random", new Vector2(0.5f, 0.5f))
};
private static readonly int numberOfPresets = presetPatterns.Length;
private Rectangle worldRectSource;
private Rectangle worldRectDest;
private Rectangle textureOnScreen;
private int zoom;
private float offsetX;
private float offsetY;
private int framesPerStep;
private int frame;
private int preset;
private InteractionMode mode;
private bool buttonZoomIn;
private bool buttonZomOut;
private bool buttonFaster;
private bool buttonSlower;
private Shader shdrGameOfLife;
private int resolutionLoc;
private RenderTexture2D world1;
private RenderTexture2D world2;
private RenderTexture2D currentWorld;
private RenderTexture2D previousWorld;
// Image to be used in DRAW mode, to be changed with mouse input
private Image imageToDraw;
private bool imageToDrawValid;
// Static locals in the original loop, promoted to fields
private Vector2 previousMousePosition;
private int firstColor;
private void FreeImageToDraw()
{
if (imageToDrawValid)
{
UnloadImage(imageToDraw);
imageToDrawValid = false;
}
}
public unsafe void Init()
{
worldRectSource = new Rectangle(0, 0, worldWidth, -worldHeight);
worldRectDest = new Rectangle(0, 0, worldWidth, worldHeight);
textureOnScreen = new Rectangle(0, 0, windowWidth, windowHeight);
zoom = 1;
offsetX = (worldWidth - windowWidth) / 2.0f; // Centered on window
offsetY = (worldHeight - windowHeight) / 2.0f; // Centered on window
framesPerStep = 1;
frame = 0;
preset = -1; // No button pressed for preset
mode = InteractionMode.Run; // Starting mode: running
buttonZoomIn = false; // Button states: false not pressed
buttonZomOut = false;
buttonFaster = false;
buttonSlower = false;
previousMousePosition = new Vector2(0.0f, 0.0f);
firstColor = -1;
imageToDrawValid = false;
// Load shader
shdrGameOfLife = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/game_of_life.fs");
// Set shader uniform size of the world
resolutionLoc = GetShaderLocation(shdrGameOfLife, "resolution");
var resolution = new[] { (float)worldWidth, (float)worldHeight };
Raylib.SetShaderValue(shdrGameOfLife, resolutionLoc, resolution, ShaderUniformDataType.Vec2);
// Define two textures: the current world and the previous world
world1 = LoadRenderTexture(worldWidth, worldHeight);
world2 = LoadRenderTexture(worldWidth, worldHeight);
BeginTextureMode(world2);
ClearBackground(Color.RayWhite);
EndTextureMode();
var startPattern = LoadImage("resources/game_of_life/r_pentomino.png");
UpdateTextureRec(
world2.Texture,
new Rectangle(worldWidth / 2.0f, worldHeight / 2.0f, startPattern.Width, startPattern.Height),
startPattern.Data
);
UnloadImage(startPattern);
// References to the two textures, to be swapped
currentWorld = world2;
previousWorld = world1;
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
frame++;
// Change zoom: both by buttons or by mouse wheel
var mouseWheelMove = GetMouseWheelMove();
if (buttonZoomIn || (buttonZomOut && (zoom > 1)) || (mouseWheelMove != 0.0f))
{
FreeImageToDraw(); // Zoom change: free the image to draw to be recreated again
var centerX = offsetX + (windowWidth / 2.0f) / zoom;
var centerY = offsetY + (windowHeight / 2.0f) / zoom;
if (buttonZoomIn || (mouseWheelMove > 0.0f)) zoom *= 2;
if ((buttonZomOut || (mouseWheelMove < 0.0f)) && (zoom > 1)) zoom /= 2;
offsetX = centerX - (windowWidth / 2.0f) / zoom;
offsetY = centerY - (windowHeight / 2.0f) / zoom;
}
// Change speed: number of frames per step
if (buttonFaster && framesPerStep > 1) framesPerStep--;
if (buttonSlower) framesPerStep++;
// Mouse management
if ((mode == InteractionMode.Run) || (mode == InteractionMode.Pause))
{
FreeImageToDraw(); // Free the image to draw: no longer needed in these modes
// Pan with mouse left button
var mousePosition = GetMousePosition();
if (IsMouseButtonDown(MouseButton.Left) && (mousePosition.X < windowWidth))
{
offsetX -= (mousePosition.X - previousMousePosition.X) / zoom;
offsetY -= (mousePosition.Y - previousMousePosition.Y) / zoom;
}
previousMousePosition = mousePosition;
}
else // MODE_DRAW
{
var offsetDecimalX = offsetX - MathF.Floor(offsetX);
var offsetDecimalY = offsetY - MathF.Floor(offsetY);
var sizeInWorldX = (int)(MathF.Ceiling((windowWidth + offsetDecimalX * zoom) / zoom));
var sizeInWorldY = (int)(MathF.Ceiling((windowHeight + offsetDecimalY * zoom) / zoom));
if (offsetX + sizeInWorldX >= worldWidth) sizeInWorldX = worldWidth - (int)MathF.Floor(offsetX);
if (offsetY + sizeInWorldY >= worldHeight) sizeInWorldY = worldHeight - (int)MathF.Floor(offsetY);
// Create image to draw if not created yet
if (!imageToDrawValid)
{
var worldOnScreen = LoadRenderTexture(sizeInWorldX, sizeInWorldY);
BeginTextureMode(worldOnScreen);
DrawTexturePro(
currentWorld.Texture,
new Rectangle(MathF.Floor(offsetX), MathF.Floor(offsetY), sizeInWorldX, -sizeInWorldY),
new Rectangle(0, 0, sizeInWorldX, sizeInWorldY),
new Vector2(0, 0), 0.0f, Color.White
);
EndTextureMode();
imageToDraw = LoadImageFromTexture(worldOnScreen.Texture);
imageToDrawValid = true;
UnloadRenderTexture(worldOnScreen);
}
var mousePosition = GetMousePosition();
if (IsMouseButtonDown(MouseButton.Left) && (mousePosition.X < windowWidth))
{
var mouseX = (int)(mousePosition.X + offsetDecimalX * zoom) / zoom;
var mouseY = (int)(mousePosition.Y + offsetDecimalY * zoom) / zoom;
if (mouseX >= sizeInWorldX) mouseX = sizeInWorldX - 1;
if (mouseY >= sizeInWorldY) mouseY = sizeInWorldY - 1;
if (firstColor == -1) firstColor = (GetImageColor(imageToDraw, mouseX, mouseY).R < 5) ? 0 : 1;
var prevColor = (GetImageColor(imageToDraw, mouseX, mouseY).R < 5) ? 0 : 1;
ImageDrawPixel(ref imageToDraw, mouseX, mouseY, (firstColor != 0) ? Color.Black : Color.RayWhite);
if (prevColor != firstColor)
{
UpdateTextureRec(
currentWorld.Texture,
new Rectangle(MathF.Floor(offsetX), MathF.Floor(offsetY), sizeInWorldX, sizeInWorldY),
imageToDraw.Data
);
}
}
else firstColor = -1;
}
// Load selected preset
if (preset >= 0)
{
Image pattern;
if (preset < numberOfPresets - 1) // Preset with pattern image to load
{
pattern = preset switch
{
0 => LoadImage("resources/game_of_life/glider.png"),
1 => LoadImage("resources/game_of_life/r_pentomino.png"),
2 => LoadImage("resources/game_of_life/acorn.png"),
3 => LoadImage("resources/game_of_life/spaceships.png"),
4 => LoadImage("resources/game_of_life/still_lifes.png"),
5 => LoadImage("resources/game_of_life/oscillators.png"),
6 => LoadImage("resources/game_of_life/puffer_train.png"),
7 => LoadImage("resources/game_of_life/glider_gun.png"),
8 => LoadImage("resources/game_of_life/breeder.png"),
_ => default,
};
BeginTextureMode(currentWorld);
ClearBackground(Color.RayWhite);
EndTextureMode();
UpdateTextureRec(
currentWorld.Texture,
new Rectangle(
worldWidth * presetPatterns[preset].Position.X - pattern.Width / 2.0f,
worldHeight * presetPatterns[preset].Position.Y - pattern.Height / 2.0f,
pattern.Width, pattern.Height
),
pattern.Data
);
}
else // Last preset: Random values
{
pattern = GenImageColor(worldWidth / randomTiles, worldHeight / randomTiles, Color.RayWhite);
for (var i = 0; i < randomTiles; i++)
{
for (var j = 0; j < randomTiles; j++)
{
ImageClearBackground(ref pattern, Color.RayWhite);
for (var x = 0; x < pattern.Width; x++)
{
for (var y = 0; y < pattern.Height; y++)
{
if (GetRandomValue(0, 100) < 15) ImageDrawPixel(ref pattern, x, y, Color.Black);
}
}
UpdateTextureRec(
currentWorld.Texture,
new Rectangle(pattern.Width * i, pattern.Height * j, pattern.Width, pattern.Height),
pattern.Data
);
}
}
}
UnloadImage(pattern);
mode = InteractionMode.Pause;
offsetX = worldWidth * presetPatterns[preset].Position.X - (float)windowWidth / zoom / 2.0f;
offsetY = worldHeight * presetPatterns[preset].Position.Y - (float)windowHeight / zoom / 2.0f;
}
// Check window draw inside world limits
if (offsetX < 0) offsetX = 0;
if (offsetY < 0) offsetY = 0;
if (offsetX > worldWidth - (float)windowWidth / zoom) offsetX = worldWidth - (float)windowWidth / zoom;
if (offsetY > worldHeight - (float)windowHeight / zoom) offsetY = worldHeight - (float)windowHeight / zoom;
// Rectangles for drawing texture portion to screen
var textureSourceToScreen = new Rectangle(offsetX, offsetY, (float)windowWidth / zoom, (float)windowHeight / zoom);
//----------------------------------------------------------------------------------
// Draw to texture
//----------------------------------------------------------------------------------
if ((mode == InteractionMode.Run) && ((frame % framesPerStep) == 0))
{
// Swap worlds
var tempWorld = currentWorld;
currentWorld = previousWorld;
previousWorld = tempWorld;
// Draw to texture
BeginTextureMode(currentWorld);
BeginShaderMode(shdrGameOfLife);
DrawTexturePro(previousWorld.Texture, worldRectSource, worldRectDest, new Vector2(0, 0), 0.0f, Color.RayWhite);
EndShaderMode();
EndTextureMode();
}
//----------------------------------------------------------------------------------
// Draw to screen
//----------------------------------------------------------------------------------
BeginDrawing();
DrawTexturePro(currentWorld.Texture, textureSourceToScreen, textureOnScreen, new Vector2(0, 0), 0.0f, Color.White);
DrawLine(windowWidth, 0, windowWidth, screenHeight, new Color(218, 218, 218, 255));
DrawRectangle(windowWidth, 0, screenWidth - windowWidth, screenHeight, new Color(232, 232, 232, 255));
DrawText("Conway's", 704, 4, 20, Color.DarkBlue);
DrawText(" game of", 704, 19, 20, Color.DarkBlue);
DrawText(" life", 708, 34, 20, Color.DarkBlue);
DrawText("in raylib", 757, 42, 6, Color.Black);
DrawText("Presets", 710, 58, 8, Color.Gray);
preset = -1;
// Draw GUI controls
//------------------------------------------------------------------------------
// NOTE: raygui is not bound in raylib-cs; controls are kept for reference. The
// simulation still runs automatically and mouse wheel zoom / left-drag pan work.
/*for (int i = 0; i < numberOfPresets; i++)
if (GuiButton(new Rectangle( 710.0f, 70.0f + 18*i, 80.0f, 16.0f ), presetPatterns[i].Name)) preset = i;
GuiToggleGroup(new Rectangle( 710, 258, 80, 16 ), "Run\nPause\nDraw", ref mode);*/
DrawText($"Zoom: {zoom}x", 710, 316, 8, Color.Gray);
/*buttonZoomIn = GuiButton(new Rectangle( 710, 328, 80, 16 ), "Zoom in");
buttonZomOut = GuiButton(new Rectangle( 710, 346, 80, 16 ), "Zoom out");*/
DrawText($"Speed: {framesPerStep} frame{((framesPerStep > 1) ? "s" : "")}", 710, 370, 8, Color.Gray);
/*buttonFaster = GuiButton(new Rectangle( 710, 382, 80, 16 ), "Faster");
buttonSlower = GuiButton(new Rectangle( 710, 400, 80, 16 ), "Slower");*/
//------------------------------------------------------------------------------
DrawFPS(712, 426);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(shdrGameOfLife);
UnloadRenderTexture(world1);
UnloadRenderTexture(world2);
FreeImageToDraw();
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - game of life");
SetTargetFPS(60); // Set at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new GameOfLife();
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;
}
}

View file

@ -0,0 +1,213 @@
/*******************************************************************************************
*
* raylib [shaders] example - lightmap rendering
*
* Example complexity rating: [] 3/4
*
* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
*
* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
*
* Example originally created with raylib 4.5, last time updated with raylib 4.5
*
* Example contributed by Jussi Viitala (@nullstare) 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) 2019-2025 Jussi Viitala (@nullstare) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
using static Raylib_cs.Rlgl;
namespace Examples.Shaders;
public partial class LightmapRendering : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
private const int MapSize = 16;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Lightmap Rendering";
public string Title => "raylib [shaders] example - lightmap rendering";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private Camera3D camera;
private Mesh mesh;
private Shader shader;
private Texture2D texture;
private Texture2D light;
private RenderTexture2D lightmap;
private Material material;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(4.0f, 6.0f, 8.0f); // Camera position
camera.Target = new Vector3(0.0f, 0.0f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
mesh = GenMeshPlane((float)MapSize, (float)MapSize, 1, 1);
// GenMeshPlane doesn't generate texcoords2 so we will upload them separately
mesh.AllocTexCoords2();
// X // Y
mesh.TexCoords2[0] = 0.0f; mesh.TexCoords2[1] = 0.0f;
mesh.TexCoords2[2] = 1.0f; mesh.TexCoords2[3] = 0.0f;
mesh.TexCoords2[4] = 0.0f; mesh.TexCoords2[5] = 1.0f;
mesh.TexCoords2[6] = 1.0f; mesh.TexCoords2[7] = 1.0f;
// Load a new texcoords2 attributes buffer
mesh.VboId[(int)ShaderLocationIndex.VertexTexcoord02] =
LoadVertexBuffer(mesh.TexCoords2, mesh.VertexCount * 2 * sizeof(float), false);
EnableVertexArray(mesh.VaoId);
// Index 5 is for texcoords2
SetVertexAttribute(5, 2, Rlgl.FLOAT, false, 0, 0);
EnableVertexAttribute(5);
DisableVertexArray();
// Load lightmap shader
shader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/lightmap.vs",
$"resources/shaders/glsl{GlslVersion}/lightmap.fs"
);
texture = LoadTexture("resources/cubicmap_atlas.png");
light = LoadTexture("resources/spark_flame.png");
GenTextureMipmaps(ref texture);
SetTextureFilter(texture, TextureFilter.Trilinear);
lightmap = LoadRenderTexture(MapSize, MapSize);
material = LoadMaterialDefault();
material.Shader = shader;
material.Maps[(int)MaterialMapIndex.Albedo].Texture = texture;
material.Maps[(int)MaterialMapIndex.Metalness].Texture = lightmap.Texture;
// Drawing to lightmap
BeginTextureMode(lightmap);
ClearBackground(Color.Black);
BeginBlendMode(BlendMode.Additive);
DrawTexturePro(
light,
new Rectangle(0, 0, (float)light.Width, (float)light.Height),
new Rectangle(0, 0, 2.0f * MapSize, 2.0f * MapSize),
new Vector2((float)MapSize, (float)MapSize),
0.0f,
Color.Red
);
DrawTexturePro(
light,
new Rectangle(0, 0, (float)light.Width, (float)light.Height),
new Rectangle((float)MapSize * 0.8f, (float)MapSize / 2.0f, 2.0f * MapSize, 2.0f * MapSize),
new Vector2((float)MapSize, (float)MapSize),
0.0f,
Color.Blue
);
DrawTexturePro(
light,
new Rectangle(0, 0, (float)light.Width, (float)light.Height),
new Rectangle((float)MapSize * 0.8f, (float)MapSize * 0.8f, (float)MapSize, (float)MapSize),
new Vector2((float)MapSize / 2.0f, (float)MapSize / 2.0f),
0.0f,
Color.Green
);
BeginBlendMode(BlendMode.Alpha);
EndTextureMode();
// NOTE: To enable trilinear filtering we need mipmaps available for texture
GenTextureMipmaps(ref lightmap.Texture);
SetTextureFilter(lightmap.Texture, TextureFilter.Trilinear);
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawMesh(mesh, material, Matrix4x4.Identity);
EndMode3D();
DrawTexturePro(
lightmap.Texture,
new Rectangle(0, 0, -MapSize, -MapSize),
new Rectangle((float)GetRenderWidth() - MapSize * 8 - 10, 10, (float)MapSize * 8, (float)MapSize * 8),
new Vector2(0.0f, 0.0f),
0.0f,
Color.White
);
DrawText($"LIGHTMAP: {MapSize}x{MapSize} pixels", GetRenderWidth() - 130, 20 + MapSize * 8, 10, Color.Green);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadMesh(mesh); // Unload the mesh
UnloadShader(shader); // Unload shader
UnloadTexture(texture); // Unload texture
UnloadTexture(light); // Unload texture
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint); // Enable Multi Sampling Anti Aliasing 4x (if available)
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - lightmap rendering");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new LightmapRendering();
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;
}
}

View file

@ -0,0 +1,265 @@
/*******************************************************************************************
*
* raylib [shaders] example - mandelbrot set
*
* Example complexity rating: [] 3/4
*
* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
*
* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Jordi Santonja (@JordSant)
* Based on previous work by Josh Colclough (@joshcol9232)
*
* 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) 2025 Jordi Santonja (@JordSant)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public partial class MandelbrotSet : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Mandelbrot Set";
public string Title => "raylib [shaders] example - mandelbrot set";
// A few good interesting places
private static readonly float[][] pointsOfInterest = new[]
{
new[] { -1.76826775f, -0.00422996283f, 28435.9238f },
new[] { 0.322004497f, -0.0357099883f, 56499.7266f },
new[] { -0.748880744f, -0.0562955774f, 9237.59082f },
new[] { -1.78385007f, -0.0156200649f, 14599.5283f },
new[] { -0.0985441282f, -0.924688697f, 26259.8535f },
new[] { 0.317785531f, -0.0322612226f, 29297.9258f },
};
private const float zoomSpeed = 1.01f;
private const float offsetSpeedMul = 2.0f;
private const float startingZoom = 0.6f;
private static readonly float[] startingOffset = { -0.5f, 0.0f };
private Shader shader;
private RenderTexture2D target;
private float[] offset;
private float zoom;
private int maxIterations;
private float maxIterationsMultiplier;
private int zoomLoc;
private int offsetLoc;
private int maxIterationsLoc;
private bool showControls;
public void Init()
{
// Load mandelbrot set shader
// NOTE: Defining null (NULL) for vertex shader forces usage of internal default vertex shader
shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/mandelbrot_set.fs");
// Create a RenderTexture2D to be used for render to texture
target = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
// Offset and zoom to draw the mandelbrot set at. (centered on screen and default size)
offset = new[] { startingOffset[0], startingOffset[1] };
zoom = startingZoom;
// Depending on the zoom the maximum number of iterations must be adapted to get more detail as we zoom in
// The solution is not perfect, so a control has been added to increase/decrease the number of iterations with UP/DOWN keys
#if BROWSER
maxIterations = 43;
maxIterationsMultiplier = 22.0f;
#else
maxIterations = 333;
maxIterationsMultiplier = 166.5f;
#endif
// Get variable (uniform) locations on the shader to connect with the program
// NOTE: If uniform variable could not be found in the shader, function returns -1
zoomLoc = GetShaderLocation(shader, "zoom");
offsetLoc = GetShaderLocation(shader, "offset");
maxIterationsLoc = GetShaderLocation(shader, "maxIterations");
// Upload the shader uniform values!
Raylib.SetShaderValue(shader, zoomLoc, zoom, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shader, offsetLoc, offset, ShaderUniformDataType.Vec2);
Raylib.SetShaderValue(shader, maxIterationsLoc, maxIterations, ShaderUniformDataType.Int);
showControls = true; // Show controls
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
var updateShader = false;
// Press [1 - 6] to reset c to a point of interest
if (IsKeyPressed(KeyboardKey.One) ||
IsKeyPressed(KeyboardKey.Two) ||
IsKeyPressed(KeyboardKey.Three) ||
IsKeyPressed(KeyboardKey.Four) ||
IsKeyPressed(KeyboardKey.Five) ||
IsKeyPressed(KeyboardKey.Six))
{
var interestIndex = 0;
if (IsKeyPressed(KeyboardKey.One)) interestIndex = 0;
else if (IsKeyPressed(KeyboardKey.Two)) interestIndex = 1;
else if (IsKeyPressed(KeyboardKey.Three)) interestIndex = 2;
else if (IsKeyPressed(KeyboardKey.Four)) interestIndex = 3;
else if (IsKeyPressed(KeyboardKey.Five)) interestIndex = 4;
else if (IsKeyPressed(KeyboardKey.Six)) interestIndex = 5;
offset[0] = pointsOfInterest[interestIndex][0];
offset[1] = pointsOfInterest[interestIndex][1];
zoom = pointsOfInterest[interestIndex][2];
updateShader = true;
}
// If "R" is pressed, reset zoom and offset
if (IsKeyPressed(KeyboardKey.R))
{
offset[0] = startingOffset[0];
offset[1] = startingOffset[1];
zoom = startingZoom;
updateShader = true;
}
if (IsKeyPressed(KeyboardKey.F1)) showControls = !showControls; // Toggle whether or not to show controls
// Change number of max iterations with UP and DOWN keys
// WARNING: Increasing the number of max iterations greatly impacts performance
if (IsKeyPressed(KeyboardKey.Up))
{
maxIterationsMultiplier *= 1.4f;
updateShader = true;
}
else if (IsKeyPressed(KeyboardKey.Down))
{
maxIterationsMultiplier /= 1.4f;
updateShader = true;
}
// If either left or right button is pressed, zoom in/out
if (IsMouseButtonDown(MouseButton.Left) || IsMouseButtonDown(MouseButton.Right))
{
// Change zoom. If Mouse left -> zoom in. Mouse right -> zoom out
zoom *= IsMouseButtonDown(MouseButton.Left) ? zoomSpeed : (1.0f / zoomSpeed);
var mousePos = GetMousePosition();
Vector2 offsetVelocity;
// Find the velocity at which to change the camera. Take the distance of the mouse
// From the center of the screen as the direction, and adjust magnitude based on the current zoom
offsetVelocity.X = (mousePos.X / (float)screenWidth - 0.5f) * offsetSpeedMul / zoom;
offsetVelocity.Y = (mousePos.Y / (float)screenHeight - 0.5f) * offsetSpeedMul / zoom;
// Apply move velocity to camera
offset[0] += GetFrameTime() * offsetVelocity.X;
offset[1] += GetFrameTime() * offsetVelocity.Y;
updateShader = true;
}
// In case a parameter has been changed, update the shader values
if (updateShader)
{
// As we zoom in, increase the number of max iterations to get more detail
// Aproximate formula, but it works-ish
maxIterations = (int)(MathF.Sqrt(2.0f * MathF.Sqrt(MathF.Abs(1.0f - MathF.Sqrt(37.5f * zoom)))) * maxIterationsMultiplier);
// Update the shader uniform values!
Raylib.SetShaderValue(shader, zoomLoc, zoom, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shader, offsetLoc, offset, ShaderUniformDataType.Vec2);
Raylib.SetShaderValue(shader, maxIterationsLoc, maxIterations, ShaderUniformDataType.Int);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
// Using a render texture to draw Mandelbrot set
BeginTextureMode(target); // Enable drawing to texture
ClearBackground(Color.Black); // Clear the render texture
// Draw a rectangle in shader mode to be used as shader canvas
// NOTE: Rectangle uses font white character texture coordinates,
// So shader can not be applied here directly because input vertexTexCoord
// Do not represent full screen coordinates (space where want to apply shader)
DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Color.Black);
EndTextureMode();
BeginDrawing();
ClearBackground(Color.Black); // Clear screen background
// Draw the saved texture and rendered mandelbrot set with shader
// NOTE: We do not invert texture on Y, already considered inside shader
BeginShaderMode(shader);
// WARNING: If FLAG_WINDOW_HIGHDPI is enabled, HighDPI monitor scaling should be considered
// When rendering the RenderTexture2D to fit in the HighDPI scaled Window
DrawTextureEx(target.Texture, new Vector2(0.0f, 0.0f), 0.0f, 1.0f, Color.White);
EndShaderMode();
if (showControls)
{
DrawText("Press Mouse buttons right/left to zoom in/out and move", 10, 15, 10, Color.RayWhite);
DrawText("Press F1 to toggle these controls", 10, 30, 10, Color.RayWhite);
DrawText("Press [1 - 6] to change point of interest", 10, 45, 10, Color.RayWhite);
DrawText("Press UP | DOWN to change number of iterations", 10, 60, 10, Color.RayWhite);
DrawText("Press R to recenter the camera", 10, 75, 10, Color.RayWhite);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(shader); // Unload shader
UnloadRenderTexture(target); // Unload render texture
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - mandelbrot set");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new MandelbrotSet();
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;
}
}

View file

@ -0,0 +1,233 @@
/*******************************************************************************************
*
* raylib [shaders] example - normalmap rendering
*
* Example complexity rating: [] 4/4
*
* NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
* OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example contributed by Jeremy Montgomery (@Sir_Irk) 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) 2025 Jeremy Montgomery (@Sir_Irk) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
using static Raylib_cs.Raymath;
namespace Examples.Shaders;
public partial class NormalmapRendering : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Normalmap Rendering";
public string Title => "raylib [shaders] example - normalmap rendering";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private Camera3D camera;
private Shader shader;
private Model plane;
private Vector3 lightPosition;
private int lightPosLoc;
private float specularExponent;
private int specularExponentLoc;
private int useNormalMap;
private int useNormalMapLoc;
public unsafe void Init()
{
camera = new();
camera.Position = new Vector3(0.0f, 2.0f, -4.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
// Load basic normal map lighting shader
shader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/normalmap.vs",
$"resources/shaders/glsl{GlslVersion}/normalmap.fs"
);
// Get some required shader locations
shader.Locs[(int)ShaderLocationIndex.MapNormal] = GetShaderLocation(shader, "normalMap");
shader.Locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(shader, "viewPos");
// NOTE: "matModel" location name is automatically assigned on shader loading,
// no need to get the location again if using that uniform name
// shader.Locs[(int)ShaderLocationIndex.MatrixModel] = GetShaderLocation(shader, "matModel");
// This example uses just 1 point light
lightPosition = new Vector3(0.0f, 1.0f, 0.0f);
lightPosLoc = GetShaderLocation(shader, "lightPos");
// Load a plane model that has proper normals and tangents
plane = LoadModel("resources/models/plane.glb");
// Set the plane model's shader and texture maps
plane.Materials[0].Shader = shader;
plane.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture = LoadTexture("resources/tiles_diffuse.png");
plane.Materials[0].Maps[(int)MaterialMapIndex.Normal].Texture = LoadTexture("resources/tiles_normal.png");
// Generate Mipmaps and use TRILINEAR filtering to help with texture aliasing
GenTextureMipmaps(ref plane.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture);
GenTextureMipmaps(ref plane.Materials[0].Maps[(int)MaterialMapIndex.Normal].Texture);
SetTextureFilter(plane.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture, TextureFilter.Trilinear);
SetTextureFilter(plane.Materials[0].Maps[(int)MaterialMapIndex.Normal].Texture, TextureFilter.Trilinear);
// Specular exponent AKA shininess of the material
specularExponent = 8.0f;
specularExponentLoc = GetShaderLocation(shader, "specularExponent");
// Allow toggling the normal map on and off for comparison purposes
useNormalMap = 1;
useNormalMapLoc = GetShaderLocation(shader, "useNormalMap");
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
// Move the light around on the X and Z axis using WASD keys
Vector3 direction = new(0.0f, 0.0f, 0.0f);
if (IsKeyDown(KeyboardKey.W))
{
direction = Vector3Add(direction, new Vector3(0.0f, 0.0f, 1.0f));
}
if (IsKeyDown(KeyboardKey.S))
{
direction = Vector3Add(direction, new Vector3(0.0f, 0.0f, -1.0f));
}
if (IsKeyDown(KeyboardKey.D))
{
direction = Vector3Add(direction, new Vector3(-1.0f, 0.0f, 0.0f));
}
if (IsKeyDown(KeyboardKey.A))
{
direction = Vector3Add(direction, new Vector3(1.0f, 0.0f, 0.0f));
}
direction = Vector3Normalize(direction);
lightPosition = Vector3Add(lightPosition, Vector3Scale(direction, GetFrameTime() * 3.0f));
// Increase/Decrease the specular exponent(shininess)
if (IsKeyDown(KeyboardKey.Up))
{
specularExponent = Clamp(specularExponent + 40.0f * GetFrameTime(), 2.0f, 128.0f);
}
if (IsKeyDown(KeyboardKey.Down))
{
specularExponent = Clamp(specularExponent - 40.0f * GetFrameTime(), 2.0f, 128.0f);
}
// Toggle normal map on and off
if (IsKeyPressed(KeyboardKey.N))
{
useNormalMap = (useNormalMap != 0) ? 0 : 1;
}
// Spin plane model at a constant rate
plane.Transform = MatrixRotateY((float)GetTime() * 0.5f);
// Update shader values
Raylib.SetShaderValue(shader, lightPosLoc, lightPosition, ShaderUniformDataType.Vec3);
Raylib.SetShaderValue(
shader,
shader.Locs[(int)ShaderLocationIndex.VectorView],
camera.Position,
ShaderUniformDataType.Vec3
);
Raylib.SetShaderValue(shader, specularExponentLoc, specularExponent, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shader, useNormalMapLoc, useNormalMap, ShaderUniformDataType.Int);
//--------------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
BeginShaderMode(shader);
DrawModel(plane, Vector3.Zero, 2.0f, Color.White);
EndShaderMode();
// Draw sphere to show light position
DrawSphereWires(lightPosition, 0.2f, 8, 8, Color.Orange);
EndMode3D();
Color textColor = (useNormalMap != 0) ? Color.DarkGreen : Color.Red;
string toggleStr = (useNormalMap != 0) ? "On" : "Off";
DrawText($"Use key [N] to toggle normal map: {toggleStr}", 10, 10, 10, textColor);
int yOffset = 24;
DrawText("Use keys [W][A][S][D] to move the light", 10, 10 + yOffset * 1, 10, Color.Black);
DrawText("Use keys [Up][Down] to change specular exponent", 10, 10 + yOffset * 2, 10, Color.Black);
DrawText($"Specular Exponent: {specularExponent:F2}", 10, 10 + yOffset * 3, 10, Color.Blue);
DrawFPS(screenWidth - 90, 10);
EndDrawing();
//--------------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(shader);
UnloadModel(plane);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - normalmap rendering");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new NormalmapRendering();
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;
}
}

View file

@ -0,0 +1,242 @@
/*******************************************************************************************
*
* raylib [shaders] example - rlgl compute
*
* WARNING: This example requires raylib compiled with OpenGL 4.3 version for
* compute shaders support, shaders used in this example are #version 430
*
* Example complexity rating: [] 4/4
*
* Example originally created with raylib 4.0, last time updated with raylib 4.0
*
* Example contributed by Teddy Astie (@tsnake41) 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) 2021-2025 Teddy Astie (@tsnake41)
*
********************************************************************************************/
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using static Raylib_cs.Raylib;
namespace Examples.Shaders;
[ExcludeFromBrowser("compute shaders are not available on WebGL")]
public partial class RlglCompute : IExample
{
// IMPORTANT: This must match gol*.glsl GOL_WIDTH constant
// This must be a multiple of 16 (check golLogic compute dispatch)
private const int GolWidth = 768;
// Maximum amount of queued draw commands (squares draw from mouse down events)
private const int MaxBufferedTransferts = 48;
private const int screenWidth = GolWidth;
private const int screenHeight = GolWidth;
public string Name => "Shaders / Rlgl Compute";
public string Title => "raylib [shaders] example - rlgl compute";
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
// Game Of Life Update Command
[StructLayout(LayoutKind.Sequential)]
private struct GolUpdateCmd
{
public uint X; // x coordinate of the gol command
public uint Y; // y coordinate of the gol command
public uint W; // width of the filled zone
public uint Enabled; // whether to enable or disable zone
}
// Inline fixed-size array of GolUpdateCmd (MAX_BUFFERED_TRANSFERTS entries)
[InlineArray(MaxBufferedTransferts)]
private struct GolUpdateCmdBuffer
{
private GolUpdateCmd _element0;
}
// Game Of Life Update Commands SSBO
[StructLayout(LayoutKind.Sequential)]
private struct GolUpdateSSBO
{
public uint Count;
public GolUpdateCmdBuffer Commands;
}
private Vector2 resolution;
private uint brushSize;
private uint golLogicShader;
private uint golLogicProgram;
private uint golTransfertShader;
private uint golTransfertProgram;
private Shader golRenderShader;
private int resUniformLoc;
private uint ssboA;
private uint ssboB;
private uint ssboTransfert;
private GolUpdateSSBO transfertBuffer;
private Texture2D whiteTex;
public unsafe void Init()
{
resolution = new Vector2(screenWidth, screenHeight);
brushSize = 8;
// Game of Life logic compute shader
var golLogicCode = LoadFileText("resources/shaders/glsl430/gol.glsl");
var golLogicBytes = Encoding.UTF8.GetBytes(golLogicCode + "\0");
fixed (byte* p = golLogicBytes)
{
golLogicShader = Rlgl.LoadShader((sbyte*)p, (int)ShaderType.Compute);
}
golLogicProgram = Rlgl.LoadShaderProgramCompute(golLogicShader);
// Game of Life logic render shader
golRenderShader = LoadShader(null, "resources/shaders/glsl430/gol_render.glsl");
resUniformLoc = GetShaderLocation(golRenderShader, "resolution");
// Game of Life transfert shader (CPU<->GPU download and upload)
var golTransfertCode = LoadFileText("resources/shaders/glsl430/gol_transfert.glsl");
var golTransfertBytes = Encoding.UTF8.GetBytes(golTransfertCode + "\0");
fixed (byte* p = golTransfertBytes)
{
golTransfertShader = Rlgl.LoadShader((sbyte*)p, (int)ShaderType.Compute);
}
golTransfertProgram = Rlgl.LoadShaderProgramCompute(golTransfertShader);
// Load shader storage buffer object (SSBO), id returned
ssboA = Rlgl.LoadShaderBuffer((uint)(GolWidth * GolWidth * sizeof(uint)), null, Rlgl.DYNAMIC_COPY);
ssboB = Rlgl.LoadShaderBuffer((uint)(GolWidth * GolWidth * sizeof(uint)), null, Rlgl.DYNAMIC_COPY);
ssboTransfert = Rlgl.LoadShaderBuffer((uint)sizeof(GolUpdateSSBO), null, Rlgl.DYNAMIC_COPY);
transfertBuffer = new();
// Create a white texture of the size of the window to update
// each pixel of the window using the fragment shader: golRenderShader
var whiteImage = GenImageColor(GolWidth, GolWidth, Color.White);
whiteTex = LoadTextureFromImage(whiteImage);
UnloadImage(whiteImage);
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
brushSize += (uint)(int)GetMouseWheelMove();
if ((IsMouseButtonDown(MouseButton.Left) || IsMouseButtonDown(MouseButton.Right))
&& (transfertBuffer.Count < MaxBufferedTransferts))
{
// Buffer a new command
transfertBuffer.Commands[(int)transfertBuffer.Count].X = (uint)GetMouseX() - brushSize / 2;
transfertBuffer.Commands[(int)transfertBuffer.Count].Y = (uint)GetMouseY() - brushSize / 2;
transfertBuffer.Commands[(int)transfertBuffer.Count].W = brushSize;
transfertBuffer.Commands[(int)transfertBuffer.Count].Enabled = IsMouseButtonDown(MouseButton.Left) ? 1u : 0u;
transfertBuffer.Count++;
}
else if (transfertBuffer.Count > 0) // Process transfert buffer
{
// Send SSBO buffer to GPU
fixed (GolUpdateSSBO* ptr = &transfertBuffer)
{
Rlgl.UpdateShaderBuffer(ssboTransfert, ptr, (uint)sizeof(GolUpdateSSBO), 0);
}
// Process SSBO commands on GPU
Rlgl.EnableShader(golTransfertProgram);
Rlgl.BindShaderBuffer(ssboA, 1);
Rlgl.BindShaderBuffer(ssboTransfert, 3);
Rlgl.ComputeShaderDispatch(transfertBuffer.Count, 1, 1); // Each GPU unit will process a command!
Rlgl.DisableShader();
transfertBuffer.Count = 0;
}
else
{
// Process game of life logic
Rlgl.EnableShader(golLogicProgram);
Rlgl.BindShaderBuffer(ssboA, 1);
Rlgl.BindShaderBuffer(ssboB, 2);
Rlgl.ComputeShaderDispatch(GolWidth / 16, GolWidth / 16, 1);
Rlgl.DisableShader();
// ssboA <-> ssboB
var temp = ssboA;
ssboA = ssboB;
ssboB = temp;
}
Rlgl.BindShaderBuffer(ssboA, 1);
Raylib.SetShaderValue(golRenderShader, resUniformLoc, resolution, ShaderUniformDataType.Vec2);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.Blank);
BeginShaderMode(golRenderShader);
DrawTexture(whiteTex, 0, 0, Color.White);
EndShaderMode();
DrawRectangleLines(GetMouseX() - (int)(brushSize / 2), GetMouseY() - (int)(brushSize / 2), (int)brushSize, (int)brushSize, Color.Red);
DrawText("Use Mouse wheel to increase/decrease brush size", 10, 10, 20, Color.White);
DrawFPS(GetScreenWidth() - 100, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
// Unload shader buffers objects
Rlgl.UnloadShaderBuffer(ssboA);
Rlgl.UnloadShaderBuffer(ssboB);
Rlgl.UnloadShaderBuffer(ssboTransfert);
// Unload compute shader
Rlgl.UnloadShader(golLogicShader);
Rlgl.UnloadShader(golTransfertShader);
Rlgl.UnloadShaderProgram(golTransfertProgram);
Rlgl.UnloadShaderProgram(golLogicProgram);
UnloadTexture(whiteTex); // Unload white texture
UnloadShader(golRenderShader); // Unload rendering fragment shader
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - rlgl compute");
//--------------------------------------------------------------------------------------
var game = new RlglCompute();
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;
}
}

View file

@ -0,0 +1,243 @@
/*******************************************************************************************
*
* raylib [shaders] example - rounded rectangle
*
* Example complexity rating: [] 3/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.5
*
* Example contributed by Anstro Pleuton (@anstropleuton) 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) 2025 Anstro Pleuton (@anstropleuton)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public class RoundedRectangle : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Rounded Rectangle";
public string Title => "raylib [shaders] example - rounded rectangle";
// Rounded rectangle data
private struct RoundedRect
{
public Vector4 CornerRadius; // Individual corner radius (top-left, top-right, bottom-left, bottom-right)
// Shadow variables
public float ShadowRadius;
public Vector2 ShadowOffset;
public float ShadowScale;
// Border variables
public float BorderThickness; // Inner-border thickness
// Shader locations
public int RectangleLoc;
public int RadiusLoc;
public int ColorLoc;
public int ShadowRadiusLoc;
public int ShadowOffsetLoc;
public int ShadowScaleLoc;
public int ShadowColorLoc;
public int BorderThicknessLoc;
public int BorderColorLoc;
}
private Shader shader;
private RoundedRect roundedRectangle;
private readonly Color rectangleColor = Color.Blue;
private readonly Color shadowColor = Color.DarkBlue;
private readonly Color borderColor = Color.SkyBlue;
public void Init()
{
// Load the shader
shader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/base.vs",
$"resources/shaders/glsl{GlslVersion}/rounded_rectangle.fs"
);
// Create a rounded rectangle
roundedRectangle = CreateRoundedRectangle(
new Vector4(5.0f, 10.0f, 15.0f, 20.0f), // Corner radius
20.0f, // Shadow radius
new Vector2(0.0f, -5.0f), // Shadow offset
0.95f, // Shadow scale
5.0f, // Border thickness
shader // Shader
);
// Update shader uniforms
UpdateRoundedRectangle(roundedRectangle, shader);
}
public void Update()
{
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Draw rectangle box with rounded corners using shader
Rectangle rec = new(50, 70, 110, 60);
DrawRectangleLines((int)rec.X - 20, (int)rec.Y - 20, (int)rec.Width + 40, (int)rec.Height + 40, Color.DarkGray);
DrawText("Rounded rectangle", (int)rec.X - 20, (int)rec.Y - 35, 10, Color.DarkGray);
// Flip Y axis to match shader coordinate system
rec.Y = screenHeight - rec.Y - rec.Height;
Raylib.SetShaderValue(shader, roundedRectangle.RectangleLoc, new[] { rec.X, rec.Y, rec.Width, rec.Height }, ShaderUniformDataType.Vec4);
// Only rectangle color
Raylib.SetShaderValue(shader, roundedRectangle.ColorLoc, new[] { rectangleColor.R / 255.0f, rectangleColor.G / 255.0f, rectangleColor.B / 255.0f, rectangleColor.A / 255.0f }, ShaderUniformDataType.Vec4);
Raylib.SetShaderValue(shader, roundedRectangle.ShadowColorLoc, new[] { 0.0f, 0.0f, 0.0f, 0.0f }, ShaderUniformDataType.Vec4);
Raylib.SetShaderValue(shader, roundedRectangle.BorderColorLoc, new[] { 0.0f, 0.0f, 0.0f, 0.0f }, ShaderUniformDataType.Vec4);
BeginShaderMode(shader);
DrawRectangle(0, 0, screenWidth, screenHeight, Color.White);
EndShaderMode();
// Draw rectangle shadow using shader
rec = new Rectangle(50, 200, 110, 60);
DrawRectangleLines((int)rec.X - 20, (int)rec.Y - 20, (int)rec.Width + 40, (int)rec.Height + 40, Color.DarkGray);
DrawText("Rounded rectangle shadow", (int)rec.X - 20, (int)rec.Y - 35, 10, Color.DarkGray);
rec.Y = screenHeight - rec.Y - rec.Height;
Raylib.SetShaderValue(shader, roundedRectangle.RectangleLoc, new[] { rec.X, rec.Y, rec.Width, rec.Height }, ShaderUniformDataType.Vec4);
// Only shadow color
Raylib.SetShaderValue(shader, roundedRectangle.ColorLoc, new[] { 0.0f, 0.0f, 0.0f, 0.0f }, ShaderUniformDataType.Vec4);
Raylib.SetShaderValue(shader, roundedRectangle.ShadowColorLoc, new[] { shadowColor.R / 255.0f, shadowColor.G / 255.0f, shadowColor.B / 255.0f, shadowColor.A / 255.0f }, ShaderUniformDataType.Vec4);
Raylib.SetShaderValue(shader, roundedRectangle.BorderColorLoc, new[] { 0.0f, 0.0f, 0.0f, 0.0f }, ShaderUniformDataType.Vec4);
BeginShaderMode(shader);
DrawRectangle(0, 0, screenWidth, screenHeight, Color.White);
EndShaderMode();
// Draw rectangle's border using shader
rec = new Rectangle(50, 330, 110, 60);
DrawRectangleLines((int)rec.X - 20, (int)rec.Y - 20, (int)rec.Width + 40, (int)rec.Height + 40, Color.DarkGray);
DrawText("Rounded rectangle border", (int)rec.X - 20, (int)rec.Y - 35, 10, Color.DarkGray);
rec.Y = screenHeight - rec.Y - rec.Height;
Raylib.SetShaderValue(shader, roundedRectangle.RectangleLoc, new[] { rec.X, rec.Y, rec.Width, rec.Height }, ShaderUniformDataType.Vec4);
// Only border color
Raylib.SetShaderValue(shader, roundedRectangle.ColorLoc, new[] { 0.0f, 0.0f, 0.0f, 0.0f }, ShaderUniformDataType.Vec4);
Raylib.SetShaderValue(shader, roundedRectangle.ShadowColorLoc, new[] { 0.0f, 0.0f, 0.0f, 0.0f }, ShaderUniformDataType.Vec4);
Raylib.SetShaderValue(shader, roundedRectangle.BorderColorLoc, new[] { borderColor.R / 255.0f, borderColor.G / 255.0f, borderColor.B / 255.0f, borderColor.A / 255.0f }, ShaderUniformDataType.Vec4);
BeginShaderMode(shader);
DrawRectangle(0, 0, screenWidth, screenHeight, Color.White);
EndShaderMode();
// Draw one more rectangle with all three colors
rec = new Rectangle(240, 80, 500, 300);
DrawRectangleLines((int)rec.X - 30, (int)rec.Y - 30, (int)rec.Width + 60, (int)rec.Height + 60, Color.DarkGray);
DrawText("Rectangle with all three combined", (int)rec.X - 30, (int)rec.Y - 45, 10, Color.DarkGray);
rec.Y = screenHeight - rec.Y - rec.Height;
Raylib.SetShaderValue(shader, roundedRectangle.RectangleLoc, new[] { rec.X, rec.Y, rec.Width, rec.Height }, ShaderUniformDataType.Vec4);
// All three colors
Raylib.SetShaderValue(shader, roundedRectangle.ColorLoc, new[] { rectangleColor.R / 255.0f, rectangleColor.G / 255.0f, rectangleColor.B / 255.0f, rectangleColor.A / 255.0f }, ShaderUniformDataType.Vec4);
Raylib.SetShaderValue(shader, roundedRectangle.ShadowColorLoc, new[] { shadowColor.R / 255.0f, shadowColor.G / 255.0f, shadowColor.B / 255.0f, shadowColor.A / 255.0f }, ShaderUniformDataType.Vec4);
Raylib.SetShaderValue(shader, roundedRectangle.BorderColorLoc, new[] { borderColor.R / 255.0f, borderColor.G / 255.0f, borderColor.B / 255.0f, borderColor.A / 255.0f }, ShaderUniformDataType.Vec4);
BeginShaderMode(shader);
DrawRectangle(0, 0, screenWidth, screenHeight, Color.White);
EndShaderMode();
DrawText("(c) Rounded rectangle SDF by Iñigo Quilez. MIT License.", screenWidth - 300, screenHeight - 20, 10, Color.Black);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(shader); // Unload shader
}
// Create a rounded rectangle and set uniform locations
private static RoundedRect CreateRoundedRectangle(Vector4 cornerRadius, float shadowRadius, Vector2 shadowOffset, float shadowScale, float borderThickness, Shader shader)
{
RoundedRect rec;
rec.CornerRadius = cornerRadius;
rec.ShadowRadius = shadowRadius;
rec.ShadowOffset = shadowOffset;
rec.ShadowScale = shadowScale;
rec.BorderThickness = borderThickness;
// Get shader uniform locations
rec.RectangleLoc = GetShaderLocation(shader, "rectangle");
rec.RadiusLoc = GetShaderLocation(shader, "radius");
rec.ColorLoc = GetShaderLocation(shader, "color");
rec.ShadowRadiusLoc = GetShaderLocation(shader, "shadowRadius");
rec.ShadowOffsetLoc = GetShaderLocation(shader, "shadowOffset");
rec.ShadowScaleLoc = GetShaderLocation(shader, "shadowScale");
rec.ShadowColorLoc = GetShaderLocation(shader, "shadowColor");
rec.BorderThicknessLoc = GetShaderLocation(shader, "borderThickness");
rec.BorderColorLoc = GetShaderLocation(shader, "borderColor");
UpdateRoundedRectangle(rec, shader);
return rec;
}
// Update rounded rectangle uniforms
private static void UpdateRoundedRectangle(RoundedRect rec, Shader shader)
{
Raylib.SetShaderValue(shader, rec.RadiusLoc, new[] { rec.CornerRadius.X, rec.CornerRadius.Y, rec.CornerRadius.Z, rec.CornerRadius.W }, ShaderUniformDataType.Vec4);
Raylib.SetShaderValue(shader, rec.ShadowRadiusLoc, rec.ShadowRadius, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shader, rec.ShadowOffsetLoc, new[] { rec.ShadowOffset.X, rec.ShadowOffset.Y }, ShaderUniformDataType.Vec2);
Raylib.SetShaderValue(shader, rec.ShadowScaleLoc, rec.ShadowScale, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shader, rec.BorderThicknessLoc, rec.BorderThickness, ShaderUniformDataType.Float);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - rounded rectangle");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new RoundedRectangle();
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;
}
}

View file

@ -0,0 +1,316 @@
/*******************************************************************************************
*
* raylib [shaders] example - shadowmap rendering
*
* 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)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
using static Raylib_cs.Raymath;
using static Raylib_cs.Rlgl;
namespace Examples.Shaders;
public partial class ShadowmapRendering : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
private const int ShadowmapResolution = 1024;
public string Name => "Shaders / Shadowmap Rendering";
public string Title => "raylib [shaders] example - shadowmap rendering";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private Camera3D camera;
private Shader shadowShader;
private Vector3 lightDir;
private int lightDirLoc;
private int lightVPLoc;
private int shadowMapLoc;
private Model cube;
private Model robot;
private unsafe ModelAnimation* anims;
private int animCount;
private RenderTexture2D shadowMap;
private Camera3D lightCamera;
private int frameCounter;
private int textureActiveSlot;
public unsafe void Init()
{
// 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
camera = new();
camera.Position = new Vector3(10.0f, 10.0f, 10.0f);
camera.Target = Vector3.Zero;
camera.Projection = CameraProjection.Perspective;
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
shadowShader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/shadowmap.vs",
$"resources/shaders/glsl{GlslVersion}/shadowmap.fs"
);
shadowShader.Locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(shadowShader, "viewPos");
lightDir = Vector3Normalize(new Vector3(0.35f, -1.0f, -0.35f));
var lightColor = Color.White;
var lightColorNormalized = ColorNormalize(lightColor);
lightDirLoc = GetShaderLocation(shadowShader, "lightDir");
var lightColLoc = GetShaderLocation(shadowShader, "lightColor");
Raylib.SetShaderValue(shadowShader, lightDirLoc, lightDir, ShaderUniformDataType.Vec3);
Raylib.SetShaderValue(shadowShader, lightColLoc, lightColorNormalized, ShaderUniformDataType.Vec4);
var ambientLoc = GetShaderLocation(shadowShader, "ambient");
var ambient = new[] { 0.1f, 0.1f, 0.1f, 1.0f };
Raylib.SetShaderValue(shadowShader, ambientLoc, ambient, ShaderUniformDataType.Vec4);
lightVPLoc = GetShaderLocation(shadowShader, "lightVP");
shadowMapLoc = GetShaderLocation(shadowShader, "shadowMap");
var shadowMapResolution = ShadowmapResolution;
Raylib.SetShaderValue(shadowShader, GetShaderLocation(shadowShader, "shadowMapResolution"), shadowMapResolution, ShaderUniformDataType.Int);
cube = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f));
cube.Materials[0].Shader = shadowShader;
robot = LoadModel("resources/models/robot.glb");
for (var i = 0; i < robot.MaterialCount; i++)
{
robot.Materials[i].Shader = shadowShader;
}
animCount = 0;
anims = LoadModelAnimations("resources/models/robot.glb", ref animCount);
shadowMap = LoadShadowmapRenderTexture(ShadowmapResolution, ShadowmapResolution);
// For the shadowmapping algorithm, we will be rendering everything from the light's point of view
lightCamera = new();
lightCamera.Position = Vector3Scale(lightDir, -15.0f);
lightCamera.Target = Vector3.Zero;
lightCamera.Projection = CameraProjection.Orthographic; // Use an orthographic projection for directional lights
lightCamera.Up = new Vector3(0.0f, 1.0f, 0.0f);
lightCamera.FovY = 20.0f;
frameCounter = 0;
textureActiveSlot = 10; // Can be anything 0 to 15, but 0 will probably be taken up
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
var deltaTime = GetFrameTime();
var cameraPos = camera.Position;
Raylib.SetShaderValue(shadowShader, shadowShader.Locs[(int)ShaderLocationIndex.VectorView], cameraPos, ShaderUniformDataType.Vec3);
UpdateCamera(ref camera, CameraMode.Orbital);
frameCounter++;
frameCounter %= anims[0].KeyFrameCount;
UpdateModelAnimation(robot, anims[0], (float)frameCounter);
// Move light with arrow keys
const float cameraSpeed = 0.05f;
if (IsKeyDown(KeyboardKey.Left))
{
if (lightDir.X < 0.6f)
{
lightDir.X += cameraSpeed * 60.0f * deltaTime;
}
}
if (IsKeyDown(KeyboardKey.Right))
{
if (lightDir.X > -0.6f)
{
lightDir.X -= cameraSpeed * 60.0f * deltaTime;
}
}
if (IsKeyDown(KeyboardKey.Up))
{
if (lightDir.Z < 0.6f)
{
lightDir.Z += cameraSpeed * 60.0f * deltaTime;
}
}
if (IsKeyDown(KeyboardKey.Down))
{
if (lightDir.Z > -0.6f)
{
lightDir.Z -= cameraSpeed * 60.0f * deltaTime;
}
}
lightDir = Vector3Normalize(lightDir);
lightCamera.Position = Vector3Scale(lightDir, -15.0f);
Raylib.SetShaderValue(shadowShader, lightDirLoc, lightDir, ShaderUniformDataType.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
Matrix4x4 lightView;
Matrix4x4 lightProj;
BeginTextureMode(shadowMap);
ClearBackground(Color.White);
BeginMode3D(lightCamera);
lightView = GetMatrixModelview();
lightProj = GetMatrixProjection();
DrawScene(cube, robot);
EndMode3D();
EndTextureMode();
var lightViewProj = MatrixMultiply(lightView, lightProj);
// PASS 02: Draw the scene into main framebuffer, using the generated shadowmap
BeginDrawing();
ClearBackground(Color.RayWhite);
SetShaderValueMatrix(shadowShader, lightVPLoc, lightViewProj);
EnableShader(shadowShader.Id);
ActiveTextureSlot(textureActiveSlot);
EnableTexture(shadowMap.Depth.Id);
var slot = textureActiveSlot;
SetUniform(shadowMapLoc, &slot, (int)ShaderUniformDataType.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, Color.Red);
DrawText("Shadows in raylib using the shadowmapping algorithm!", screenWidth - 280, screenHeight - 20, 10, Color.Gray);
EndDrawing();
if (IsKeyPressed(KeyboardKey.F))
{
TakeScreenshot("shaders_shadowmap.png");
}
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
UnloadShader(shadowShader);
UnloadModel(cube);
UnloadModel(robot);
UnloadModelAnimations(anims, animCount);
UnloadShadowmapRenderTexture(shadowMap);
}
// Load render texture for shadowmap projection
// NOTE: Load framebuffer with only a texture depth attachment,
// no color attachment required for shadowmap
private static unsafe RenderTexture2D LoadShadowmapRenderTexture(int width, int height)
{
RenderTexture2D target = new();
target.Id = LoadFramebuffer(); // Load an empty framebuffer
target.Texture.Width = width;
target.Texture.Height = height;
if (target.Id > 0)
{
EnableFramebuffer(target.Id);
// Create depth texture
// NOTE: No need a color texture attachment for the shadowmap
target.Depth.Id = LoadTextureDepth(width, height, false);
target.Depth.Width = width;
target.Depth.Height = height;
target.Depth.Format = (PixelFormat)19; // DEPTH_COMPONENT_24BIT?
target.Depth.Mipmaps = 1;
// Attach depth texture to FBO
FramebufferAttach(target.Id, target.Depth.Id, FramebufferAttachType.Depth, FramebufferAttachTextureType.Texture2D, 0);
// Check if fbo is complete with attachments (valid)
if (FramebufferComplete(target.Id) != 0)
{
TraceLog(TraceLogLevel.Info, $"FBO: [ID {target.Id}] Framebuffer object created successfully");
}
DisableFramebuffer();
}
else
{
TraceLog(TraceLogLevel.Warning, "FBO: Framebuffer object can not be created");
}
return target;
}
// Unload shadowmap render texture from GPU memory (VRAM)
private static void UnloadShadowmapRenderTexture(RenderTexture2D target)
{
if (target.Id > 0)
{
// NOTE: Depth texture/renderbuffer is automatically
// queried and deleted before deleting framebuffer
UnloadFramebuffer(target.Id);
}
}
// Draw full scene projecting shadows
// NOTE: Required to be called several time to generate shadowmap
private static void DrawScene(Model cube, Model robot)
{
DrawModelEx(cube, Vector3.Zero, new Vector3(0.0f, 1.0f, 0.0f), 0.0f, new Vector3(10.0f, 1.0f, 10.0f), Color.Blue);
DrawModelEx(cube, new Vector3(1.5f, 1.0f, -1.5f), new Vector3(0.0f, 1.0f, 0.0f), 0.0f, Vector3.One, Color.White);
DrawModelEx(robot, new Vector3(0.0f, 0.5f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f), 0.0f, new Vector3(1.0f, 1.0f, 1.0f), Color.Red);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shadowmap rendering");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new ShadowmapRendering();
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;
}
}

View file

@ -0,0 +1,143 @@
/*******************************************************************************************
*
* raylib [shaders] example - texture tiling
*
* Example complexity rating: [] 2/4
*
* Example demonstrates how to tile a texture on a 3D model using raylib
*
* Example originally created with raylib 4.5, last time updated with raylib 4.5
*
* Example contributed by Luis Almeida (@luis605) 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 Luis Almeida (@luis605)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public class TextureTiling : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Texture Tiling";
public string Title => "raylib [shaders] example - texture tiling";
public bool CursorDisabled => true;
private Camera3D camera;
private Model model;
private Texture2D texture;
private Shader shader;
public unsafe void Init()
{
// Define the camera to look into our 3d world
camera = new();
camera.Position = new Vector3(4.0f, 4.0f, 4.0f); // Camera position
camera.Target = new Vector3(0.0f, 0.5f, 0.0f); // Camera looking at point
camera.Up = new Vector3(0.0f, 1.0f, 0.0f); // Camera up vector (rotation towards target)
camera.FovY = 45.0f; // Camera field-of-view Y
camera.Projection = CameraProjection.Perspective; // Camera projection type
// Load a cube model
var cube = GenMeshCube(1.0f, 1.0f, 1.0f);
model = LoadModelFromMesh(cube);
// Load a texture and assign to cube model
texture = LoadTexture("resources/cubicmap_atlas.png");
model.Materials[0].Maps[(int)MaterialMapIndex.Diffuse].Texture = texture;
// Set the texture tiling using a shader
var tiling = new[] { 3.0f, 3.0f };
shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/tiling.fs");
SetTextureWrap(texture, TextureWrap.Repeat);
Raylib.SetShaderValue(shader, GetShaderLocation(shader, "tiling"), tiling, ShaderUniformDataType.Vec2);
model.Materials[0].Shader = shader;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
if (IsKeyPressed(KeyboardKey.Z))
{
camera.Target = new Vector3(0.0f, 0.5f, 0.0f);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
BeginShaderMode(shader);
DrawModel(model, new Vector3(0.0f, 0.0f, 0.0f), 2.0f, Color.White);
EndShaderMode();
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("Use mouse to rotate the camera", 10, 10, 20, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(model); // Unload model
UnloadShader(shader); // Unload shader
UnloadTexture(texture); // Unload texture
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture tiling");
DisableCursor(); // Limit cursor to relative movement inside the window
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new TextureTiling();
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;
}
}

View file

@ -0,0 +1,148 @@
/*******************************************************************************************
*
* raylib [shaders] example - vertex displacement
*
* Example complexity rating: [] 3/4
*
* Example originally created with raylib 5.0, last time updated with raylib 4.5
*
* Example contributed by Alex ZH (@ZzzhHe) 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 Alex ZH (@ZzzhHe)
*
********************************************************************************************/
using System.Numerics;
using static Raylib_cs.Raylib;
using static Raylib_cs.Rlgl;
namespace Examples.Shaders;
public partial class VertexDisplacement : IExample
{
private const int screenWidth = 800;
private const int screenHeight = 450;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Vertex Displacement";
public string Title => "raylib [shaders] example - vertex displacement";
private Camera3D camera;
private Shader shader;
private Texture2D perlinNoiseMap;
private Model planeModel;
private float time;
public unsafe void Init()
{
// set up camera
camera = new();
camera.Position = new Vector3(20.0f, 5.0f, -20.0f);
camera.Target = new Vector3(0.0f, 0.0f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 60.0f;
camera.Projection = CameraProjection.Perspective;
// Load vertex and fragment shaders
shader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/vertex_displacement.vs",
$"resources/shaders/glsl{GlslVersion}/vertex_displacement.fs"
);
// Load perlin noise texture
var perlinNoiseImage = GenImagePerlinNoise(512, 512, 0, 0, 1.0f);
perlinNoiseMap = LoadTextureFromImage(perlinNoiseImage);
UnloadImage(perlinNoiseImage);
// Set shader uniform location
var perlinNoiseMapLoc = GetShaderLocation(shader, "perlinNoiseMap");
EnableShader(shader.Id);
ActiveTextureSlot(1);
EnableTexture(perlinNoiseMap.Id);
SetUniformSampler(perlinNoiseMapLoc, 1);
// Create a plane mesh and model
var planeMesh = GenMeshPlane(50, 50, 50, 50);
planeModel = LoadModelFromMesh(planeMesh);
// Set plane model material
var materials = planeModel.Materials;
materials[0].Shader = shader;
time = 0.0f;
}
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free); // Update camera
time += GetFrameTime(); // Update time variable
Raylib.SetShaderValue(shader, GetShaderLocation(shader, "time"), time, ShaderUniformDataType.Float); // Send time value to shader
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
BeginShaderMode(shader);
// Draw plane model
DrawModel(planeModel, new Vector3(0.0f, 0.0f, 0.0f), 1.0f, new Color(255, 255, 255, 255));
EndShaderMode();
EndMode3D();
DrawText("Vertex displacement", 10, 10, 20, Color.DarkGray);
DrawFPS(10, 40);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(shader);
UnloadModel(planeModel);
UnloadTexture(perlinNoiseMap);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - vertex displacement");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new VertexDisplacement();
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;
}
}