diff --git a/Community/Shaders/RaylibCsExamples.Community.Shaders.BasicPbr/PbrLight.cs b/Community/Shaders/RaylibCsExamples.Community.Shaders.BasicPbr/PbrLight.cs
new file mode 100644
index 0000000..acf8ccb
--- /dev/null
+++ b/Community/Shaders/RaylibCsExamples.Community.Shaders.BasicPbr/PbrLight.cs
@@ -0,0 +1,81 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.Numerics;
+using Raylib_cs;
+
+namespace RaylibCsExamples.Community.Shaders.BasicPbr;
+
+public struct PbrLight
+{
+ public required PbrLightType Type { get; set; }
+ public required bool Enabled { get; set; }
+ public required Vector3 Position { get; set; }
+ public required Vector3 Target { get; set; }
+ public required Vector4 Color { get; set; }
+ public required float Intensity { get; set; }
+
+ // Shader light parameters locations
+ public required int TypeLoc { get; set; }
+ public required int EnabledLoc { get; set; }
+ public required int PositionLoc { get; set; }
+ public required int TargetLoc { get; set; }
+ public required int ColorLoc { get; set; }
+ public required int IntensityLoc { get; set; }
+}
+
+public enum PbrLightType
+{
+ Directional,
+ Point,
+ Spot,
+}
+
+public class PbrLights
+{
+ public static PbrLight CreatePbrLight(
+ int lightsCount,
+ PbrLightType type,
+ Vector3 pos,
+ Vector3 target,
+ Color color,
+ float intensity,
+ Shader shader
+ )
+ {
+ var light = new PbrLight
+ {
+ Enabled = true,
+ Type = type,
+ Position = pos,
+ Target = target,
+ Color = new Vector4(
+ color.R / 255.0f,
+ color.G / 255.0f,
+ color.B / 255.0f,
+ color.A / 255.0f
+ ),
+ Intensity = intensity,
+
+ EnabledLoc = Raylib.GetShaderLocation(shader, $"lights[{lightsCount}].enabled"),
+ TypeLoc = Raylib.GetShaderLocation(shader, $"lights[{lightsCount}].type"),
+ PositionLoc = Raylib.GetShaderLocation(shader, $"lights[{lightsCount}].position"),
+ TargetLoc = Raylib.GetShaderLocation(shader, $"lights[{lightsCount}].target"),
+ ColorLoc = Raylib.GetShaderLocation(shader, $"lights[{lightsCount}].color"),
+ IntensityLoc = Raylib.GetShaderLocation(shader, $"lights[{lightsCount}].intensity")
+ };
+
+ UpdateShaderValues(shader, light);
+
+ return light;
+ }
+
+ public static void UpdateShaderValues(Shader shader, PbrLight light)
+ {
+ Raylib.SetShaderValue(shader, light.EnabledLoc , light.Enabled ? 1 : 0 , ShaderUniformDataType.Int);
+ Raylib.SetShaderValue(shader, light.TypeLoc , light.Type , ShaderUniformDataType.Int);
+ Raylib.SetShaderValue(shader, light.TargetLoc , light.Target , ShaderUniformDataType.Vec3);
+ Raylib.SetShaderValue(shader, light.PositionLoc , light.Position , ShaderUniformDataType.Vec3);
+ Raylib.SetShaderValue(shader, light.ColorLoc , light.Color , ShaderUniformDataType.Vec4);
+ Raylib.SetShaderValue(shader, light.IntensityLoc, light.Intensity , ShaderUniformDataType.Float);
+ }
+}
diff --git a/Community/Shaders/RaylibCsExamples.Community.Shaders.BasicPbr/Program.cs b/Community/Shaders/RaylibCsExamples.Community.Shaders.BasicPbr/Program.cs
new file mode 100644
index 0000000..666cc41
--- /dev/null
+++ b/Community/Shaders/RaylibCsExamples.Community.Shaders.BasicPbr/Program.cs
@@ -0,0 +1,267 @@
+using System.Numerics;
+using Raylib_cs;
+
+namespace RaylibCsExamples.Community.Shaders.BasicPbr;
+
+public class Program
+{
+ private const int GLSL_VERSION = 330;
+
+ public static unsafe int Main()
+ {
+ const int screenWidth = 1600;
+ const int screenHeight = 900;
+
+ Raylib.SetConfigFlags(ConfigFlags.Msaa4xHint);
+ Raylib.InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic pbr");
+
+ var camera = new Camera3D
+ {
+ Position = new Vector3(2.0f, 4.0f, 6.0f),
+ Target = new Vector3(0.0f, 0.5f, 0.0f),
+ Up = new Vector3(0.0f, 1.0f, 0.0f),
+ FovY = 45.0f,
+ Projection = CameraProjection.Perspective
+ };
+
+ var shader = Raylib.LoadShader(
+ "Resources/pbr.vs",
+ "Resources/pbr.fs"
+ );
+
+ shader.Locs[(int)ShaderLocationIndex.MapAlbedo] = Raylib.GetShaderLocation(shader, "albedoMap");
+ // WARNING: Metalness, roughness, and ambient occlusion are all packed into a MRA texture
+ // They are passed as to the SHADER_LOC_MAP_METALNESS location for convenience,
+ // shader already takes care of it accordingly
+ shader.Locs[(int)ShaderLocationIndex.MapMetalness] = Raylib.GetShaderLocation(shader, "mraMap");
+ shader.Locs[(int)ShaderLocationIndex.MapNormal] = Raylib.GetShaderLocation(shader, "normalMap");
+ // WARNING: Similar to the MRA map, the emissive map packs different information
+ // into a single texture: it stores height and emission data
+ // It is binded to SHADER_LOC_MAP_EMISSION location an properly processed on shader
+ shader.Locs[(int)ShaderLocationIndex.MapEmission] = Raylib.GetShaderLocation(shader, "emissiveMap");
+ shader.Locs[(int)ShaderLocationIndex.ColorDiffuse] = Raylib.GetShaderLocation(shader, "albedoColor");
+
+ shader.Locs[(int)ShaderLocationIndex.VectorView] = Raylib.GetShaderLocation(shader, "viewPos");
+
+ var lightCountLoc = Raylib.GetShaderLocation(shader, "numOfLights");
+ var maxLightCount = 4;
+
+ Raylib.SetShaderValue(shader, lightCountLoc, &maxLightCount, ShaderUniformDataType.Int);
+
+ var ambientIntensity = .02f;
+ var ambientColor = new Color(26, 32, 135, 255);
+ var ambientColorNormalized = new Vector3(
+ ambientColor.R / 255.0f,
+ ambientColor.G / 255.0f,
+ ambientColor.B / 255.0f
+ );
+
+ Raylib.SetShaderValue(shader, Raylib.GetShaderLocation(shader, "ambientColor"), &ambientColorNormalized, ShaderUniformDataType.Vec3);
+ Raylib.SetShaderValue(shader, Raylib.GetShaderLocation(shader, "ambient"), &ambientIntensity, ShaderUniformDataType.Float);
+
+ var car = LoadCarModel(shader);
+ var floor = LoadFloorModel(shader);
+
+ var lights = new[] {
+ PbrLights.CreatePbrLight(
+ lightsCount : 0,
+ type : PbrLightType.Point,
+ pos : new Vector3(-1, 1, -2),
+ target : Vector3.Zero,
+ color : Color.Yellow,
+ intensity : 4,
+ shader : shader
+ ),
+
+ PbrLights.CreatePbrLight(
+ lightsCount : 1,
+ type : PbrLightType.Point,
+ pos : new Vector3(-2, 1, 1),
+ target : Vector3.Zero,
+ color : Color.Green,
+ intensity : 3.3f,
+ shader : shader
+ ),
+
+ PbrLights.CreatePbrLight(
+ lightsCount : 2,
+ type : PbrLightType.Point,
+ pos : new Vector3(-2, 1, 1),
+ target : Vector3.Zero,
+ color : Color.Red,
+ intensity : 8.3f,
+ shader : shader
+ ),
+
+ PbrLights.CreatePbrLight(
+ lightsCount : 3,
+ type : PbrLightType.Point,
+ pos : new Vector3(1, 1, -2),
+ target : Vector3.Zero,
+ color : Color.Black,
+ intensity : 2,
+ shader : shader
+ )
+ };
+
+ // Setup material texture maps usage in shader
+ // NOTE: By default, the texture maps are always used
+ var usage = 1;
+ Raylib.SetShaderValue(shader, Raylib.GetShaderLocation(shader, "useTexAlbedo") , &usage, ShaderUniformDataType.Int);
+ Raylib.SetShaderValue(shader, Raylib.GetShaderLocation(shader, "useTexNormal") , &usage, ShaderUniformDataType.Int);
+ Raylib.SetShaderValue(shader, Raylib.GetShaderLocation(shader, "useTexMRA") , &usage, ShaderUniformDataType.Int);
+ Raylib.SetShaderValue(shader, Raylib.GetShaderLocation(shader, "useTexEmissive") , &usage, ShaderUniformDataType.Int);
+ Raylib.SetTargetFPS(60);
+
+ var emissiveIntensityLoc = Raylib.GetShaderLocation(shader, "emissivePower");
+ var emissiveColorLoc = Raylib.GetShaderLocation(shader, "emissiveColor");
+ var textureTilingLoc = Raylib.GetShaderLocation(shader, "tiling");
+
+ while (!Raylib.WindowShouldClose())
+ {
+ Raylib.UpdateCamera(&camera, CameraMode.Orbital);
+ Raylib.SetShaderValue(shader, shader.Locs[(int)ShaderLocationIndex.VectorView], camera.Position, ShaderUniformDataType.Vec3);
+
+ if (Raylib.IsKeyPressed(KeyboardKey.One))
+ {
+ lights[2].Enabled = !lights[2].Enabled;
+ }
+
+ if (Raylib.IsKeyPressed(KeyboardKey.Two))
+ {
+ lights[1].Enabled = !lights[1].Enabled;
+ }
+
+ if (Raylib.IsKeyPressed(KeyboardKey.Three))
+ {
+ lights[3].Enabled = !lights[3].Enabled;
+ }
+
+ if (Raylib.IsKeyPressed(KeyboardKey.Four))
+ {
+ lights[0].Enabled = !lights[0].Enabled;
+ }
+
+ foreach (var light in lights)
+ {
+ UpdateLight(shader, light);
+ }
+
+ Raylib.BeginDrawing();
+ {
+ Raylib.ClearBackground(Color.Black);
+ Raylib.BeginMode3D(camera);
+ {
+
+
+ var carTextureTiling = new Vector2(.5f, .5f);
+ var floorTextureTiling = new Vector2(.5f, .5f);
+
+ Raylib.SetShaderValue(shader, textureTilingLoc, &floorTextureTiling, ShaderUniformDataType.Vec2);
+ var floorEmissiveColor = Raylib.ColorNormalize(floor.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color);
+ Raylib.SetShaderValue(shader, emissiveColorLoc, &floorEmissiveColor, ShaderUniformDataType.Vec4);
+
+ Raylib.DrawModel(floor, Vector3.Zero, 5, Color.White);
+
+ Raylib.SetShaderValue(shader, textureTilingLoc, &carTextureTiling, ShaderUniformDataType.Vec2);
+ var carEmissiveColor = Raylib.ColorNormalize(car.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color);
+ Raylib.SetShaderValue(shader, emissiveColorLoc, &carEmissiveColor, ShaderUniformDataType.Vec4);
+
+ var emissiveIntensity = .01f;
+ Raylib.SetShaderValue(shader, emissiveColorLoc, &emissiveIntensity, ShaderUniformDataType.Float);
+
+ Raylib.DrawModel(car, Vector3.Zero, .005f, Color.White);
+
+ foreach (var light in lights)
+ {
+ var color = light.Color;
+ var lightColor = new Color(
+ (byte)(color.X * 255),
+ (byte)(color.Y * 255),
+ (byte)(color.Z * 255),
+ (byte)(color.W * 255)
+ );
+
+ if (light.Enabled)
+ {
+ Raylib.DrawSphereEx(light.Position, .2f, 8, 8, lightColor);
+ }
+ else
+ {
+ Raylib.DrawSphereWires(light.Position, .2f, 8, 8, Raylib.ColorAlpha(lightColor, .3f));
+ }
+ }
+ }
+ Raylib.EndMode3D();
+
+ Raylib.DrawText("ToggleLights: [1][2][3][4]", 10, 40, 20, Color.LightGray);
+ Raylib.DrawText("(c) Old Rusty Car model by Renafox (https://skfb.ly/LxRy)", screenWidth - 320, screenHeight - 20, 10, Color.LightGray);
+ Raylib.DrawFPS(10, 10);
+ }
+ Raylib.EndDrawing();
+ }
+
+ car.Materials[0].Shader = new();
+ Raylib.UnloadMaterial(car.Materials[0]);
+ car.Materials[0].Maps = null;
+ Raylib.UnloadModel(car);
+
+ floor.Materials[0].Shader = new();
+ Raylib.UnloadMaterial(floor.Materials[0]);
+ floor.Materials[0].Maps = null;
+ Raylib.UnloadModel(floor);
+
+ Raylib.UnloadShader(shader);
+
+ Raylib.CloseWindow();
+
+ return 0;
+ }
+
+ private static void UpdateLight(Shader shader, PbrLight light)
+ {
+ Raylib.SetShaderValue(shader, light.EnabledLoc, light.Enabled, ShaderUniformDataType.Int);
+ Raylib.SetShaderValue(shader, light.TypeLoc, light.Type, ShaderUniformDataType.Int);
+ Raylib.SetShaderValue(shader, light.PositionLoc, light.Position, ShaderUniformDataType.Vec3);
+ Raylib.SetShaderValue(shader, light.ColorLoc, light.Color, ShaderUniformDataType.Vec4);
+ Raylib.SetShaderValue(shader, light.IntensityLoc, light.Intensity, ShaderUniformDataType.Float);
+ }
+
+ private static unsafe Model LoadFloorModel(Shader shader)
+ {
+ var floor = Raylib.LoadModel("Resources/plane.glb");
+
+ floor.Materials[0].Shader = shader;
+ floor.Materials[0].Maps[(int)MaterialMapIndex.Albedo].Color = Color.White;
+ floor.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 0;
+ floor.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value = 0;
+ floor.Materials[0].Maps[(int)MaterialMapIndex.Occlusion].Value = 1;
+ floor.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color = Color.Black;
+
+ floor.Materials[0].Maps[(int)MaterialMapIndex.Albedo].Texture = Raylib.LoadTexture("Resources/road_a.png");
+ floor.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Texture = Raylib.LoadTexture("Resources/road_mra.png");
+ floor.Materials[0].Maps[(int)MaterialMapIndex.Normal].Texture = Raylib.LoadTexture("Resources/road_n.png");
+
+ return floor;
+ }
+
+ private static unsafe Model LoadCarModel(Shader shader)
+ {
+ var car = Raylib.LoadModel("Resources/old_car_new.glb");
+
+ car.Materials[0].Shader = shader;
+
+ car.Materials[0].Maps[(int)MaterialMapIndex.Albedo].Color = Color.White;
+ car.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Value = 0;
+ car.Materials[0].Maps[(int)MaterialMapIndex.Roughness].Value = 0;
+ car.Materials[0].Maps[(int)MaterialMapIndex.Occlusion].Value = 1;
+ car.Materials[0].Maps[(int)MaterialMapIndex.Emission].Color = new(255, 162, 0, 255);
+
+ car.Materials[0].Maps[(int)MaterialMapIndex.Albedo].Texture = Raylib.LoadTexture("Resources/old_car_d.png");
+ car.Materials[0].Maps[(int)MaterialMapIndex.Metalness].Texture = Raylib.LoadTexture("Resources/old_car_mra.png");
+ car.Materials[0].Maps[(int)MaterialMapIndex.Normal].Texture = Raylib.LoadTexture("Resources/old_car_n.png");
+ car.Materials[0].Maps[(int)MaterialMapIndex.Emission].Texture = Raylib.LoadTexture("Resources/old_car_e.png");
+
+ return car;
+ }
+}
\ No newline at end of file
diff --git a/Community/Shaders/RaylibCsExamples.Community.Shaders.BasicPbr/RaylibCsExamples.Community.Shaders.BasicPbr.csproj b/Community/Shaders/RaylibCsExamples.Community.Shaders.BasicPbr/RaylibCsExamples.Community.Shaders.BasicPbr.csproj
new file mode 100644
index 0000000..0f3a17c
--- /dev/null
+++ b/Community/Shaders/RaylibCsExamples.Community.Shaders.BasicPbr/RaylibCsExamples.Community.Shaders.BasicPbr.csproj
@@ -0,0 +1,56 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+ true
+
+
+
+
+ Always
+
+
+
+ Always
+
+
+
+ Always
+
+
+
+ Always
+
+
+
+ Always
+
+
+
+ Always
+
+
+
+ Always
+
+
+
+ Always
+
+
+
+ Always
+
+
+
+ Always
+
+
+
+ Always
+
+
+
diff --git a/RaylibCsExamples.slnx b/RaylibCsExamples.slnx
index d13bae5..ac15d4e 100644
--- a/RaylibCsExamples.slnx
+++ b/RaylibCsExamples.slnx
@@ -20,6 +20,7 @@
+