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

feat: backporting existing raylib-cs examples and new official raylib examples to WASM, adopting raylib's original code style

This commit is contained in:
tiger tiger tiger 2026-07-17 08:22:54 +02:00
commit f804ab7773
35 changed files with 38997 additions and 10578 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

@ -2,26 +2,21 @@
*
* raylib [shaders] example - basic lighting
*
* 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.
* 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).
* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
*
* This example has been created using raylib 2.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 3.0, last time updated with raylib 4.2
*
* Example contributed by Chris Camacho (@codifies) and reviewed by Ramon Santamaria (@raysan5)
* Example contributed by Chris Camacho (@chriscamacho) and reviewed by Ramon Santamaria (@raysan5)
*
* Chris Camacho (@codifies - http://bedroomcoders.co.uk/) notes:
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* This is based on the PBR lighting example, but greatly simplified to aid learning...
* actually there is very little of the PBR example left!
* When I first looked at the bewildering complexity of the PBR example I feared
* I would never understand how I could do simple lighting with raylib however its
* a testement to the authors of raylib (including rlights.h) that the example
* came together fairly quickly.
*
* Copyright (c) 2019 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5)
* Copyright (c) 2019-2025 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -31,52 +26,55 @@ using Examples.Shared;
namespace Examples.Shaders;
public class BasicLighting
public class BasicLighting : IExample
{
const int GLSL_VERSION = 330;
private const int screenWidth = 800;
private const int screenHeight = 450;
public unsafe static int Main()
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Basic Lighting";
public string Title => "raylib [shaders] example - basic lighting";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private Camera3D camera;
private Shader shader;
private Light[] lights;
public unsafe void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
// Enable Multi Sampling Anti Aliasing 4x (if available)
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic lighting");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(2.0f, 4.0f, 6.0f);
camera.Target = new Vector3(0.0f, 0.5f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(2.0f, 4.0f, 6.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 plane model from a generated mesh
Model model = LoadModelFromMesh(GenMeshPlane(10.0f, 10.0f, 3, 3));
Model cube = LoadModelFromMesh(GenMeshCube(2.0f, 4.0f, 2.0f));
Shader shader = LoadShader(
"resources/shaders/glsl330/lighting.vs",
"resources/shaders/glsl330/lighting.fs"
// Load basic lighting shader
shader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/lighting.vs",
$"resources/shaders/glsl{GlslVersion}/lighting.fs"
);
// Get some required shader loactions
// Get some required shader locations
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");
// ambient light level
int ambientLoc = GetShaderLocation(shader, "ambient");
float[] ambient = new[] { 0.1f, 0.1f, 0.1f, 1.0f };
// Ambient light level (some basic lighting)
var ambientLoc = GetShaderLocation(shader, "ambient");
var ambient = new[] { 0.1f, 0.1f, 0.1f, 1.0f };
Raylib.SetShaderValue(shader, ambientLoc, ambient, ShaderUniformDataType.Vec4);
// Assign out lighting shader to model
model.Materials[0].Shader = shader;
cube.Materials[0].Shader = shader;
// Using 4 point lights: Color.gold, Color.red, Color.green and Color.blue
Light[] lights = new Light[4];
// Create lights
lights = new Light[4];
lights[0] = Rlights.CreateLight(
0,
LightType.Point,
@ -109,114 +107,116 @@ public class BasicLighting
Color.Blue,
shader
);
}
SetTargetFPS(60);
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(
shader,
shader.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;
}
// Update light values (actually, only enable/disable them)
for (var i = 0; i < 4; i++)
{
Rlights.UpdateLightValues(shader, lights[i]);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
BeginShaderMode(shader);
DrawPlane(Vector3.Zero, new Vector2(10.0f, 10.0f), Color.White);
DrawCube(Vector3.Zero, 2.0f, 4.0f, 2.0f, Color.White);
EndShaderMode();
// Draw spheres to show where the lights are
for (var i = 0; i < 4; 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));
}
}
DrawGrid(10, 1.0f);
EndMode3D();
DrawFPS(10, 10);
DrawText("Use keys [Y][R][G][B] to toggle lights", 10, 40, 20, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(shader); // Unload shader
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint); // Enable Multi Sampling Anti Aliasing 4x (if available)
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic lighting");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new BasicLighting();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
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;
}
// Update light values (actually, only enable/disable them)
Rlights.UpdateLightValues(shader, lights[0]);
Rlights.UpdateLightValues(shader, lights[1]);
Rlights.UpdateLightValues(shader, lights[2]);
Rlights.UpdateLightValues(shader, lights[3]);
// Update the light shader with the camera view position
Raylib.SetShaderValue(
shader,
shader.Locs[(int)ShaderLocationIndex.VectorView],
camera.Position,
ShaderUniformDataType.Vec3
);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, Vector3.Zero, 1.0f, Color.White);
DrawModel(cube, Vector3.Zero, 1.0f, Color.White);
// Draw markers to show where the lights are
if (lights[0].Enabled)
{
DrawSphereEx(lights[0].Position, 0.2f, 8, 8, Color.Yellow);
}
else
{
DrawSphereWires(lights[0].Position, 0.2f, 8, 8, ColorAlpha(Color.Yellow, 0.3f));
}
if (lights[1].Enabled)
{
DrawSphereEx(lights[1].Position, 0.2f, 8, 8, Color.Red);
}
else
{
DrawSphereWires(lights[1].Position, 0.2f, 8, 8, ColorAlpha(Color.Red, 0.3f));
}
if (lights[2].Enabled)
{
DrawSphereEx(lights[2].Position, 0.2f, 8, 8, Color.Green);
}
else
{
DrawSphereWires(lights[2].Position, 0.2f, 8, 8, ColorAlpha(Color.Green, 0.3f));
}
if (lights[3].Enabled)
{
DrawSphereEx(lights[3].Position, 0.2f, 8, 8, Color.Blue);
}
else
{
DrawSphereWires(lights[3].Position, 0.2f, 8, 8, ColorAlpha(Color.Blue, 0.3f));
}
DrawGrid(10, 1.0f);
EndMode3D();
DrawFPS(10, 10);
DrawText("Use keys [Y][R][G][B] to toggle lights", 10, 40, 20, Color.DarkGray);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(model);
UnloadModel(cube);
UnloadShader(shader);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,21 +1,23 @@
/*******************************************************************************************
*
* raylib [shaders] example - Basic PBR
*
* Example originally created with raylib 5.0, last time updated with raylib 5.1-dev
*
* Example contributed by Afan OLOVCIC (@_DevDad) 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-2024 Afan OLOVCIC (@_DevDad)
*
* Model: "Old Rusty Car" (https://skfb.ly/LxRy) by Renafox,
* licensed under Creative Commons Attribution-NonCommercial
* (http://creativecommons.org/licenses/by-nc/4.0/)
*
********************************************************************************************/
*
* raylib [shaders] example - basic pbr
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 5.0, last time updated with raylib 5.5
*
* Example contributed by Afan OLOVCIC (@_DevDad) 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 Afan OLOVCIC (@_DevDad)
*
* Model: "Old Rusty Car" (https://skfb.ly/LxRy) by Renafox,
* licensed under Creative Commons Attribution-NonCommercial
* (http://creativecommons.org/licenses/by-nc/4.0/)
*
********************************************************************************************/
using System.Numerics;
using Examples.Shared;
@ -23,31 +25,50 @@ using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public class BasicPbr
public class BasicPbr : IExample
{
private const int GLSL_VERSION = 330;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public static unsafe int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Shaders / Basic PBR";
public string Title => "raylib [shaders] example - basic pbr";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private Camera3D camera;
private Shader shader;
private Model car;
private Model floor;
private PbrLight[] lights;
private int metallicValueLoc;
private int roughnessValueLoc;
private int emissiveIntensityLoc;
private int emissiveColorLoc;
private int textureTilingLoc;
private Vector2 carTextureTiling;
private Vector2 floorTextureTiling;
public unsafe void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
// Enable Multi Sampling Anti Aliasing 4x (if available)
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic pbr");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(2.0f, 4.0f, 6.0f);
camera = new();
camera.Position = new Vector3(2.0f, 2.0f, 6.0f);
camera.Target = new Vector3(0.0f, 0.5f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
// Load PBR shader and setup all required locations
var shader = LoadShader("resources/shaders/glsl330/pbr.vs", "resources/shaders/glsl330/pbr.fs");
shader = LoadShader($"resources/shaders/glsl{GlslVersion}/pbr.vs", $"resources/shaders/glsl{GlslVersion}/pbr.fs");
shader.Locs[(int)ShaderLocationIndex.MapAlbedo] = GetShaderLocation(shader, "albedoMap");
// WARNING: Metalness, roughness, and ambient occlusion are all packed into a MRA texture
@ -75,23 +96,25 @@ public class BasicPbr
SetShaderValue(shader, GetShaderLocation(shader, "ambient"), &ambientIntensity, ShaderUniformDataType.Float);
// Get location for shader parameters that can be modified in real time
var emissiveIntensityLoc = GetShaderLocation(shader, "emissivePower");
var emissiveColorLoc = GetShaderLocation(shader, "emissiveColor");
var textureTilingLoc = GetShaderLocation(shader, "tiling");
metallicValueLoc = GetShaderLocation(shader, "metallicValue");
roughnessValueLoc = GetShaderLocation(shader, "roughnessValue");
emissiveIntensityLoc = GetShaderLocation(shader, "emissivePower");
emissiveColorLoc = GetShaderLocation(shader, "emissiveColor");
textureTilingLoc = GetShaderLocation(shader, "tiling");
// Load old car model using PBR maps and shader
// WARNING: We know this model consists of a single model.meshes[0] and
// that model.materials[0] is by default assigned to that mesh
// There could be more complex models consisting of multiple meshes and
// multiple materials defined for those meshes... but always 1 mesh = 1 material
var car = LoadModel("resources/models/gltf/old_car_new.glb");
car = LoadModel("resources/models/gltf/old_car_new.glb");
// Assign already setup PBR shader to model.materials[0], used by models.meshes[0]
car.Materials[0].Shader = shader;
// Setup materials[0].maps default parameters
car.Materials[0].Maps[(int)MaterialMapIndex.Albedo].Color = Color.White;
car.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 0.0f;
car.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 1.0f;
car.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value = 0.0f;
car.Materials[0].Maps[(int)MaterialMapIndex.Occlusion].Value = 1.0f;
car.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color = new Color(255, 162, 0, 255);
@ -104,7 +127,7 @@ public class BasicPbr
// Load floor model mesh and assign material parameters
// NOTE: A basic plane shape can be generated instead of being loaded from a model file
var floor = LoadModel("resources/models/gltf/plane.glb");
floor = LoadModel("resources/models/gltf/plane.glb");
//Mesh floorMesh = GenMeshPlane(10, 10, 10, 10);
//GenMeshTangents(&floorMesh); // TODO: Review tangents generation
//Model floor = LoadModelFromMesh(floorMesh);
@ -113,8 +136,8 @@ public class BasicPbr
floor.Materials[0].Shader = shader;
floor.Materials[0].Maps[(int)MaterialMapIndex.Albedo].Color = Color.White;
floor.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 0.0f;
floor.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value = 0.0f;
floor.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 0.8f;
floor.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value = 0.1f;
floor.Materials[0].Maps[(int)MaterialMapIndex.Occlusion].Value = 1.0f;
floor.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color = Color.Black;
@ -124,11 +147,11 @@ public class BasicPbr
// Models texture tiling parameter can be stored in the Material struct if required (CURRENTLY NOT USED)
// NOTE: Material.params[4] are available for generic parameters storage (float)
var carTextureTiling = new Vector2(0.5f, 0.5f);
var floorTextureTiling = new Vector2(0.5f, 0.5f);
carTextureTiling = new Vector2(0.5f, 0.5f);
floorTextureTiling = new Vector2(0.5f, 0.5f);
// Create some lights
var lights = new PbrLight[4];
lights = new PbrLight[4];
lights[0] = PbrLights.CreateLight(
0,
PbrLightType.Point,
@ -167,104 +190,113 @@ public class BasicPbr
SetShaderValue(shader, GetShaderLocation(shader, "useTexNormal"), &usage, ShaderUniformDataType.Int);
SetShaderValue(shader, GetShaderLocation(shader, "useTexMRA"), &usage, ShaderUniformDataType.Int);
SetShaderValue(shader, GetShaderLocation(shader, "useTexEmissive"), &usage, ShaderUniformDataType.Int);
}
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//---------------------------------------------------------------------------------------
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
var cameraPos = camera.Position;
SetShaderValue(shader, shader.Locs[(int)ShaderLocationIndex.VectorView], cameraPos, ShaderUniformDataType.Vec3);
// Check key inputs to enable/disable lights
if (IsKeyPressed(KeyboardKey.One))
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(&camera, CameraMode.Orbital);
// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
var cameraPos = camera.Position;
SetShaderValue(shader, shader.Locs[(int)ShaderLocationIndex.VectorView], cameraPos, ShaderUniformDataType.Vec3);
// Check key inputs to enable/disable lights
if (IsKeyPressed(KeyboardKey.One))
{
lights[2].Enabled = !lights[2].Enabled;
}
if (IsKeyPressed(KeyboardKey.Two))
{
lights[1].Enabled = !lights[1].Enabled;
}
if (IsKeyPressed(KeyboardKey.Three))
{
lights[3].Enabled = !lights[3].Enabled;
}
if (IsKeyPressed(KeyboardKey.Four))
{
lights[0].Enabled = !lights[0].Enabled;
}
// Update light values on shader (actually, only enable/disable them)
for (var i = 0; i < 4; i++)
{
UpdateLight(shader, lights[i]);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.Black);
BeginMode3D(camera);
// Set floor model texture tiling and emissive color parameters on shader
SetShaderValue(shader, textureTilingLoc, &floorTextureTiling, ShaderUniformDataType.Vec2);
var floorEmissiveColor = ColorNormalize(floor.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color);
SetShaderValue(shader, emissiveColorLoc, &floorEmissiveColor, ShaderUniformDataType.Vec4);
DrawModel(floor, Vector3.Zero, 5.0f, Color.White); // Draw floor model
// Set old car model texture tiling, emissive color and emissive intensity parameters on shader
SetShaderValue(shader, textureTilingLoc, &carTextureTiling, ShaderUniformDataType.Vec2);
var carEmissiveColor = ColorNormalize(car.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color);
SetShaderValue(shader, emissiveColorLoc, &carEmissiveColor, ShaderUniformDataType.Vec4);
var emissiveIntensity = 0.01f;
SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, ShaderUniformDataType.Float);
DrawModel(car, Vector3.Zero, 0.25f, Color.White); // Draw car model
// Draw spheres to show the lights positions
for (var i = 0; i < 4; i++)
{
var color = lights[i].Color;
var lightColor = new Color((byte)(color.X * 255), (byte)(color.Y * 255), (byte)(color.Z * 255),
(byte)(color.W * 255));
if (lights[i].Enabled)
{
DrawSphereEx(lights[i].Position, 0.2f, 8, 8, lightColor);
}
else
{
DrawSphereWires(lights[i].Position, 0.2f, 8, 8, ColorAlpha(lightColor, 0.3f));
}
}
EndMode3D();
DrawText("Toggle lights: [1][2][3][4]", 10, 40, 20, Color.LightGray);
DrawText("(c) Old Rusty Car model by Renafox (https://skfb.ly/LxRy)", screenWidth - 320, screenHeight - 20, 10, Color.LightGray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
lights[2].Enabled = !lights[2].Enabled;
}
// De-Initialization
//--------------------------------------------------------------------------------------
if (IsKeyPressed(KeyboardKey.Two))
{
lights[1].Enabled = !lights[1].Enabled;
}
if (IsKeyPressed(KeyboardKey.Three))
{
lights[3].Enabled = !lights[3].Enabled;
}
if (IsKeyPressed(KeyboardKey.Four))
{
lights[0].Enabled = !lights[0].Enabled;
}
// Update light values on shader (actually, only enable/disable them)
for (var i = 0; i < 4; i++)
{
UpdateLight(shader, lights[i]);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.Black);
BeginMode3D(camera);
// Set floor model texture tiling and emissive color parameters on shader
SetShaderValue(shader, textureTilingLoc, floorTextureTiling, ShaderUniformDataType.Vec2);
var floorEmissiveColor = ColorNormalize(floor.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color);
SetShaderValue(shader, emissiveColorLoc, &floorEmissiveColor, ShaderUniformDataType.Vec4);
// Set floor metallic and roughness values
var floorMetallicValue = floor.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value;
SetShaderValue(shader, metallicValueLoc, &floorMetallicValue, ShaderUniformDataType.Float);
var floorRoughnessValue = floor.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value;
SetShaderValue(shader, roughnessValueLoc, &floorRoughnessValue, ShaderUniformDataType.Float);
DrawModel(floor, Vector3.Zero, 5.0f, Color.White); // Draw floor model
// Set old car model texture tiling, emissive color and emissive intensity parameters on shader
SetShaderValue(shader, textureTilingLoc, carTextureTiling, ShaderUniformDataType.Vec2);
var carEmissiveColor = ColorNormalize(car.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color);
SetShaderValue(shader, emissiveColorLoc, &carEmissiveColor, ShaderUniformDataType.Vec4);
var emissiveIntensity = 0.01f;
SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, ShaderUniformDataType.Float);
// Set old car metallic and roughness values
var carMetallicValue = car.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value;
SetShaderValue(shader, metallicValueLoc, &carMetallicValue, ShaderUniformDataType.Float);
var carRoughnessValue = car.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value;
SetShaderValue(shader, roughnessValueLoc, &carRoughnessValue, ShaderUniformDataType.Float);
DrawModel(car, Vector3.Zero, 0.25f, Color.White); // Draw car model
// Draw spheres to show the lights positions
for (var i = 0; i < 4; i++)
{
var color = lights[i].Color;
var lightColor = new Color((byte)(color.X * 255), (byte)(color.Y * 255), (byte)(color.Z * 255),
(byte)(color.W * 255));
if (lights[i].Enabled)
{
DrawSphereEx(lights[i].Position, 0.2f, 8, 8, lightColor);
}
else
{
DrawSphereWires(lights[i].Position, 0.2f, 8, 8, ColorAlpha(lightColor, 0.3f));
}
}
EndMode3D();
DrawText("Toggle lights: [1][2][3][4]", 10, 40, 20, Color.LightGray);
DrawText("(c) Old Rusty Car model by Renafox (https://skfb.ly/LxRy)", screenWidth - 320, screenHeight - 20, 10, Color.LightGray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public unsafe void Unload()
{
// Unbind (disconnect) shader from car.material[0]
// to avoid UnloadMaterial() trying to unload it automatically
car.Materials[0].Shader = new();
@ -278,7 +310,32 @@ public class BasicPbr
UnloadModel(floor);
UnloadShader(shader);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
// Enable Multi Sampling Anti Aliasing 4x (if available)
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic pbr");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//---------------------------------------------------------------------------------------
var game = new BasicPbr();
game.Init();
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------
@ -287,8 +344,8 @@ public class BasicPbr
private static void UpdateLight(Shader shader, PbrLight light)
{
SetShaderValue(shader, light.EnabledLoc, light.Enabled, ShaderUniformDataType.Int);
SetShaderValue(shader, light.TypeLoc, light.Type, ShaderUniformDataType.Int);
SetShaderValue(shader, light.EnabledLoc, light.Enabled ? 1 : 0, ShaderUniformDataType.Int);
SetShaderValue(shader, light.TypeLoc, (int)light.Type, ShaderUniformDataType.Int);
// Send to shader light position values
SetShaderValue(shader, light.PositionLoc, light.Position, ShaderUniformDataType.Vec3);

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

@ -1,18 +1,22 @@
/*******************************************************************************************
*
* raylib [shaders] example - Apply a postprocessing shader and connect a custom uniform variable
* raylib [shaders] example - custom uniform
*
* 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.
* 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), to test this example
* on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders
* raylib comes with shaders ready for both versions, check raylib/shaders install folder
*
* This example has been created using raylib 1.3 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 1.3, last time updated with raylib 4.0
*
* Copyright (c) 2015 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) 2015-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -21,123 +25,156 @@ using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public class CustomUniform
public class CustomUniform : IExample
{
public static int Main()
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Shaders / Custom Uniform";
public string Title => "raylib [shaders] example - custom uniform";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private Camera3D camera;
private Model model;
private Texture2D texture;
private Vector3 position;
private Shader shader;
private int swirlCenterLoc;
private float[] swirlCenter;
private RenderTexture2D target;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
// Enable Multi Sampling Anti Aliasing 4x (if available)
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - custom uniform variable");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(8.0f, 8.0f, 8.0f);
camera.Target = new Vector3(0.0f, 1.5f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(8.0f, 8.0f, 8.0f); // Camera position
camera.Target = new Vector3(0.0f, 1.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
Model model = LoadModel("resources/models/obj/barracks.obj");
Texture2D texture = LoadTexture("resources/models/obj/barracks_diffuse.png");
model = LoadModel("resources/models/obj/barracks.obj"); // Load OBJ model
texture = LoadTexture("resources/models/obj/barracks_diffuse.png"); // Load model texture (diffuse map)
// Set model diffuse texture
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
Vector3 position = new(0.0f, 0.0f, 0.0f);
position = new(0.0f, 0.0f, 0.0f); // Set model position
// Load postpro shader
Shader shader = LoadShader("resources/shaders/glsl330/base.vs",
"resources/shaders/glsl330/swirl.fs");
// Load postprocessing shader
// NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/swirl.fs");
// Get variable (uniform) location on the shader to connect with the program
// NOTE: If uniform variable could not be found in the shader, function returns -1
int swirlCenterLoc = GetShaderLocation(shader, "center");
swirlCenterLoc = GetShaderLocation(shader, "center");
float[] swirlCenter = new float[2] { (float)screenWidth / 2, (float)screenHeight / 2 };
swirlCenter = new float[2] { (float)screenWidth / 2, (float)screenHeight / 2 };
// Create a RenderTexture2D to be used for render to texture
RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
target = LoadRenderTexture(screenWidth, screenHeight);
}
SetTargetFPS(60);
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
var mousePosition = GetMousePosition();
swirlCenter[0] = mousePosition.X;
swirlCenter[1] = screenHeight - mousePosition.Y;
// Send new value to the shader to be used on drawing
Raylib.SetShaderValue(shader, swirlCenterLoc, swirlCenter, ShaderUniformDataType.Vec2);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginTextureMode(target); // Enable drawing to texture
ClearBackground(Color.RayWhite); // Clear texture background
BeginMode3D(camera); // Begin 3d mode drawing
DrawModel(model, position, 0.5f, Color.White); // Draw 3d model with texture
DrawGrid(10, 1.0f); // Draw a grid
EndMode3D(); // End 3d mode drawing, returns to orthographic 2d mode
DrawText("TEXT DRAWN IN RENDER TEXTURE", 200, 10, 30, Color.Red);
EndTextureMode(); // End drawing to texture (now we have a texture available for next passes)
BeginDrawing();
ClearBackground(Color.RayWhite); // Clear screen background
// Enable shader using the custom uniform
BeginShaderMode(shader);
// NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom)
DrawTextureRec(
target.Texture,
new Rectangle(0, 0, target.Texture.Width, -target.Texture.Height),
new Vector2(0, 0),
Color.White
);
EndShaderMode();
// Draw some 2d text over drawn texture
DrawText(
"(c) Barracks 3D model by Alberto Cano",
screenWidth - 220,
screenHeight - 20,
10,
Color.Gray
);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(shader); // Unload shader
UnloadTexture(texture); // Unload texture
UnloadModel(model); // Unload model
UnloadRenderTexture(target); // Unload render texture
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
SetConfigFlags(ConfigFlags.Msaa4xHint); // Enable Multi Sampling Anti Aliasing 4x (if available)
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - custom uniform");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new CustomUniform();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
Vector2 mousePosition = GetMousePosition();
swirlCenter[0] = mousePosition.X;
swirlCenter[1] = screenHeight - mousePosition.Y;
// Send new value to the shader to be used on drawing
Raylib.SetShaderValue(shader, swirlCenterLoc, swirlCenter, ShaderUniformDataType.Vec2);
UpdateCamera(ref camera, CameraMode.Orbital);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Enable drawing to texture
BeginTextureMode(target);
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 0.5f, Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText("TEXT DRAWN IN RENDER TEXTURE", 200, 10, 30, Color.Red);
// End drawing to texture (now we have a texture available for next passes)
EndTextureMode();
BeginShaderMode(shader);
// NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom)
DrawTextureRec(
target.Texture,
new Rectangle(0, 0, target.Texture.Width, -target.Texture.Height),
new Vector2(0, 0),
Color.White
);
EndShaderMode();
DrawText(
"(c) Barracks 3D model by Alberto Cano",
screenWidth - 220,
screenHeight - 20,
10,
Color.Gray
);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadShader(shader);
UnloadTexture(texture);
UnloadModel(model);
UnloadRenderTexture(target);
CloseWindow();
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

@ -1,25 +1,29 @@
/*******************************************************************************************
*
* raylib [shaders] example - Sieve of Eratosthenes
* raylib [shaders] example - eratosthenes sieve
*
* Sieve of Eratosthenes, the earliest known (ancient Greek) prime number sieve.
* Example complexity rating: [★★★☆] 3/4
*
* "Sift the twos and sift the threes,
* The Sieve of Eratosthenes.
* When the multiples sublime,
* the numbers that are left are prime."
* NOTE: Sieve of Eratosthenes, the earliest known (ancient Greek) prime number sieve
*
* "Sift the twos and sift the threes,
* The Sieve of Eratosthenes.
* When the multiples sublime,
* the numbers that are left are prime."
*
* 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.
* 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).
* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
*
* This example has been created using raylib 2.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 2.5, last time updated with raylib 4.0
*
* Example contributed by ProfJski and reviewed by Ramon Santamaria (@raysan5)
* Example contributed by ProfJski (@ProfJski) and reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2019 ProfJski and 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 ProfJski (@ProfJski) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -28,73 +32,97 @@ using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public class Eratosthenes
public class Eratosthenes : IExample
{
const int GlslVersion = 330;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Shaders / Eratosthenes";
public string Title => "raylib [shaders] example - eratosthenes sieve";
private RenderTexture2D target;
private Shader shader;
public void Init()
{
target = LoadRenderTexture(screenWidth, screenHeight);
// Load Eratosthenes shader
// NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/eratosthenes.fs");
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// Nothing to do here, everything is happening in the shader
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
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(); // End drawing to texture (now we have a blank texture available for the shader)
BeginDrawing();
ClearBackground(Color.RayWhite); // Clear screen background
BeginShaderMode(shader);
// NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom)
DrawTextureRec(
target.Texture,
new Rectangle(0, 0, target.Texture.Width, -target.Texture.Height),
new Vector2(0.0f, 0.0f),
Color.White
);
EndShaderMode();
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(shader);
UnloadRenderTexture(target);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - Sieve of Eratosthenes");
RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
// Load Eratosthenes shader
// NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
Shader shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/eratosthenes.fs");
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - eratosthenes sieve");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new Eratosthenes();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
// Nothing to do here, everything is happening in the shader
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// Enable drawing to texture
BeginTextureMode(target);
ClearBackground(Color.Black);
// 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);
// End drawing to texture (now we have a blank texture available for the shader)
EndTextureMode();
BeginShaderMode(shader);
// NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom)
DrawTextureRec(
target.Texture,
new Rectangle(0, 0, target.Texture.Width, -target.Texture.Height),
new Vector2(0.0f, 0.0f),
Color.White
);
EndShaderMode();
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadShader(shader);
UnloadRenderTexture(target);
CloseWindow();
//--------------------------------------------------------------------------------------

View file

@ -1,27 +1,22 @@
/*******************************************************************************************
*
* raylib [shaders] example - fog
* raylib [shaders] example - fog 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.
* 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).
* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
*
* This example has been created using raylib 2.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 2.5, last time updated with raylib 3.7
*
* Example contributed by Chris Camacho (@codifies) and reviewed by Ramon Santamaria (@raysan5)
* Example contributed by Chris Camacho (@chriscamacho) and reviewed by Ramon Santamaria (@raysan5)
*
* Chris Camacho (@codifies - http://bedroomcoders.co.uk/) notes:
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* This is based on the PBR lighting example, but greatly simplified to aid learning...
* actually there is very little of the PBR example left!
* When I first looked at the bewildering complexity of the PBR example I feared
* I would never understand how I could do simple lighting with raylib however its
* a testement to the authors of raylib (including rlights.h) that the example
* came together fairly quickly.
*
* Copyright (c) 2019 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5)
* Copyright (c) 2019-2025 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -32,21 +27,36 @@ using Examples.Shared;
namespace Examples.Shaders;
public class Fog
public class Fog : IExample
{
public unsafe static int Main()
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Shaders / Fog";
public string Title => "raylib [shaders] example - fog rendering";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
private Camera3D camera;
private Model modelA;
private Model modelB;
private Model modelC;
private Texture2D texture;
private Shader shader;
private int fogDensityLoc;
private float fogDensity;
public unsafe void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
// Enable Multi Sampling Anti Aliasing 4x (if available)
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - fog");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera = new();
camera.Position = new Vector3(2.0f, 2.0f, 6.0f);
camera.Target = new Vector3(0.0f, 0.5f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
@ -54,10 +64,10 @@ public class Fog
camera.Projection = CameraProjection.Perspective;
// Load models and texture
Model modelA = LoadModelFromMesh(GenMeshTorus(0.4f, 1.0f, 16, 32));
Model modelB = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f));
Model modelC = LoadModelFromMesh(GenMeshSphere(0.5f, 32, 32));
Texture2D texture = LoadTexture("resources/texel_checker.png");
modelA = LoadModelFromMesh(GenMeshTorus(0.4f, 1.0f, 16, 32));
modelB = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f));
modelC = LoadModelFromMesh(GenMeshSphere(0.5f, 32, 32));
texture = LoadTexture("resources/texel_checker.png");
// Assign texture to default model material
Raylib.SetMaterialTexture(ref modelA, 0, MaterialMapIndex.Albedo, ref texture);
@ -65,12 +75,15 @@ public class Fog
Raylib.SetMaterialTexture(ref modelC, 0, MaterialMapIndex.Albedo, ref texture);
// Load shader and set up some uniforms
Shader shader = LoadShader("resources/shaders/glsl330/lighting.vs", "resources/shaders/glsl330/fog.fs");
shader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/lighting.vs",
$"resources/shaders/glsl{GlslVersion}/fog.fs"
);
shader.Locs[(int)ShaderLocationIndex.MatrixModel] = GetShaderLocation(shader, "matModel");
shader.Locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(shader, "viewPos");
// Ambient light level
int ambientLoc = GetShaderLocation(shader, "ambient");
var ambientLoc = GetShaderLocation(shader, "ambient");
Raylib.SetShaderValue(
shader,
ambientLoc,
@ -78,8 +91,12 @@ public class Fog
ShaderUniformDataType.Vec4
);
float fogDensity = 0.15f;
int fogDensityLoc = GetShaderLocation(shader, "fogDensity");
var fogColor = ColorNormalize(Color.Gray);
var fogColorLoc = GetShaderLocation(shader, "fogColor");
Raylib.SetShaderValue(shader, fogColorLoc, fogColor, ShaderUniformDataType.Vec4);
fogDensity = 0.15f;
fogDensityLoc = GetShaderLocation(shader, "fogDensity");
Raylib.SetShaderValue(shader, fogDensityLoc, fogDensity, ShaderUniformDataType.Float);
// NOTE: All models share the same shader
@ -89,90 +106,112 @@ public class Fog
// Using just 1 point lights
Rlights.CreateLight(0, LightType.Point, new Vector3(0, 2, 6), Vector3.Zero, Color.White, shader);
}
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Main game loop
while (!WindowShouldClose())
if (IsKeyDown(KeyboardKey.Up))
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
if (IsKeyDown(KeyboardKey.Up))
fogDensity += 0.001f;
if (fogDensity > 1.0f)
{
fogDensity += 0.001f;
if (fogDensity > 1.0f)
{
fogDensity = 1.0f;
}
fogDensity = 1.0f;
}
if (IsKeyDown(KeyboardKey.Down))
{
fogDensity -= 0.001f;
if (fogDensity < 0.0f)
{
fogDensity = 0.0f;
}
}
Raylib.SetShaderValue(shader, fogDensityLoc, fogDensity, ShaderUniformDataType.Float);
// Rotate the torus
modelA.Transform = MatrixMultiply(modelA.Transform, MatrixRotateX(-0.025f));
modelA.Transform = MatrixMultiply(modelA.Transform, MatrixRotateZ(0.012f));
// Update the light shader with the camera view position
Raylib.SetShaderValue(
shader,
shader.Locs[(int)ShaderLocationIndex.VectorView],
camera.Position,
ShaderUniformDataType.Vec3
);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.Gray);
BeginMode3D(camera);
// Draw the three models
DrawModel(modelA, Vector3.Zero, 1.0f, Color.White);
DrawModel(modelB, new Vector3(-2.6f, 0, 0), 1.0f, Color.White);
DrawModel(modelC, new Vector3(2.6f, 0, 0), 1.0f, Color.White);
for (int i = -20; i < 20; i += 2)
{
DrawModel(modelA, new Vector3(i, 0, 2), 1.0f, Color.White);
}
EndMode3D();
DrawText(
$"Use up/down to change fog density [{fogDensity:F2}]",
10,
10,
20,
Color.RayWhite
);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Down))
{
fogDensity -= 0.001f;
if (fogDensity < 0.0f)
{
fogDensity = 0.0f;
}
}
Raylib.SetShaderValue(shader, fogDensityLoc, fogDensity, ShaderUniformDataType.Float);
// Rotate the torus
modelA.Transform = MatrixMultiply(modelA.Transform, MatrixRotateX(-0.025f));
modelA.Transform = MatrixMultiply(modelA.Transform, MatrixRotateZ(0.012f));
// Update the light shader with the camera view position
Raylib.SetShaderValue(
shader,
shader.Locs[(int)ShaderLocationIndex.VectorView],
camera.Position,
ShaderUniformDataType.Vec3
);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.Gray);
BeginMode3D(camera);
// Draw the three models
DrawModel(modelA, Vector3.Zero, 1.0f, Color.White);
DrawModel(modelB, new Vector3(-2.6f, 0, 0), 1.0f, Color.White);
DrawModel(modelC, new Vector3(2.6f, 0, 0), 1.0f, Color.White);
for (var i = -20; i < 20; i += 2)
{
DrawModel(modelA, new Vector3(i, 0, 2), 1.0f, Color.White);
}
EndMode3D();
DrawText(
$"Use KEY_UP/KEY_DOWN to change fog density [{fogDensity:F2}]",
10,
10,
20,
Color.RayWhite
);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadModel(modelA);
UnloadModel(modelB);
UnloadModel(modelC);
UnloadTexture(texture);
UnloadShader(shader);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
// Enable Multi Sampling Anti Aliasing 4x (if available)
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - fog rendering");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new Fog();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
//--------------------------------------------------------------------------------------

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

@ -1,135 +1,189 @@
/*******************************************************************************************
*
* raylib [shaders] example - Hot reloading
* raylib [shaders] example - hot reloading
*
* Example complexity rating: [★★★☆] 3/4
*
* NOTE: This example requires raylib OpenGL 3.3 for shaders support and only #version 330
* is currently supported. OpenGL ES 2.0 platforms are not supported at the moment.
* is currently supported. OpenGL ES 2.0 platforms are not supported at the moment
*
* This example has been created using raylib 3.0 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 3.0, last time updated with raylib 3.5
*
* Copyright (c) 2020 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) 2020-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public class HotReloading
public class HotReloading : IExample
{
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Shaders / Hot Reloading";
public string Title => "raylib [shaders] example - hot reloading";
private string fragShaderFileName;
private Shader shader;
private int resolutionLoc;
private int mouseLoc;
private int timeLoc;
private float[] resolution;
private float totalTime;
#if !BROWSER
private long fragShaderFileModTime;
private bool shaderAutoReloading;
#endif
public void Init()
{
fragShaderFileName = $"resources/shaders/glsl{GlslVersion}/reload.fs";
#if !BROWSER
fragShaderFileModTime = GetFileModTime(fragShaderFileName);
#endif
// Load raymarching shader
// NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
shader = LoadShader(null, fragShaderFileName);
// Get shader locations for required uniforms
resolutionLoc = GetShaderLocation(shader, "resolution");
mouseLoc = GetShaderLocation(shader, "mouse");
timeLoc = GetShaderLocation(shader, "time");
resolution = new[] { (float)screenWidth, (float)screenHeight };
Raylib.SetShaderValue(shader, resolutionLoc, resolution, ShaderUniformDataType.Vec2);
totalTime = 0.0f;
#if !BROWSER
shaderAutoReloading = false;
#endif
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
totalTime += GetFrameTime();
var mouse = GetMousePosition();
var mousePos = new[] { mouse.X, mouse.Y };
// Set shader required uniform values
Raylib.SetShaderValue(shader, timeLoc, totalTime, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shader, mouseLoc, mousePos, ShaderUniformDataType.Vec2);
#if !BROWSER
// Hot shader reloading
if (shaderAutoReloading || (IsMouseButtonPressed(MouseButton.Left)))
{
var currentFragShaderModTime = GetFileModTime(fragShaderFileName);
// Check if shader file has been modified
if (currentFragShaderModTime != fragShaderFileModTime)
{
// Try reloading updated shader
var updatedShader = LoadShader(null, fragShaderFileName);
// It was correctly loaded
if (updatedShader.Id != Rlgl.GetShaderIdDefault())
{
UnloadShader(shader);
shader = updatedShader;
// Get shader locations for required uniforms
resolutionLoc = GetShaderLocation(shader, "resolution");
mouseLoc = GetShaderLocation(shader, "mouse");
timeLoc = GetShaderLocation(shader, "time");
// Reset required uniforms
Raylib.SetShaderValue(
shader,
resolutionLoc,
resolution,
ShaderUniformDataType.Vec2
);
}
fragShaderFileModTime = currentFragShaderModTime;
}
}
if (IsKeyPressed(KeyboardKey.A))
{
shaderAutoReloading = !shaderAutoReloading;
}
#endif
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// We only draw a white full-screen rectangle, frame is generated in shader
BeginShaderMode(shader);
DrawRectangle(0, 0, screenWidth, screenHeight, Color.White);
EndShaderMode();
#if BROWSER
DrawText("Shader generates the frame in real time", 10, 10, 10, Color.Black);
#else
var info = $"PRESS [A] to TOGGLE SHADER AUTOLOADING: {(shaderAutoReloading ? "AUTO" : "MANUAL")}";
DrawText(info, 10, 10, 10, shaderAutoReloading ? Color.Red : Color.Black);
if (!shaderAutoReloading)
{
DrawText("MOUSE CLICK to SHADER RE-LOADING", 10, 30, 10, Color.Black);
}
var lastModification = DateTimeOffset.FromUnixTimeSeconds(fragShaderFileModTime).LocalDateTime.ToString();
DrawText($"Shader last modification: {lastModification}", 10, 430, 10, Color.Black);
#endif
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(shader);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
int screenWidth = 800;
int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - hot reloading");
string fragShaderFileName = "resources/shaders/glsl330/reload.fs";
long fragShaderFileModTime = GetFileModTime(fragShaderFileName);
// Load raymarching shader
// NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
Shader shader = LoadShader(null, fragShaderFileName);
// Get shader locations for required uniforms
int resolutionLoc = GetShaderLocation(shader, "resolution");
int mouseLoc = GetShaderLocation(shader, "mouse");
int timeLoc = GetShaderLocation(shader, "time");
float[] resolution = new[] { (float)screenWidth, (float)screenHeight };
Raylib.SetShaderValue(shader, resolutionLoc, resolution, ShaderUniformDataType.Vec2);
float totalTime = 0.0f;
bool shaderAutoReloading = false;
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new HotReloading();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
totalTime += GetFrameTime();
Vector2 mouse = GetMousePosition();
float[] mousePos = new[] { mouse.X, mouse.Y };
// Set shader required uniform values
Raylib.SetShaderValue(shader, timeLoc, totalTime, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shader, mouseLoc, mousePos, ShaderUniformDataType.Vec2);
// Hot shader reloading
if (shaderAutoReloading || (IsMouseButtonPressed(MouseButton.Left)))
{
long currentFragShaderModTime = GetFileModTime(fragShaderFileName);
// Check if shader file has been modified
if (currentFragShaderModTime != fragShaderFileModTime)
{
// Try reloading updated shader
Shader updatedShader = LoadShader(null, fragShaderFileName);
// It was correctly loaded
if (updatedShader.Id != 0) //rlGetShaderIdDefault())
{
UnloadShader(shader);
shader = updatedShader;
// Get shader locations for required uniforms
resolutionLoc = GetShaderLocation(shader, "resolution");
mouseLoc = GetShaderLocation(shader, "mouse");
timeLoc = GetShaderLocation(shader, "time");
// Reset required uniforms
Raylib.SetShaderValue(
shader,
resolutionLoc,
resolution,
ShaderUniformDataType.Vec2
);
}
fragShaderFileModTime = currentFragShaderModTime;
}
}
if (IsKeyPressed(KeyboardKey.A))
{
shaderAutoReloading = !shaderAutoReloading;
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
// We only draw a white full-screen rectangle, frame is generated in shader
BeginShaderMode(shader);
DrawRectangle(0, 0, screenWidth, screenHeight, Color.White);
EndShaderMode();
string info = $"PRESS [A] to TOGGLE SHADER AUTOLOADING: {(shaderAutoReloading ? "AUTO" : "MANUAL")}";
DrawText(info, 10, 10, 10, shaderAutoReloading ? Color.Red : Color.Black);
if (!shaderAutoReloading)
{
DrawText("MOUSE CLICK to SHADER RE-LOADING", 10, 30, 10, Color.Black);
}
// DrawText($"Shader last modification: ", 10, 430, 10, Color.BLACK);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadShader(shader);
CloseWindow();
//--------------------------------------------------------------------------------------

View file

@ -1,6 +1,8 @@
/*******************************************************************************************
*
* raylib [shaders] example - Hybrid Rendering
* raylib [shaders] example - hybrid rendering
*
* Example complexity rating: [★★★★] 4/4
*
* Example originally created with raylib 4.2, last time updated with raylib 4.2
*
@ -9,7 +11,7 @@
* 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) 2022-2023 Buğra Alptekin Sarı (@BugraAlptekinSari)
* Copyright (c) 2022-2025 Buğra Alptekin Sarı (@BugraAlptekinSari)
*
********************************************************************************************/
@ -19,43 +21,54 @@ using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public class HybridRender
public class HybridRender : IExample
{
struct RayLocs
private struct RayLocs
{
public int CamPos;
public int CamDir;
public int ScreenCenter;
}
const int GLSL_VERSION = 330;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public static int Main()
private const int screenWidth = 800;
private const int screenHeight = 450;
public string Name => "Shaders / Hybrid Render";
public string Title => "raylib [shaders] example - hybrid rendering";
private Shader shdrRaymarch;
private Shader shdrRaster;
private RayLocs marchLocs;
private RenderTexture2D target;
private Camera3D camera;
private float camDist;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - hybrid render");
// This shader calculates pixel depth and color using raymarch
Shader shdrRaymarch = LoadShader(null, $"resources/shaders/glsl{GLSL_VERSION}/hybrid_raymarch.fs");
// This Shader calculates pixel depth and color using raymarch
shdrRaymarch = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/hybrid_raymarch.fs");
// This Shader is a standard rasterization fragment shader with the addition of depth writing
// You are required to write depth for all shaders if one shader does it
Shader shdrRaster = LoadShader(null, $"resources/shaders/glsl{GLSL_VERSION}/hybrid_raster.fs");
shdrRaster = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/hybrid_raster.fs");
// Declare struct used to store camera locs
RayLocs marchLocs = new();
// Declare Struct used to store camera locs
marchLocs = new();
// Fill the struct with shader locs.
// Fill the struct with shader locs
marchLocs.CamPos = GetShaderLocation(shdrRaymarch, "camPos");
marchLocs.CamDir = GetShaderLocation(shdrRaymarch, "camDir");
marchLocs.ScreenCenter = GetShaderLocation(shdrRaymarch, "screenCenter");
// Transfer screenCenter position to shader. Which is used to calculate ray direction.
Vector2 screenCenter = new(screenWidth / 2, screenHeight / 2);
// Transfer screenCenter position to shader. Which is used to calculate ray direction
Vector2 screenCenter = new(screenWidth / 2.0f, screenHeight / 2.0f);
SetShaderValue(
shdrRaymarch,
marchLocs.ScreenCenter,
@ -63,91 +76,111 @@ public class HybridRender
ShaderUniformDataType.Vec2
);
// Use customized function to create writable depth texture buffer
RenderTexture2D target = LoadRenderTextureDepthTex(screenWidth, screenHeight);
// Use Customized function to create writable depth texture buffer
target = LoadRenderTextureDepthTex(screenWidth, screenHeight);
// Define the camera to look into our 3d world
Camera3D camera;
camera.Position = new Vector3(0.5f, 1.0f, 1.5f);
camera.Target = new Vector3(0.0f, 0.5f, 0.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
camera = new();
camera.Position = new Vector3(0.5f, 1.0f, 1.5f); // 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
// Camera FOV is pre-calculated in the camera Distance.
float camDist = 1.0f / (MathF.Tan(camera.FovY * 0.5f * Raylib.DEG2RAD));
// Camera FOV is pre-calculated in the camera distance
camDist = 1.0f / (MathF.Tan(camera.FovY * 0.5f * Raylib.DEG2RAD));
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Update Camera Postion in the ray march shader
SetShaderValue(
shdrRaymarch,
marchLocs.CamPos,
camera.Position,
ShaderUniformDataType.Vec3
);
// Update Camera Looking Vector. Vector length determines FOV
var camDir = Vector3.Normalize(camera.Target - camera.Position) * camDist;
SetShaderValue(shdrRaymarch, marchLocs.CamDir, camDir, ShaderUniformDataType.Vec3);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
// Draw into our custom render texture (framebuffer)
BeginTextureMode(target);
ClearBackground(Color.White);
// Raymarch Scene
// Manually enable Depth Test to handle multiple rendering methods
Rlgl.EnableDepthTest();
BeginShaderMode(shdrRaymarch);
DrawRectangleRec(new Rectangle(0, 0, screenWidth, screenHeight), Color.White);
EndShaderMode();
// Rasterize Scene
BeginMode3D(camera);
BeginShaderMode(shdrRaster);
DrawCubeWiresV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Red);
DrawCubeV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Purple);
DrawCubeWiresV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.DarkGreen);
DrawCubeV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Yellow);
DrawGrid(10, 1.0f);
EndShaderMode();
EndMode3D();
EndTextureMode();
// Draw custom render texture
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawTextureRec(
target.Texture,
new Rectangle(0, 0, screenWidth, -screenHeight),
Vector2.Zero,
Color.White
);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadRenderTextureDepthTex(target);
UnloadShader(shdrRaymarch);
UnloadShader(shdrRaster);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - hybrid rendering");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new HybridRender();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
// Update Camera Postion in the ray march shader.
SetShaderValue(
shdrRaymarch,
marchLocs.CamPos,
camera.Position,
ShaderUniformDataType.Vec3
);
// Update Camera Looking Vector. Vector length determines FOV.
Vector3 camDir = Vector3.Normalize(camera.Target - camera.Position) * camDist;
SetShaderValue(shdrRaymarch, marchLocs.CamDir, camDir, ShaderUniformDataType.Vec3);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
// Draw into our custom render texture (framebuffer)
BeginTextureMode(target);
ClearBackground(Color.White);
// Raymarch Scene
// Manually enable Depth Test to handle multiple rendering methods.
Rlgl.EnableDepthTest();
BeginShaderMode(shdrRaymarch);
DrawRectangleRec(new Rectangle(0, 0, screenWidth, screenHeight), Color.White);
EndShaderMode();
// Rasterize Scene
BeginMode3D(camera);
BeginShaderMode(shdrRaster);
DrawCubeWiresV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Red);
DrawCubeV(new Vector3(0.0f, 0.5f, 1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Purple);
DrawCubeWiresV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.DarkGreen);
DrawCubeV(new Vector3(0.0f, 0.5f, -1.0f), new Vector3(1.0f, 1.0f, 1.0f), Color.Yellow);
DrawGrid(10, 1.0f);
EndShaderMode();
EndMode3D();
EndTextureMode();
// Draw custom render texture
BeginDrawing();
ClearBackground(Color.RayWhite);
DrawTextureRec(
target.Texture,
new Rectangle(0, 0, screenWidth, -screenHeight),
Vector2.Zero,
Color.White
);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadRenderTextureDepthTex(target);
UnloadShader(shdrRaymarch);
UnloadShader(shdrRaster);
CloseWindow();
//--------------------------------------------------------------------------------------
@ -203,7 +236,7 @@ public class HybridRender
);
// Check if fbo is complete with attachments (valid)
if (Rlgl.FramebufferComplete(target.Id))
if (Rlgl.FramebufferComplete(target.Id) != 0)
{
TraceLog(TraceLogLevel.Info, $"FBO: [ID {target.Id}] Framebuffer object created successfully");
}

View file

@ -1,18 +1,22 @@
/*******************************************************************************************
*
* raylib [shaders] example - julia sets
* raylib [shaders] example - julia 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.
* 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).
* NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
*
* This example has been created using raylib 2.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 2.5, last time updated with raylib 4.0
*
* Example contributed by eggmund (@eggmund) and reviewed by Ramon Santamaria (@raysan5)
* Example contributed by Josh Colclough (@joshcol9232) and reviewed by Ramon Santamaria (@raysan5)
*
* Copyright (c) 2019 eggmund (@eggmund) and 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 Josh Colclough (@joshcol9232) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -21,12 +25,27 @@ using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public class JuliaSet
public class JuliaSet : IExample
{
const int GlslVersion = 330;
private const int screenWidth = 800;
private const int screenHeight = 450;
private const float zoomSpeed = 1.01f;
private const float offsetSpeedMul = 2.0f;
private const float startingZoom = 0.75f;
#if BROWSER
const int GlslVersion = 100; // WebGL1 needs GLSL ES 100
#else
private const int GlslVersion = 330;
#endif
public string Name => "Shaders / Julia Set";
public string Title => "raylib [shaders] example - julia set";
// A few good julia sets
static float[][] PointsOfInterest = new float[][] {
private float[][] PointsOfInterest = new float[][] {
new float[] { -0.348827f, 0.607167f },
new float[] { -0.786268f, 0.169728f },
new float[] { -0.8f, 0.156f },
@ -35,38 +54,38 @@ public class JuliaSet
new float[] { -0.70176f, -0.3842f },
};
public static int Main()
private Shader shader;
private RenderTexture2D target;
private float[] c;
private float[] offset;
private float zoom;
private int cLoc;
private int zoomLoc;
private int offsetLoc;
private int incrementSpeed;
private bool showControls;
public void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
const float zoomSpeed = 1.01f;
const float offsetSpeedMul = 2.0f;
const float startingZoom = 0.75f;
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - julia sets");
// Load julia set shader
// NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
Shader shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/julia_set.fs");
shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/julia_set.fs");
// Create a RenderTexture2D to be used for render to texture
RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
target = LoadRenderTexture(screenWidth, screenHeight);
// c constant to use in z^2 + c
float[] c = { PointsOfInterest[0][0], PointsOfInterest[0][1] };
c = new float[] { PointsOfInterest[0][0], PointsOfInterest[0][1] };
// Offset and zoom to draw the julia set at. (centered on screen and default size)
float[] offset = { 0, 0 };
float zoom = startingZoom;
offset = new float[] { 0, 0 };
zoom = startingZoom;
// 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
int cLoc = GetShaderLocation(shader, "c");
int zoomLoc = GetShaderLocation(shader, "zoom");
int offsetLoc = GetShaderLocation(shader, "offset");
cLoc = GetShaderLocation(shader, "c");
zoomLoc = GetShaderLocation(shader, "zoom");
offsetLoc = GetShaderLocation(shader, "offset");
// Upload the shader uniform values!
Raylib.SetShaderValue(shader, cLoc, c, ShaderUniformDataType.Vec2);
@ -74,166 +93,189 @@ public class JuliaSet
Raylib.SetShaderValue(shader, offsetLoc, offset, ShaderUniformDataType.Vec2);
// Multiplier of speed to change c value
int incrementSpeed = 0;
incrementSpeed = 0;
// Show controls
bool showControls = true;
showControls = true;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
// 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))
{
if (IsKeyPressed(KeyboardKey.One))
{
c[0] = PointsOfInterest[0][0];
c[1] = PointsOfInterest[0][1];
}
else if (IsKeyPressed(KeyboardKey.Two))
{
c[0] = PointsOfInterest[1][0];
c[1] = PointsOfInterest[1][1];
}
else if (IsKeyPressed(KeyboardKey.Three))
{
c[0] = PointsOfInterest[2][0];
c[1] = PointsOfInterest[2][1];
}
else if (IsKeyPressed(KeyboardKey.Four))
{
c[0] = PointsOfInterest[3][0];
c[1] = PointsOfInterest[3][1];
}
else if (IsKeyPressed(KeyboardKey.Five))
{
c[0] = PointsOfInterest[4][0];
c[1] = PointsOfInterest[4][1];
}
else if (IsKeyPressed(KeyboardKey.Six))
{
c[0] = PointsOfInterest[5][0];
c[1] = PointsOfInterest[5][1];
}
Raylib.SetShaderValue(shader, cLoc, c, ShaderUniformDataType.Vec2);
}
// If "R" is pressed, reset zoom and offset
if (IsKeyPressed(KeyboardKey.R))
{
zoom = startingZoom;
offset[0] = 0.0f;
offset[1] = 0.0f;
Raylib.SetShaderValue(shader, zoomLoc, zoom, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shader, offsetLoc, offset, ShaderUniformDataType.Vec2);
}
// Pause animation (c change)
if (IsKeyPressed(KeyboardKey.Space))
{
incrementSpeed = 0;
}
// Toggle whether or not to show controls
if (IsKeyPressed(KeyboardKey.F1))
{
showControls = !showControls;
}
if (IsKeyPressed(KeyboardKey.Right))
{
incrementSpeed++;
}
else if (IsKeyPressed(KeyboardKey.Left))
{
incrementSpeed--;
}
// If either left or right button is pressed, zoom in/out
if (IsMouseButtonDown(MouseButton.Left) || IsMouseButtonDown(MouseButton.Right))
{
if (IsMouseButtonDown(MouseButton.Left))
{
zoom *= zoomSpeed;
}
if (IsMouseButtonDown(MouseButton.Right))
{
zoom *= 1.0f / zoomSpeed;
}
var mousePos = GetMousePosition();
var offsetVelocity = Vector2.Zero;
offsetVelocity.X = (mousePos.X / screenWidth - 0.5f) * offsetSpeedMul / zoom;
offsetVelocity.Y = (mousePos.Y / screenHeight - 0.5f) * offsetSpeedMul / zoom;
// Apply move velocity to camera
offset[0] += GetFrameTime() * offsetVelocity.X;
offset[1] += GetFrameTime() * offsetVelocity.Y;
Raylib.SetShaderValue(shader, zoomLoc, zoom, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shader, offsetLoc, offset, ShaderUniformDataType.Vec2);
}
// Increment c value with time
var dc = GetFrameTime() * incrementSpeed * 0.0005f;
c[0] += dc;
c[1] += dc;
Raylib.SetShaderValue(shader, cLoc, c, ShaderUniformDataType.Vec2);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
// Using a render texture to draw Julia set
BeginTextureMode(target);
ClearBackground(Color.Black);
// 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);
// Draw the saved texture and rendered julia 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 KEY_F1 to toggle these controls", 10, 30, 10, Color.RayWhite);
DrawText("Press KEYS [1 - 6] to change point of interest", 10, 45, 10, Color.RayWhite);
DrawText("Press KEY_LEFT | KEY_RIGHT to change speed", 10, 60, 10, Color.RayWhite);
DrawText("Press KEY_SPACE to stop movement animation", 10, 75, 10, Color.RayWhite);
DrawText("Press KEY_R to recenter the camera", 10, 90, 10, Color.RayWhite);
}
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(shader);
UnloadRenderTexture(target);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - julia set");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new JuliaSet();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
// 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))
{
if (IsKeyPressed(KeyboardKey.One))
{
c[0] = PointsOfInterest[0][0];
c[1] = PointsOfInterest[0][1];
}
else if (IsKeyPressed(KeyboardKey.Two))
{
c[0] = PointsOfInterest[1][0];
c[1] = PointsOfInterest[1][1];
}
else if (IsKeyPressed(KeyboardKey.Three))
{
c[0] = PointsOfInterest[2][0];
c[1] = PointsOfInterest[2][1];
}
else if (IsKeyPressed(KeyboardKey.Four))
{
c[0] = PointsOfInterest[3][0];
c[1] = PointsOfInterest[3][1];
}
else if (IsKeyPressed(KeyboardKey.Five))
{
c[0] = PointsOfInterest[4][0];
c[1] = PointsOfInterest[4][1];
}
else if (IsKeyPressed(KeyboardKey.Six))
{
c[0] = PointsOfInterest[5][0];
c[1] = PointsOfInterest[5][1];
}
Raylib.SetShaderValue(shader, cLoc, c, ShaderUniformDataType.Vec2);
}
if (IsKeyPressed(KeyboardKey.R))
{
zoom = startingZoom;
offset[0] = 1f;
offset[1] = 1f;
Raylib.SetShaderValue(shader, zoomLoc, zoom, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shader, offsetLoc, offset, ShaderUniformDataType.Vec2);
}
// Pause animation (c change)
if (IsKeyPressed(KeyboardKey.Space))
{
incrementSpeed = 0;
}
// Toggle whether or not to show controls
if (IsKeyPressed(KeyboardKey.F1))
{
showControls = !showControls;
}
if (IsKeyPressed(KeyboardKey.Right))
{
incrementSpeed++;
}
else if (IsKeyPressed(KeyboardKey.Left))
{
incrementSpeed--;
}
// If either left or right button is pressed, zoom in/out
if (IsMouseButtonDown(MouseButton.Left) || IsMouseButtonDown(MouseButton.Right))
{
if (IsMouseButtonDown(MouseButton.Left))
{
zoom *= zoomSpeed;
}
if (IsMouseButtonDown(MouseButton.Right))
{
zoom *= 1.0f / zoomSpeed;
}
Vector2 mousePos = GetMousePosition();
Vector2 offsetVelocity = Vector2.Zero;
offsetVelocity.X = (mousePos.X / screenWidth - 0.5f) * offsetSpeedMul / zoom;
offsetVelocity.Y = (mousePos.Y / screenHeight - 0.5f) * offsetSpeedMul / zoom;
// Apply move velocity to camera
offset[0] += GetFrameTime() * offsetVelocity.X;
offset[1] += GetFrameTime() * offsetVelocity.Y;
Raylib.SetShaderValue(shader, zoomLoc, zoom, ShaderUniformDataType.Float);
Raylib.SetShaderValue(shader, offsetLoc, offset, ShaderUniformDataType.Vec2);
}
// Increment c value with time
float amount = GetFrameTime() * incrementSpeed * 0.0005f;
c[0] += amount;
c[1] += amount;
Raylib.SetShaderValue(shader, cLoc, c, ShaderUniformDataType.Vec2);
// Using a render texture to draw Julia set
// Enable drawing to texture
BeginTextureMode(target);
ClearBackground(Color.Black);
// Draw a rectangle in shader mode to be used as shader canvas
// NOTE: Rectangle uses font Color.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();
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.Black);
// Draw the saved texture and rendered julia set with shader
// NOTE: We do not invert texture on Y, already considered inside shader
BeginShaderMode(shader);
DrawTexture(target.Texture, 0, 0, Color.White);
EndShaderMode();
if (showControls)
{
DrawText("Press Mouse buttons right/left to zoom in/out and move", 10, 15, 10, Color.RayWhite);
DrawText("Press KEY_F1 to toggle these controls", 10, 30, 10, Color.RayWhite);
DrawText("Press KEYS [1 - 6] to change point of interest", 10, 45, 10, Color.RayWhite);
DrawText("Press KEY_LEFT | KEY_RIGHT to change speed", 10, 60, 10, Color.RayWhite);
DrawText("Press KEY_SPACE to pause movement animation", 10, 75, 10, Color.RayWhite);
DrawText("Press KEY_R to recenter the camera", 10, 90, 10, Color.RayWhite);
}
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadShader(shader);
UnloadRenderTexture(target);
CloseWindow();
//--------------------------------------------------------------------------------------

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

@ -1,112 +1,97 @@
/*******************************************************************************************
*
* raylib [shaders] example - rlgl module usage for instanced meshes
* raylib [shaders] example - mesh instancing
*
* This example uses [rlgl] module funtionality (pseudo-OpenGL 1.1 style coding)
* Example complexity rating: [★★★★] 4/4
*
* This example has been created using raylib 3.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 3.7, last time updated with raylib 4.2
*
* Example contributed by @seanpringle and reviewed by Ramon Santamaria (@raysan5)
* Example contributed by seanpringle (@seanpringle) and reviewed by Max (@moliad) and Ramon Santamaria (@raysan5)
*
* Copyright (c) 2020 @seanpringle
* 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) 2020-2025 seanpringle (@seanpringle), Max (@moliad) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
using System;
using System.Numerics;
using static Raylib_cs.Raylib;
using Examples.Shared;
namespace Examples.Shaders;
public class MeshInstancing
public class MeshInstancing : IExample
{
public static int Main()
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 MaxInstances = 10000;
public string Name => "Shaders / Mesh Instancing";
public string Title => "raylib [shaders] example - mesh instancing";
private Camera3D camera;
private Mesh cube;
private Matrix4x4[] transforms;
private Shader shader;
private Material matInstances;
private Material matDefault;
public unsafe void Init()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
const int fps = 60;
// Enable Multi Sampling Anti Aliasing 4x (if available)
SetConfigFlags(ConfigFlags.Msaa4xHint);
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - rlgl mesh instanced");
// Speed of jump animation
int speed = 30;
// Count of separate groups jumping around
int groups = 2;
// Maximum amplitude of jump
float amp = 10;
// Global variance in jump height
float variance = 0.8f;
// Individual cube's computed loop timer
float loop = 0.0f;
// Used for various 3D coordinate & vector ops
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(-125.0f, 125.0f, -125.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;
camera = new();
camera.Position = new Vector3(-125.0f, 125.0f, -125.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
// Number of instances to display
const int instances = 10000;
Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f);
// Define mesh to be instanced
cube = GenMeshCube(1.0f, 1.0f, 1.0f);
// Rotation state of instances
Matrix4x4[] rotations = new Matrix4x4[instances];
// Per-frame rotation animation of instances
Matrix4x4[] rotationsInc = new Matrix4x4[instances];
// Locations of instances
Matrix4x4[] translations = new Matrix4x4[instances];
// Define transforms to be uploaded to GPU for instances
transforms = new Matrix4x4[MaxInstances]; // Pre-multiplied transformations passed to rlgl
// Scatter random cubes around
for (int i = 0; i < instances; i++)
// Translate and rotate cubes randomly
for (var i = 0; i < MaxInstances; i++)
{
x = GetRandomValue(-50, 50);
y = GetRandomValue(-50, 50);
z = GetRandomValue(-50, 50);
translations[i] = Matrix4x4.CreateTranslation(x, y, z);
x = GetRandomValue(0, 360);
y = GetRandomValue(0, 360);
z = GetRandomValue(0, 360);
Vector3 axis = Vector3.Normalize(new Vector3(x, y, z));
float angle = (float)GetRandomValue(0, 10) * DEG2RAD;
rotationsInc[i] = Matrix4x4.CreateFromAxisAngle(axis, angle);
rotations[i] = Matrix4x4.Identity;
}
// Pre-multiplied transformations passed to rlgl
Matrix4x4[] transforms = new Matrix4x4[instances];
Shader shader = LoadShader(
"resources/shaders/glsl330/lighting_instancing.vs",
"resources/shaders/glsl330/lighting.fs"
);
// Get some shader loactions
unsafe
{
int* locs = (int*)shader.Locs;
locs[(int)ShaderLocationIndex.MatrixMvp] = GetShaderLocation(shader, "mvp");
locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(shader, "viewPos");
locs[(int)ShaderLocationIndex.MatrixModel] = GetShaderLocationAttrib(
shader,
"instanceTransform"
var translation = Matrix4x4.CreateTranslation(
GetRandomValue(-50, 50),
GetRandomValue(-50, 50),
GetRandomValue(-50, 50)
);
var axis = Vector3.Normalize(new Vector3(
GetRandomValue(0, 360),
GetRandomValue(0, 360),
GetRandomValue(0, 360)
));
var angle = GetRandomValue(0, 180) * DEG2RAD;
var rotation = Matrix4x4.CreateFromAxisAngle(axis, angle);
transforms[i] = Matrix4x4.Transpose(Matrix4x4.Multiply(rotation, translation));
}
// Ambient light level
int ambientLoc = GetShaderLocation(shader, "ambient");
// Load lighting shader
shader = LoadShader(
$"resources/shaders/glsl{GlslVersion}/lighting_instancing.vs",
$"resources/shaders/glsl{GlslVersion}/lighting.fs"
);
// Get shader locations
shader.Locs[(int)ShaderLocationIndex.MatrixMvp] = GetShaderLocation(shader, "mvp");
shader.Locs[(int)ShaderLocationIndex.VectorView] = GetShaderLocation(shader, "viewPos");
// Set shader value: ambient light level
var ambientLoc = GetShaderLocation(shader, "ambient");
Raylib.SetShaderValue(
shader,
ambientLoc,
@ -114,211 +99,105 @@ public class MeshInstancing
ShaderUniformDataType.Vec4
);
// Create one light
Rlights.CreateLight(
0,
LightType.Directorional,
new Vector3(50, 50, 0),
new Vector3(50.0f, 50.0f, 0.0f),
Vector3.Zero,
Color.White,
shader
);
Material material = LoadMaterialDefault();
material.Shader = shader;
unsafe
{
material.Maps[(int)MaterialMapIndex.Diffuse].Color = Color.Red;
}
// NOTE: We are assigning the intancing shader to material.shader
// to be used on mesh drawing with DrawMeshInstanced()
matInstances = LoadMaterialDefault();
matInstances.Shader = shader;
matInstances.Maps[(int)MaterialMapIndex.Diffuse].Color = Color.Red;
int textPositionY = 300;
// Load default material (using raylib intenral default shader) for non-instanced mesh drawing
// WARNING: Default shader enables vertex color attribute BUT GenMeshCube() does not generate vertex colors, so,
// when drawing the color attribute is disabled and a default color value is provided as input for thevertex attribute
matDefault = LoadMaterialDefault();
matDefault.Maps[(int)MaterialMapIndex.Diffuse].Color = Color.Blue;
}
// Simple frames counter to manage animation
int framesCounter = 0;
public unsafe void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Orbital);
SetTargetFPS(fps);
// Update the light shader with the camera view position
float[] cameraPos = { camera.Position.X, camera.Position.Y, camera.Position.Z };
Raylib.SetShaderValue(
shader,
shader.Locs[(int)ShaderLocationIndex.VectorView],
cameraPos,
ShaderUniformDataType.Vec3
);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
// Draw cube mesh with default material (BLUE)
DrawMesh(cube, matDefault, Matrix4x4.Transpose(Matrix4x4.CreateTranslation(-10.0f, 0.0f, 0.0f)));
// Draw meshes instanced using material containing instancing shader (RED + lighting),
// transforms[] for the instances should be provided, they are dynamically
// updated in GPU every frame, so we can animate the different mesh instances
DrawMeshInstanced(cube, matInstances, transforms, MaxInstances);
// Draw cube mesh with default material (BLUE)
DrawMesh(cube, matDefault, Matrix4x4.Transpose(Matrix4x4.CreateTranslation(10.0f, 0.0f, 0.0f)));
EndMode3D();
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
// Detach shader so UnloadMaterial does not also unload it, then free everything.
matInstances.Shader = new();
UnloadMaterial(matInstances);
UnloadMaterial(matDefault);
UnloadMesh(cube);
UnloadShader(shader);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - mesh instancing");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new MeshInstancing();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
textPositionY = 300;
framesCounter += 1;
if (IsKeyDown(KeyboardKey.Up))
{
amp += 0.5f;
}
if (IsKeyDown(KeyboardKey.Down))
{
amp = (amp <= 1) ? 1.0f : (amp - 1.0f);
}
if (IsKeyDown(KeyboardKey.Left))
{
variance = (variance <= 0.0f) ? 0.0f : (variance - 0.01f);
}
if (IsKeyDown(KeyboardKey.Right))
{
variance = (variance >= 1.0f) ? 1.0f : (variance + 0.01f);
}
if (IsKeyDown(KeyboardKey.One))
{
groups = 1;
}
if (IsKeyDown(KeyboardKey.Two))
{
groups = 2;
}
if (IsKeyDown(KeyboardKey.Three))
{
groups = 3;
}
if (IsKeyDown(KeyboardKey.Four))
{
groups = 4;
}
if (IsKeyDown(KeyboardKey.Five))
{
groups = 5;
}
if (IsKeyDown(KeyboardKey.Six))
{
groups = 6;
}
if (IsKeyDown(KeyboardKey.Seven))
{
groups = 7;
}
if (IsKeyDown(KeyboardKey.Eight))
{
groups = 8;
}
if (IsKeyDown(KeyboardKey.Nine))
{
groups = 9;
}
if (IsKeyDown(KeyboardKey.W))
{
groups = 7;
amp = 25;
speed = 18;
variance = 0.70f;
}
if (IsKeyDown(KeyboardKey.Equal))
{
speed = (speed <= (int)(fps * 0.25f)) ? (int)(fps * 0.25f) : (int)(speed * 0.95f);
}
if (IsKeyDown(KeyboardKey.KpAdd))
{
speed = (speed <= (int)(fps * 0.25f)) ? (int)(fps * 0.25f) : (int)(speed * 0.95f);
}
if (IsKeyDown(KeyboardKey.Minus))
{
speed = (int)MathF.Max(speed * 1.02f, speed + 1);
}
if (IsKeyDown(KeyboardKey.KpSubtract))
{
speed = (int)MathF.Max(speed * 1.02f, speed + 1);
}
// Update the light shader with the camera view position
float[] cameraPos = { camera.Position.X, camera.Position.Y, camera.Position.Z };
Raylib.SetShaderValue(
shader,
(int)ShaderLocationIndex.VectorView,
cameraPos,
ShaderUniformDataType.Vec3
);
// Apply per-instance transformations
for (int i = 0; i < instances; i++)
{
rotations[i] = Matrix4x4.Multiply(rotations[i], rotationsInc[i]);
transforms[i] = Matrix4x4.Multiply(rotations[i], translations[i]);
// Get the animation cycle's framesCounter for this instance
loop = (float)((framesCounter + (int)(((float)(i % groups) / groups) * speed)) % speed) / speed;
// Calculate the y according to loop cycle
y = (MathF.Sin(loop * MathF.PI * 2)) * amp * ((1 - variance) + (variance * (float)(i % (groups * 10)) / (groups * 10)));
// Clamp to floor
y = (y < 0) ? 0.0f : y;
transforms[i] = Matrix4x4.Multiply(transforms[i], Matrix4x4.CreateTranslation(0.0f, y, 0.0f));
transforms[i] = Matrix4x4.Transpose(transforms[i]);
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawMeshInstanced(cube, material, transforms, instances);
EndMode3D();
DrawText("A CUBE OF DANCING CUBES!", 490, 10, 20, Color.Maroon);
DrawText("PRESS KEYS:", 10, textPositionY, 20, Color.Black);
DrawText("1 - 9", 10, textPositionY += 25, 10, Color.Black);
DrawText(": Number of groups", 50, textPositionY, 10, Color.Black);
DrawText($": {groups}", 160, textPositionY, 10, Color.Black);
DrawText("UP", 10, textPositionY += 15, 10, Color.Black);
DrawText(": increase amplitude", 50, textPositionY, 10, Color.Black);
DrawText($": {amp}%.2f", 160, textPositionY, 10, Color.Black);
DrawText("DOWN", 10, textPositionY += 15, 10, Color.Black);
DrawText(": decrease amplitude", 50, textPositionY, 10, Color.Black);
DrawText("LEFT", 10, textPositionY += 15, 10, Color.Black);
DrawText(": decrease variance", 50, textPositionY, 10, Color.Black);
DrawText($": {variance}.2f", 160, textPositionY, 10, Color.Black);
DrawText("RIGHT", 10, textPositionY += 15, 10, Color.Black);
DrawText(": increase variance", 50, textPositionY, 10, Color.Black);
DrawText("+/=", 10, textPositionY += 15, 10, Color.Black);
DrawText(": increase speed", 50, textPositionY, 10, Color.Black);
DrawText($": {speed} = {((float)fps / speed)} loops/sec", 160, textPositionY, 10, Color.Black);
DrawText("-", 10, textPositionY += 15, 10, Color.Black);
DrawText(": decrease speed", 50, textPositionY, 10, Color.Black);
DrawText("W", 10, textPositionY += 15, 10, Color.Black);
DrawText(": Wild setup!", 50, textPositionY, 10, Color.Black);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,18 +1,22 @@
/*******************************************************************************************
*
* raylib [shaders] example - Apply a shader to a 3d model
* raylib [shaders] example - model shader
*
* 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.
* 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), to test this example
* on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders
* raylib comes with shaders ready for both versions, check raylib/shaders install folder
*
* This example has been created using raylib 1.3 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 1.3, last time updated with raylib 3.7
*
* Copyright (c) 2014 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) 2014-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -21,86 +25,122 @@ using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public class ModelShader
public class ModelShader : 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 / Model Shader";
public string Title => "raylib [shaders] example - model shader";
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
public bool CursorDisabled => true;
private Camera3D camera;
private Model model;
private Texture2D texture;
private Shader shader;
private Vector3 position;
public 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, 1.0f, -1.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
model = LoadModel("resources/models/obj/watermill.obj"); // Load OBJ model
texture = LoadTexture("resources/models/obj/watermill_diffuse.png"); // Load model texture
// Load shader for model
// NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/grayscale.fs");
Raylib.SetMaterialShader(ref model, 0, ref shader); // Set shader effect to 3d model
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture); // Bind texture to model
position = new(0.0f, 0.0f, 0.0f); // Set model position
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 0.2f, Color.White); // Draw 3d model with texture
DrawGrid(10, 1.0f); // Draw a grid
EndMode3D();
DrawText(
"(c) Watermill 3D model by Alberto Cano",
screenWidth - 210,
screenHeight - 20,
10,
Color.Gray
);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(shader); // Unload shader
UnloadTexture(texture); // Unload texture
UnloadModel(model); // Unload model
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
// Enable Multi Sampling Anti Aliasing 4x (if available)
SetConfigFlags(ConfigFlags.Msaa4xHint);
SetConfigFlags(ConfigFlags.Msaa4xHint); // Enable Multi Sampling Anti Aliasing 4x (if available)
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - model shader");
// Define the camera to look into our 3d world
Camera3D camera = new();
camera.Position = new Vector3(4.0f, 4.0f, 4.0f);
camera.Target = new Vector3(0.0f, 1.0f, -1.0f);
camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
camera.FovY = 45.0f;
camera.Projection = CameraProjection.Perspective;
Model model = LoadModel("resources/models/obj/watermill.obj");
Texture2D texture = LoadTexture("resources/models/obj/watermill_diffuse.png");
Shader shader = LoadShader("resources/shaders/glsl330/base.vs",
"resources/shaders/glsl330/grayscale.fs");
Raylib.SetMaterialShader(ref model, 0, ref shader);
Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.Albedo, ref texture);
Vector3 position = new(0.0f, 0.0f, 0.0f);
SetTargetFPS(60);
DisableCursor(); // Limit cursor to relative movement inside the window
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
var game = new ModelShader();
game.Init();
// Main game loop
while (!WindowShouldClose())
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(ref camera, CameraMode.Free);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginMode3D(camera);
DrawModel(model, position, 0.2f, Color.White);
DrawGrid(10, 1.0f);
EndMode3D();
DrawText(
"(c) Watermill 3D model by Alberto Cano",
screenWidth - 210,
screenHeight - 20,
10,
Color.Gray
);
DrawText($"Camera3D position: ({camera.Position})", 600, 20, 10, Color.Black);
DrawText($"Camera3D target: ({camera.Position})", 600, 40, 10, Color.Gray);
DrawFPS(10, 10);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadShader(shader);
UnloadTexture(texture);
UnloadModel(model);
CloseWindow();
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;

View file

@ -1,18 +1,22 @@
/*******************************************************************************************
*
* raylib [shaders] example - Multiple sample2D with default batch system
* raylib [shaders] example - multi sample2d
*
* 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.
* 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), to test this example
* on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders
* raylib comes with shaders ready for both versions, check raylib/shaders install folder
*
* This example has been created using raylib 3.5 (www.raylib.com)
* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
* Example originally created with raylib 3.5, last time updated with raylib 3.5
*
* Copyright (c) 2020 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) 2020-2025 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
@ -20,94 +24,129 @@ using static Raylib_cs.Raylib;
namespace Examples.Shaders;
public class MultiSample2d
public class MultiSample2d : 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 / Multi Sample 2D";
public string Title => "raylib [shaders] example - multi sample2d";
private Texture2D texRed;
private Texture2D texBlue;
private Shader shader;
private int texBlueLoc;
private int dividerLoc;
private float dividerValue;
public void Init()
{
var imRed = GenImageColor(800, 450, new Color(255, 0, 0, 255));
texRed = LoadTextureFromImage(imRed);
UnloadImage(imRed);
var imBlue = GenImageColor(800, 450, new Color(0, 0, 255, 255));
texBlue = LoadTextureFromImage(imBlue);
UnloadImage(imBlue);
shader = LoadShader(null, $"resources/shaders/glsl{GlslVersion}/color_mix.fs");
// Get an additional sampler2D location to be enabled on drawing
texBlueLoc = GetShaderLocation(shader, "texture1");
// Get shader uniform for divider
dividerLoc = GetShaderLocation(shader, "divider");
dividerValue = 0.5f;
}
public void Update()
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Right))
{
dividerValue += 0.01f;
}
else if (IsKeyDown(KeyboardKey.Left))
{
dividerValue -= 0.01f;
}
if (dividerValue < 0.0f)
{
dividerValue = 0.0f;
}
else if (dividerValue > 1.0f)
{
dividerValue = 1.0f;
}
Raylib.SetShaderValue(shader, dividerLoc, dividerValue, ShaderUniformDataType.Float);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginShaderMode(shader);
// WARNING: Additional textures (sampler2D) are enabled for ALL draw calls in the batch,
// but EndShaderMode() forces batch drawing and resets active textures, this way
// other textures (sampler2D) can be activated on consequent drawings (if required)
// The downside of this approach is that SetShaderValue() must be called inside the loop,
// to be set again after every EndShaderMode() reset
SetShaderValueTexture(shader, texBlueLoc, texBlue);
// We are drawing texRed using default [sampler2D texture0] but
// an additional texture units is enabled for texBlue [sampler2D texture1]
DrawTexture(texRed, 0, 0, Color.White);
EndShaderMode(); // Texture sampler2D is reseted, needs to be set again for next frame
var y = GetScreenHeight() - 40;
DrawText("Use KEY_LEFT/KEY_RIGHT to move texture mixing in shader!", 80, y, 20, Color.RayWhite);
EndDrawing();
//----------------------------------------------------------------------------------
}
public void Unload()
{
UnloadShader(shader);
UnloadTexture(texRed);
UnloadTexture(texBlue);
}
public static int Main()
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib - multiple sample2D");
Image imRed = GenImageColor(800, 450, new Color(255, 0, 0, 255));
Texture2D texRed = LoadTextureFromImage(imRed);
UnloadImage(imRed);
Image imBlue = GenImageColor(800, 450, new Color(0, 0, 255, 255));
Texture2D texBlue = LoadTextureFromImage(imBlue);
UnloadImage(imBlue);
Shader shader = LoadShader(null, "resources/shaders/glsl330/color_mix.fs");
// Get an additional sampler2D location to be enabled on drawing
int texBlueLoc = GetShaderLocation(shader, "texture1");
// Get shader uniform for divider
int dividerLoc = GetShaderLocation(shader, "divider");
float dividerValue = 0.5f;
InitWindow(screenWidth, screenHeight, "raylib [shaders] example - multi sample2d");
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
var game = new MultiSample2d();
game.Init();
// Main game loop
while (!WindowShouldClose())
{
// Update
//----------------------------------------------------------------------------------
if (IsKeyDown(KeyboardKey.Right))
{
dividerValue += 0.01f;
}
else if (IsKeyDown(KeyboardKey.Left))
{
dividerValue -= 0.01f;
}
if (dividerValue < 0.0f)
{
dividerValue = 0.0f;
}
else if (dividerValue > 1.0f)
{
dividerValue = 1.0f;
}
Raylib.SetShaderValue(shader, dividerLoc, dividerValue, ShaderUniformDataType.Float);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(Color.RayWhite);
BeginShaderMode(shader);
// WARNING: Additional samplers are enabled for all draw calls in the batch,
// EndShaderMode() forces batch drawing and consequently resets active textures
// to let other sampler2D to be activated on consequent drawings (if required)
SetShaderValueTexture(shader, texBlueLoc, texBlue);
// We are drawing texRed using default sampler2D texture0 but
// an additional texture units is enabled for texBlue (sampler2D texture1)
DrawTexture(texRed, 0, 0, Color.White);
EndShaderMode();
int y = GetScreenHeight() - 40;
DrawText("Use KEY_LEFT/KEY_RIGHT to move texture mixing in shader!", 80, y, 20, Color.RayWhite);
EndDrawing();
//----------------------------------------------------------------------------------
game.Update();
}
game.Unload();
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadShader(shader);
UnloadTexture(texRed);
UnloadTexture(texBlue);
CloseWindow();
//--------------------------------------------------------------------------------------

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;
}
}

Some files were not shown because too many files have changed in this diff Show more