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 @@ +