From b04c3f92a7c3ee1fe6634ab1da55e1cb9721f17c Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Thu, 2 Apr 2026 10:09:19 +0800 Subject: [PATCH 01/24] Add platform check to restrict app to supported OSes Added a check in the App class constructor to throw a PlatformNotSupportedException if the application is run on an OS other than Windows, macOS, or Linux. This prevents execution on unsupported platforms. --- sources/Experiments/CornellBox/App.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sources/Experiments/CornellBox/App.cs b/sources/Experiments/CornellBox/App.cs index c6c1ddf1..78bd4509 100644 --- a/sources/Experiments/CornellBox/App.cs +++ b/sources/Experiments/CornellBox/App.cs @@ -27,6 +27,11 @@ internal static class App static App() { + if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS() && !OperatingSystem.IsLinux()) + { + throw new PlatformNotSupportedException("This application only supports Windows, macOS, and Linux."); + } + if (OperatingSystem.IsWindows()) { Context = GraphicsContext.CreateDirectX12(useValidationLayer: true); From 05c05a9e1d0c77dea5c139c98255ddb56eea66bb Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Thu, 2 Apr 2026 10:12:54 +0800 Subject: [PATCH 02/24] Set window title and size during creation Refactored window initialization to specify the title ("Cornell Box - Zenith.NET") and size (1280x720) directly in the creation options, making the code more concise and improving clarity. --- sources/Experiments/CornellBox/App.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sources/Experiments/CornellBox/App.cs b/sources/Experiments/CornellBox/App.cs index 78bd4509..8646e411 100644 --- a/sources/Experiments/CornellBox/App.cs +++ b/sources/Experiments/CornellBox/App.cs @@ -47,8 +47,12 @@ static App() Context.ValidationMessage += static (sender, args) => Console.WriteLine($"[{args.Source} - {args.Severity}] {args.Message}"); - window = Window.Create(WindowOptions.Default with { API = GraphicsAPI.None }); - window.Size = new(1280, 720); + window = Window.Create(WindowOptions.Default with + { + API = GraphicsAPI.None, + Title = "Cornell Box - Zenith.NET", + Size = new(1280, 720) + }); window.Initialize(); window.Center(); From fbb70ccdaa3fcd0d2d587a20b4a973ba6bd4d4ce Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Thu, 2 Apr 2026 10:55:49 +0800 Subject: [PATCH 03/24] Restore Vulkan binding indexing; remove ImGui overlay helper Restores Vulkan-specific resource binding indexing logic in BindingHelper to ensure correct binding indices. Removes Extensions.cs, eliminating the ImGui overlay extension method and related helpers. --- .../CornellBox/Helpers/BindingHelper.cs | 22 +++++++------- .../CornellBox/Helpers/Extensions.cs | 29 ------------------- 2 files changed, 11 insertions(+), 40 deletions(-) delete mode 100644 sources/Experiments/CornellBox/Helpers/Extensions.cs diff --git a/sources/Experiments/CornellBox/Helpers/BindingHelper.cs b/sources/Experiments/CornellBox/Helpers/BindingHelper.cs index a4b30688..bcfd50ef 100644 --- a/sources/Experiments/CornellBox/Helpers/BindingHelper.cs +++ b/sources/Experiments/CornellBox/Helpers/BindingHelper.cs @@ -41,17 +41,6 @@ ResourceType.StructuredBufferReadWrite or } break; - case Backend.Vulkan: - { - for (int i = 0; i < bindings.Length; i++) - { - ref ResourceBinding binding = ref bindings[i]; - - binding = binding with { Index = (uint)i }; - } - } - break; - case Backend.Metal: { uint bufferIndex = 0; @@ -82,6 +71,17 @@ ResourceType.Texture or } } break; + + case Backend.Vulkan: + { + for (int i = 0; i < bindings.Length; i++) + { + ref ResourceBinding binding = ref bindings[i]; + + binding = binding with { Index = (uint)i }; + } + } + break; } return bindings; diff --git a/sources/Experiments/CornellBox/Helpers/Extensions.cs b/sources/Experiments/CornellBox/Helpers/Extensions.cs deleted file mode 100644 index 1ae53d40..00000000 --- a/sources/Experiments/CornellBox/Helpers/Extensions.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Hexa.NET.ImGui; - -namespace CornellBox.Helpers; - -internal static class Extensions -{ - private const ImGuiWindowFlags OverlayFlags = ImGuiWindowFlags.NoDecoration - | ImGuiWindowFlags.AlwaysAutoResize - | ImGuiWindowFlags.NoSavedSettings - | ImGuiWindowFlags.NoFocusOnAppearing - | ImGuiWindowFlags.NoInputs - | ImGuiWindowFlags.NoMove; - - extension(ImGui) - { - public static void Overlay(string name, Action action) - { - ImGui.SetNextWindowPos(new(10, 10), ImGuiCond.Always, new(0, 0)); - ImGui.SetNextWindowBgAlpha(0.35f); - - if (ImGui.Begin(name, OverlayFlags)) - { - action(); - - ImGui.End(); - } - } - } -} From 1032e791954e5a294f7ded3fdd499838539b7366 Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Thu, 2 Apr 2026 11:02:39 +0800 Subject: [PATCH 04/24] Refactor mouse event handler method names in CameraHandler Renamed Mouse_MouseDown, Mouse_MouseUp, and Mouse_MouseMove to OnMouseDown, OnMouseUp, and OnMouseMove for clarity. Updated event subscriptions to use the new method names. No changes to event handler logic. --- .../Experiments/CornellBox/Handlers/CameraHandler.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sources/Experiments/CornellBox/Handlers/CameraHandler.cs b/sources/Experiments/CornellBox/Handlers/CameraHandler.cs index 769c4cdb..e4d15527 100644 --- a/sources/Experiments/CornellBox/Handlers/CameraHandler.cs +++ b/sources/Experiments/CornellBox/Handlers/CameraHandler.cs @@ -12,9 +12,9 @@ internal class CameraHandler public CameraHandler(IInputContext input, Matrix4x4 initial) { IMouse mouse = input.Mice[0]; - mouse.MouseDown += Mouse_MouseDown; - mouse.MouseUp += Mouse_MouseUp; - mouse.MouseMove += Mouse_MouseMove; + mouse.MouseDown += OnMouseDown; + mouse.MouseUp += OnMouseUp; + mouse.MouseMove += OnMouseMove; IKeyboard keyboard = input.Keyboards[0]; keyboard.KeyDown += OnKeyDown; @@ -86,7 +86,7 @@ public void Update(double delta, uint width, uint height) } } - private void Mouse_MouseDown(IMouse arg1, MouseButton arg2) + private void OnMouseDown(IMouse arg1, MouseButton arg2) { if (arg2 is MouseButton.Right) { @@ -94,7 +94,7 @@ private void Mouse_MouseDown(IMouse arg1, MouseButton arg2) } } - private void Mouse_MouseUp(IMouse arg1, MouseButton arg2) + private void OnMouseUp(IMouse arg1, MouseButton arg2) { if (arg2 is MouseButton.Right) { @@ -102,7 +102,7 @@ private void Mouse_MouseUp(IMouse arg1, MouseButton arg2) } } - private void Mouse_MouseMove(IMouse mouse, Vector2 vector) + private void OnMouseMove(IMouse mouse, Vector2 vector) { const float clipRadians = 89.0f * MathF.PI / 180.0f; From 9f318592973d6ae954a7d71b074344ca4129c0e2 Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Thu, 2 Apr 2026 20:30:33 +0800 Subject: [PATCH 05/24] Move shaders to external Slang files and load from disk Refactored the renderer code to remove embedded shader source strings from C# files. Shaders for both path tracing and rasterization are now stored as external `.slang` files in `Assets/Shaders`, and are loaded at runtime using a new `ShaderPath` helper. This improves maintainability, enables syntax highlighting, and separates shader code from application logic. No changes were made to shader logic or rendering behavior. --- .../Assets/Shaders/PathTracing.slang | 426 ++++++++++++++++++ .../Assets/Shaders/Rasterization.slang | 159 +++++++ .../Renderers/PathTracingRenderer.cs | 401 +---------------- .../Renderers/RasterizationRenderer.cs | 124 +---- .../CornellBox/Renderers/Renderer.cs | 5 + 5 files changed, 593 insertions(+), 522 deletions(-) create mode 100644 sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang create mode 100644 sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang diff --git a/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang b/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang new file mode 100644 index 00000000..4a8fb8a2 --- /dev/null +++ b/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang @@ -0,0 +1,426 @@ +static const float3 LightMin = float3(213.0, 548.6, 227.0); +static const float3 LightMax = float3(343.0, 548.6, 332.0); +static const float LightArea = (343.0 - 213.0) * (332.0 - 227.0); +static const float3 LightNormal = float3(0.0, -1.0, 0.0); +static const float PI = 3.14159265; + +struct Vertex +{ + private float4 PositionAndPadding; + + private float4 NormalAndMaterialID; + + property float3 Position + { + get { + return PositionAndPadding.xyz; + } + } + + property float3 Normal + { + get { + return NormalAndMaterialID.xyz; + } + } + + property uint MaterialID + { + get { + return asuint(NormalAndMaterialID.w); + } + } +}; + +struct Material +{ + private float4 AlbedoAndEmission; + + float Metallic; + + float Roughness; + + private float padding0; + + private float padding1; + + property float3 Albedo + { + get { + return AlbedoAndEmission.xyz; + } + } + + property float Emission + { + get { + return AlbedoAndEmission.w; + } + } +}; + +struct CameraParams +{ + float4x4 InvView; + + float4x4 InvProjection; + + private float4 PositionAndPadding; + + uint FrameCount; + + uint Width; + + uint Height; + + private float padding0; + + property float3 Position + { + get { + return PositionAndPadding.xyz; + } + } +}; + +RaytracingAccelerationStructure scene; +ConstantBuffer camera; +StructuredBuffer vertices; +StructuredBuffer indices; +StructuredBuffer materials; +RWTexture2D accumTexture; +RWTexture2D outputTexture; + +float DistributionGGX(float NdotH, float roughness) +{ + float a = roughness * roughness; + float a2 = a * a; + float denom = NdotH * NdotH * (a2 - 1.0) + 1.0; + + return a2 / (PI * denom * denom); +} + +float GeometrySchlickGGX(float NdotX, float roughness) +{ + float r = roughness + 1.0; + float k = (r * r) / 8.0; + + return NdotX / (NdotX * (1.0 - k) + k); +} + +float GeometrySmith(float NdotV, float NdotL, float roughness) +{ + return GeometrySchlickGGX(NdotV, roughness) * GeometrySchlickGGX(NdotL, roughness); +} + +float3 FresnelSchlick(float cosTheta, float3 F0) +{ + return F0 + (1.0 - F0) * pow(saturate(1.0 - cosTheta), 5.0); +} + +float3 evaluateBRDF(float3 N, float3 V, float3 L, Material mat) +{ + float roughness = max(mat.Roughness, 0.04); + float NdotL = max(dot(N, L), 0.0); + float NdotV = max(dot(N, V), 0.001); + float3 H = normalize(V + L); + float NdotH = max(dot(N, H), 0.0); + float HdotV = max(dot(H, V), 0.0); + + float3 F0 = lerp(float3(0.04, 0.04, 0.04), mat.Albedo, mat.Metallic); + + float D = DistributionGGX(NdotH, roughness); + float G = GeometrySmith(NdotV, NdotL, roughness); + float3 F = FresnelSchlick(HdotV, F0); + + float3 specular = D * G * F / (4.0 * NdotV * NdotL + 0.0001); + + float3 kD = (1.0 - F) * (1.0 - mat.Metallic); + float3 diffuse = kD * mat.Albedo / PI; + + return diffuse + specular; +} + +float3 sampleGGXHalfVector(float3 N, float roughness, inout uint seed) +{ + float a = roughness * roughness; + + float r1 = randomFloat(seed); + float r2 = randomFloat(seed); + + float phi = 2.0 * PI * r1; + float cosTheta = sqrt((1.0 - r2) / (1.0 + (a * a - 1.0) * r2)); + float sinTheta = sqrt(1.0 - cosTheta * cosTheta); + + float3 w = N; + float3 helper = abs(w.x) > 0.99 ? float3(0.0, 1.0, 0.0) : float3(1.0, 0.0, 0.0); + float3 u = normalize(cross(helper, w)); + float3 v = cross(w, u); + + return normalize(u * cos(phi) * sinTheta + v * sin(phi) * sinTheta + w * cosTheta); +} + +uint pcgHash(uint input) +{ + uint state = input * 747796405u + 2891336453u; + uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + + return (word >> 22u) ^ word; +} + +float randomFloat(inout uint seed) +{ + seed = pcgHash(seed); + + return float(seed) / 4294967295.0; +} + +float3 cosineSampleHemisphere(float3 normal, inout uint seed) +{ + float r1 = randomFloat(seed); + float r2 = randomFloat(seed); + + float phi = 2.0 * 3.14159265 * r1; + float sinTheta = sqrt(r2); + float cosTheta = sqrt(1.0 - r2); + + float3 w = normal; + float3 helper = abs(w.x) > 0.99 ? float3(0.0, 1.0, 0.0) : float3(1.0, 0.0, 0.0); + float3 u = normalize(cross(helper, w)); + float3 v = cross(w, u); + + return normalize(u * cos(phi) * sinTheta + v * sin(phi) * sinTheta + w * cosTheta); +} + +bool traceShadowRay(float3 origin, float3 direction, float maxDist) +{ + RayDesc shadowRay; + shadowRay.Origin = origin; + shadowRay.Direction = direction; + shadowRay.TMin = 0.001; + shadowRay.TMax = maxDist - 0.001; + + RayQuery shadowQuery; + shadowQuery.TraceRayInline(scene, RAY_FLAG_NONE, 0xFF, shadowRay); + + while (shadowQuery.Proceed()) + { + } + + return shadowQuery.CommittedStatus() != COMMITTED_NOTHING; +} + +float3 sampleLightDirect(float3 hitPos, float3 hitNormal, float3 V, Material mat, inout uint rng) +{ + float u = randomFloat(rng); + float v = randomFloat(rng); + + float3 lightPoint = float3(lerp(LightMin.x, LightMax.x, u), LightMin.y, lerp(LightMin.z, LightMax.z, v)); + + float3 toLight = lightPoint - hitPos; + float dist = length(toLight); + float3 L = toLight / dist; + + float NdotL = dot(hitNormal, L); + if (NdotL <= 0.0) + { + return float3(0.0, 0.0, 0.0); + } + + float lightCosine = max(dot(LightNormal, -L), 0.0); + if (lightCosine <= 0.0) + { + return float3(0.0, 0.0, 0.0); + } + + if (traceShadowRay(hitPos + hitNormal * 0.001, L, dist)) + { + return float3(0.0, 0.0, 0.0); + } + + Material lightMat = materials[3]; + float3 lightEmission = lightMat.Albedo * lightMat.Emission; + + float pdf = 1.0 / LightArea; + float3 brdf = evaluateBRDF(hitNormal, V, L, mat); + float geometryTerm = NdotL * lightCosine / (dist * dist); + + return lightEmission * brdf * geometryTerm / pdf; +} + +float3 tracePath(float3 origin, float3 direction, inout uint rng) +{ + float3 throughput = float3(1.0, 1.0, 1.0); + float3 radiance = float3(0.0, 0.0, 0.0); + + for (int bounce = 0; bounce < 8; bounce++) + { + RayDesc ray; + ray.Origin = origin; + ray.Direction = direction; + ray.TMin = 0.001; + ray.TMax = 100000.0; + + RayQuery query; + query.TraceRayInline(scene, RAY_FLAG_NONE, 0xFF, ray); + + while (query.Proceed()) + { + } + + if (query.CommittedStatus() == COMMITTED_NOTHING) + { + float3 skyColor = lerp(float3(0.15, 0.12, 0.10), float3(0.30, 0.35, 0.45), saturate(direction.y * 0.5 + 0.5)); + radiance += throughput * skyColor; + + break; + } + + uint primIdx = query.CommittedPrimitiveIndex(); + float2 bary = query.CommittedTriangleBarycentrics(); + float t = query.CommittedRayT(); + float3 hitPos = origin + direction * t; + + uint i0 = indices[primIdx * 3 + 0]; + uint i1 = indices[primIdx * 3 + 1]; + uint i2 = indices[primIdx * 3 + 2]; + + Vertex v0 = vertices[i0]; + Vertex v1 = vertices[i1]; + Vertex v2 = vertices[i2]; + + float3 baryWeights = float3(1.0 - bary.x - bary.y, bary.x, bary.y); + float3 normal = normalize(v0.Normal * baryWeights.x + v1.Normal * baryWeights.y + v2.Normal * baryWeights.z); + + if (dot(normal, direction) > 0.0) + { + normal = -normal; + } + + Material mat = materials[v0.MaterialID]; + + if (mat.Emission > 0.0) + { + if (bounce == 0) + { + radiance += throughput * mat.Albedo * mat.Emission; + } + + break; + } + + float3 V = -direction; + + radiance += throughput * sampleLightDirect(hitPos, normal, V, mat, rng); + + float3 F0 = lerp(float3(0.04, 0.04, 0.04), mat.Albedo, mat.Metallic); + float roughness = max(mat.Roughness, 0.04); + float specWeight = max(F0.r, max(F0.g, F0.b)); + float diffWeight = (1.0 - specWeight) * (1.0 - mat.Metallic); + float total = specWeight + diffWeight; + float specProb = specWeight / total; + + float3 newDir; + float NdotV = max(dot(normal, V), 0.001); + + if (randomFloat(rng) < specProb) + { + float3 H = sampleGGXHalfVector(normal, roughness, rng); + newDir = reflect(-V, H); + + float NdotL = dot(normal, newDir); + if (NdotL <= 0.0) + { + break; + } + + float NdotH = max(dot(normal, H), 0.0); + float HdotV = max(dot(H, V), 0.0); + + float3 F = FresnelSchlick(HdotV, F0); + float G = GeometrySmith(NdotV, NdotL, roughness); + + float3 specThroughput = F * G * HdotV / (NdotV * NdotH * specProb + 0.0001); + throughput *= min(specThroughput, float3(10.0, 10.0, 10.0)); + } + else + { + newDir = cosineSampleHemisphere(normal, rng); + + float3 H = normalize(V + newDir); + float HdotV = max(dot(H, V), 0.0); + + float3 F = FresnelSchlick(HdotV, F0); + float3 kD = (1.0 - F) * (1.0 - mat.Metallic); + + throughput *= kD * mat.Albedo / (1.0 - specProb + 0.0001); + } + + origin = hitPos + normal * 0.001; + direction = newDir; + + if (bounce >= 2) + { + float p = max(throughput.r, max(throughput.g, throughput.b)); + + if (randomFloat(rng) > p) + { + break; + } + + throughput /= p; + } + } + + return radiance; +} + +[numthreads(16, 16, 1)] +void CSMain(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + uint2 pixel = dispatchThreadID.xy; + + if (pixel.x >= camera.Width || pixel.y >= camera.Height) + { + return; + } + + uint rng = pcgHash(pixel.x + pixel.y * camera.Width + camera.FrameCount * camera.Width * camera.Height); + + float2 jitter = float2(randomFloat(rng), randomFloat(rng)); + float2 uv = (float2(pixel) + jitter) / float2(camera.Width, camera.Height); + float2 ndc = uv * 2.0 - 1.0; + ndc.y = -ndc.y; + + float4 target = mul(float4(ndc, 1.0, 1.0), camera.InvProjection); + float3 localDir = normalize(target.xyz / target.w); + float3 direction = normalize(mul(float4(localDir, 0.0), camera.InvView).xyz); + float3 origin = camera.Position; + + float3 color = tracePath(origin, direction, rng); + color = min(color, float3(30.0, 30.0, 30.0)); + + float4 prev = accumTexture[pixel]; + float4 accumulated; + + if (camera.FrameCount == 0) + { + accumulated = float4(color, 1.0); + } + else + { + accumulated = prev + float4(color, 1.0); + } + + accumTexture[pixel] = accumulated; + + float3 avg = accumulated.rgb / float(camera.FrameCount + 1); + + // ACES tonemapping + float3 a = avg * (avg * 2.51 + 0.03); + float3 b = avg * (avg * 2.43 + 0.59) + 0.14; + avg = saturate(a / b); + + avg = pow(avg, 1.0 / 2.2); + outputTexture[pixel] = float4(avg, 1.0); +} diff --git a/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang b/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang new file mode 100644 index 00000000..a54ad98a --- /dev/null +++ b/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang @@ -0,0 +1,159 @@ +struct Material +{ + private float4 AlbedoAndEmission; + + float Metallic; + + float Roughness; + + private float padding0; + + private float padding1; + + property float3 Albedo + { + get { + return AlbedoAndEmission.xyz; + } + } + + property float Emission + { + get { + return AlbedoAndEmission.w; + } + } +}; + +struct RasterConstants +{ + float4x4 Model; + + float4x4 View; + + float4x4 Projection; + + private float4 LightPosAndPadding; + + private float4 LightColorAndPadding; + + private float4 CameraPosAndPadding; + + property float3 LightPos + { + get { + return LightPosAndPadding.xyz; + } + } + + property float3 LightColor + { + get { + return LightColorAndPadding.xyz; + } + } + + property float3 CameraPos + { + get { + return CameraPosAndPadding.xyz; + } + } +}; + +struct VSInput +{ + private float4 PositionAndPadding : POSITION0; + + private float4 NormalAndMaterialID : NORMAL0; + + property float3 Position + { + get { + return PositionAndPadding.xyz; + } + } + + property float3 Normal + { + get { + return NormalAndMaterialID.xyz; + } + } + + property uint MaterialID + { + get { + return asuint(NormalAndMaterialID.w); + } + } +}; + +struct PSInput +{ + float4 Position : SV_POSITION; + + float3 WorldPos : TEXCOORD0; + + float3 Normal : TEXCOORD1; + + nointerpolation uint MaterialID : TEXCOORD2; +}; + +ConstantBuffer constants; +StructuredBuffer materials; + +PSInput VSMain(VSInput input) +{ + float4 worldPos = mul(float4(input.Position, 1.0), constants.Model); + + PSInput output; + output.Position = mul(mul(worldPos, constants.View), constants.Projection); + output.WorldPos = worldPos.xyz; + output.Normal = normalize(mul(float4(input.Normal, 0.0), constants.Model).xyz); + output.MaterialID = input.MaterialID; + + return output; +} + +float4 PSMain(PSInput input) : SV_TARGET +{ + Material mat = materials[input.MaterialID]; + + if (mat.Emission > 0.0) + { + float3 emissive = mat.Albedo * mat.Emission; + float3 mapped = emissive / (emissive + 1.0); + + return float4(pow(mapped, 1.0 / 2.2), 1.0); + } + + float3 N = normalize(input.Normal); + float3 worldPos = input.WorldPos; + float3 L = normalize(constants.LightPos - worldPos); + float3 V = normalize(constants.CameraPos - worldPos); + float3 H = normalize(L + V); + + float NdotL = max(dot(N, L), 0.0); + float NdotH = max(dot(N, H), 0.0); + float spec = pow(NdotH, 64.0); + + float dist = length(constants.LightPos - worldPos); + float atten = 1.0 / (1.0 + 0.000005 * dist * dist); + + float hemiFactor = N.y * 0.5 + 0.5; + float3 ambient = mat.Albedo * lerp(0.06, 0.15, hemiFactor); + float3 diffuse = mat.Albedo * constants.LightColor * NdotL * atten; + float3 specular = constants.LightColor * spec * atten * 0.1; + + float3 color = ambient + diffuse + specular; + + // ACES tonemapping + float3 a = color * (color * 2.51 + 0.03); + float3 b = color * (color * 2.43 + 0.59) + 0.14; + color = saturate(a / b); + + color = pow(color, 1.0 / 2.2); + + return float4(color, 1.0); +} diff --git a/sources/Experiments/CornellBox/Renderers/PathTracingRenderer.cs b/sources/Experiments/CornellBox/Renderers/PathTracingRenderer.cs index 161535ca..d75ec775 100644 --- a/sources/Experiments/CornellBox/Renderers/PathTracingRenderer.cs +++ b/sources/Experiments/CornellBox/Renderers/PathTracingRenderer.cs @@ -12,405 +12,6 @@ internal unsafe class PathTracingRenderer : Renderer { private const uint ThreadGroupSize = 16; - private const string ShaderSource = """ - struct Vertex - { - private float4 PositionAndPadding; - - private float4 NormalAndMaterialID; - - property float3 Position { get { return PositionAndPadding.xyz; } } - - property float3 Normal { get { return NormalAndMaterialID.xyz; } } - - property uint MaterialID { get { return asuint(NormalAndMaterialID.w); } } - }; - - struct Material - { - private float4 AlbedoAndEmission; - - float Metallic; - - float Roughness; - - private float padding0; - - private float padding1; - - property float3 Albedo { get { return AlbedoAndEmission.xyz; } } - - property float Emission { get { return AlbedoAndEmission.w; } } - }; - - struct CameraParams - { - float4x4 InvView; - - float4x4 InvProjection; - - private float4 PositionAndPadding; - - uint FrameCount; - - uint Width; - - uint Height; - - private float padding0; - - property float3 Position { get { return PositionAndPadding.xyz; } } - }; - - RaytracingAccelerationStructure scene; - ConstantBuffer camera; - StructuredBuffer vertices; - StructuredBuffer indices; - StructuredBuffer materials; - RWTexture2D accumTexture; - RWTexture2D outputTexture; - - // Light geometry constants (hardcoded ceiling light quad) - static const float3 LightMin = float3(213.0, 548.6, 227.0); - static const float3 LightMax = float3(343.0, 548.6, 332.0); - static const float LightArea = (343.0 - 213.0) * (332.0 - 227.0); - static const float3 LightNormal = float3(0.0, -1.0, 0.0); - static const float PI = 3.14159265; - - float DistributionGGX(float NdotH, float roughness) - { - float a = roughness * roughness; - float a2 = a * a; - float denom = NdotH * NdotH * (a2 - 1.0) + 1.0; - return a2 / (PI * denom * denom); - } - - float GeometrySchlickGGX(float NdotX, float roughness) - { - float r = roughness + 1.0; - float k = (r * r) / 8.0; - return NdotX / (NdotX * (1.0 - k) + k); - } - - float GeometrySmith(float NdotV, float NdotL, float roughness) - { - return GeometrySchlickGGX(NdotV, roughness) * GeometrySchlickGGX(NdotL, roughness); - } - - float3 FresnelSchlick(float cosTheta, float3 F0) - { - return F0 + (1.0 - F0) * pow(saturate(1.0 - cosTheta), 5.0); - } - - float3 evaluateBRDF(float3 N, float3 V, float3 L, Material mat) - { - float roughness = max(mat.Roughness, 0.04); - float NdotL = max(dot(N, L), 0.0); - float NdotV = max(dot(N, V), 0.001); - float3 H = normalize(V + L); - float NdotH = max(dot(N, H), 0.0); - float HdotV = max(dot(H, V), 0.0); - - float3 F0 = lerp(float3(0.04, 0.04, 0.04), mat.Albedo, mat.Metallic); - - float D = DistributionGGX(NdotH, roughness); - float G = GeometrySmith(NdotV, NdotL, roughness); - float3 F = FresnelSchlick(HdotV, F0); - - float3 specular = D * G * F / (4.0 * NdotV * NdotL + 0.0001); - - float3 kD = (1.0 - F) * (1.0 - mat.Metallic); - float3 diffuse = kD * mat.Albedo / PI; - - return diffuse + specular; - } - - float3 sampleGGXHalfVector(float3 N, float roughness, inout uint seed) - { - float a = roughness * roughness; - - float r1 = randomFloat(seed); - float r2 = randomFloat(seed); - - float phi = 2.0 * PI * r1; - float cosTheta = sqrt((1.0 - r2) / (1.0 + (a * a - 1.0) * r2)); - float sinTheta = sqrt(1.0 - cosTheta * cosTheta); - - float3 w = N; - float3 helper = abs(w.x) > 0.99 ? float3(0.0, 1.0, 0.0) : float3(1.0, 0.0, 0.0); - float3 u = normalize(cross(helper, w)); - float3 v = cross(w, u); - - return normalize(u * cos(phi) * sinTheta + v * sin(phi) * sinTheta + w * cosTheta); - } - - uint pcgHash(uint input) - { - uint state = input * 747796405u + 2891336453u; - uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; - return (word >> 22u) ^ word; - } - - float randomFloat(inout uint seed) - { - seed = pcgHash(seed); - return float(seed) / 4294967295.0; - } - - float3 cosineSampleHemisphere(float3 normal, inout uint seed) - { - float r1 = randomFloat(seed); - float r2 = randomFloat(seed); - - float phi = 2.0 * 3.14159265 * r1; - float sinTheta = sqrt(r2); - float cosTheta = sqrt(1.0 - r2); - - float3 w = normal; - float3 helper = abs(w.x) > 0.99 ? float3(0.0, 1.0, 0.0) : float3(1.0, 0.0, 0.0); - float3 u = normalize(cross(helper, w)); - float3 v = cross(w, u); - - return normalize(u * cos(phi) * sinTheta + v * sin(phi) * sinTheta + w * cosTheta); - } - - bool traceShadowRay(float3 origin, float3 direction, float maxDist) - { - RayDesc shadowRay; - shadowRay.Origin = origin; - shadowRay.Direction = direction; - shadowRay.TMin = 0.001; - shadowRay.TMax = maxDist - 0.001; - - RayQuery shadowQuery; - shadowQuery.TraceRayInline(scene, RAY_FLAG_NONE, 0xFF, shadowRay); - - while (shadowQuery.Proceed()) {} - - return shadowQuery.CommittedStatus() != COMMITTED_NOTHING; - } - - float3 sampleLightDirect(float3 hitPos, float3 hitNormal, float3 V, Material mat, inout uint rng) - { - float u = randomFloat(rng); - float v = randomFloat(rng); - - float3 lightPoint = float3( - lerp(LightMin.x, LightMax.x, u), - LightMin.y, - lerp(LightMin.z, LightMax.z, v) - ); - - float3 toLight = lightPoint - hitPos; - float dist = length(toLight); - float3 L = toLight / dist; - - float NdotL = dot(hitNormal, L); - if (NdotL <= 0.0) - { - return float3(0.0, 0.0, 0.0); - } - - float lightCosine = max(dot(LightNormal, -L), 0.0); - if (lightCosine <= 0.0) - { - return float3(0.0, 0.0, 0.0); - } - - if (traceShadowRay(hitPos + hitNormal * 0.001, L, dist)) - { - return float3(0.0, 0.0, 0.0); - } - - Material lightMat = materials[3]; - float3 lightEmission = lightMat.Albedo * lightMat.Emission; - - float pdf = 1.0 / LightArea; - float3 brdf = evaluateBRDF(hitNormal, V, L, mat); - float geometryTerm = NdotL * lightCosine / (dist * dist); - - return lightEmission * brdf * geometryTerm / pdf; - } - - float3 tracePath(float3 origin, float3 direction, inout uint rng) - { - float3 throughput = float3(1.0, 1.0, 1.0); - float3 radiance = float3(0.0, 0.0, 0.0); - - for (int bounce = 0; bounce < 8; bounce++) - { - RayDesc ray; - ray.Origin = origin; - ray.Direction = direction; - ray.TMin = 0.001; - ray.TMax = 100000.0; - - RayQuery query; - query.TraceRayInline(scene, RAY_FLAG_NONE, 0xFF, ray); - - while (query.Proceed()) {} - - if (query.CommittedStatus() == COMMITTED_NOTHING) - { - float3 skyColor = lerp(float3(0.15, 0.12, 0.10), float3(0.30, 0.35, 0.45), saturate(direction.y * 0.5 + 0.5)); - radiance += throughput * skyColor; - break; - } - - uint primIdx = query.CommittedPrimitiveIndex(); - float2 bary = query.CommittedTriangleBarycentrics(); - float t = query.CommittedRayT(); - float3 hitPos = origin + direction * t; - - uint i0 = indices[primIdx * 3 + 0]; - uint i1 = indices[primIdx * 3 + 1]; - uint i2 = indices[primIdx * 3 + 2]; - - Vertex v0 = vertices[i0]; - Vertex v1 = vertices[i1]; - Vertex v2 = vertices[i2]; - - float3 baryWeights = float3(1.0 - bary.x - bary.y, bary.x, bary.y); - float3 normal = normalize( - v0.Normal * baryWeights.x + - v1.Normal * baryWeights.y + - v2.Normal * baryWeights.z - ); - - if (dot(normal, direction) > 0.0) - { - normal = -normal; - } - - Material mat = materials[v0.MaterialID]; - - if (mat.Emission > 0.0) - { - if (bounce == 0) - { - radiance += throughput * mat.Albedo * mat.Emission; - } - - break; - } - - float3 V = -direction; - - radiance += throughput * sampleLightDirect(hitPos, normal, V, mat, rng); - - float3 F0 = lerp(float3(0.04, 0.04, 0.04), mat.Albedo, mat.Metallic); - float roughness = max(mat.Roughness, 0.04); - float specWeight = max(F0.r, max(F0.g, F0.b)); - float diffWeight = (1.0 - specWeight) * (1.0 - mat.Metallic); - float total = specWeight + diffWeight; - float specProb = specWeight / total; - - float3 newDir; - float NdotV = max(dot(normal, V), 0.001); - - if (randomFloat(rng) < specProb) - { - float3 H = sampleGGXHalfVector(normal, roughness, rng); - newDir = reflect(-V, H); - - float NdotL = dot(normal, newDir); - if (NdotL <= 0.0) - { - break; - } - - float NdotH = max(dot(normal, H), 0.0); - float HdotV = max(dot(H, V), 0.0); - - float3 F = FresnelSchlick(HdotV, F0); - float G = GeometrySmith(NdotV, NdotL, roughness); - - float3 specThroughput = F * G * HdotV / (NdotV * NdotH * specProb + 0.0001); - throughput *= min(specThroughput, float3(10.0, 10.0, 10.0)); - } - else - { - newDir = cosineSampleHemisphere(normal, rng); - - float3 H = normalize(V + newDir); - float HdotV = max(dot(H, V), 0.0); - - float3 F = FresnelSchlick(HdotV, F0); - float3 kD = (1.0 - F) * (1.0 - mat.Metallic); - - throughput *= kD * mat.Albedo / (1.0 - specProb + 0.0001); - } - - origin = hitPos + normal * 0.001; - direction = newDir; - - if (bounce >= 2) - { - float p = max(throughput.r, max(throughput.g, throughput.b)); - - if (randomFloat(rng) > p) - { - break; - } - - throughput /= p; - } - } - - return radiance; - } - - [numthreads(16, 16, 1)] - void CSMain(uint3 dispatchThreadID : SV_DispatchThreadID) - { - uint2 pixel = dispatchThreadID.xy; - - if (pixel.x >= camera.Width || pixel.y >= camera.Height) - { - return; - } - - uint rng = pcgHash(pixel.x + pixel.y * camera.Width + camera.FrameCount * camera.Width * camera.Height); - - float2 jitter = float2(randomFloat(rng), randomFloat(rng)); - float2 uv = (float2(pixel) + jitter) / float2(camera.Width, camera.Height); - float2 ndc = uv * 2.0 - 1.0; - ndc.y = -ndc.y; - - float4 target = mul(float4(ndc, 1.0, 1.0), camera.InvProjection); - float3 localDir = normalize(target.xyz / target.w); - float3 direction = normalize(mul(float4(localDir, 0.0), camera.InvView).xyz); - float3 origin = camera.Position; - - float3 color = tracePath(origin, direction, rng); - color = min(color, float3(30.0, 30.0, 30.0)); - - float4 prev = accumTexture[pixel]; - float4 accumulated; - - if (camera.FrameCount == 0) - { - accumulated = float4(color, 1.0); - } - else - { - accumulated = prev + float4(color, 1.0); - } - - accumTexture[pixel] = accumulated; - - float3 avg = accumulated.rgb / float(camera.FrameCount + 1); - - // ACES tonemapping - float3 a = avg * (avg * 2.51 + 0.03); - float3 b = avg * (avg * 2.43 + 0.59) + 0.14; - avg = saturate(a / b); - - avg = pow(avg, 1.0 / 2.2); - outputTexture[pixel] = float4(avg, 1.0); - } - """; - private readonly Buffer vertexBuffer; private readonly Buffer indexBuffer; private readonly Buffer materialBuffer; @@ -519,7 +120,7 @@ public PathTracingRenderer() ) }); - using Shader computeShader = App.Context.LoadShaderFromSource(ShaderSource, "CSMain", ShaderStageFlags.Compute); + using Shader computeShader = App.Context.LoadShaderFromFile(ShaderPath("PathTracing.slang"), "CSMain", ShaderStageFlags.Compute); pipeline = App.Context.CreateComputePipeline(new() { diff --git a/sources/Experiments/CornellBox/Renderers/RasterizationRenderer.cs b/sources/Experiments/CornellBox/Renderers/RasterizationRenderer.cs index 5bb33d23..dc25e5dd 100644 --- a/sources/Experiments/CornellBox/Renderers/RasterizationRenderer.cs +++ b/sources/Experiments/CornellBox/Renderers/RasterizationRenderer.cs @@ -10,126 +10,6 @@ namespace CornellBox.Renderers; internal unsafe class RasterizationRenderer : Renderer { - private const string ShaderSource = """ - struct Material - { - private float4 AlbedoAndEmission; - - float Metallic; - - float Roughness; - - private float padding0; - - private float padding1; - - property float3 Albedo { get { return AlbedoAndEmission.xyz; } } - - property float Emission { get { return AlbedoAndEmission.w; } } - }; - - struct RasterConstants - { - float4x4 Model; - - float4x4 View; - - float4x4 Projection; - - private float4 LightPosAndPadding; - - private float4 LightColorAndPadding; - - private float4 CameraPosAndPadding; - - property float3 LightPos { get { return LightPosAndPadding.xyz; } } - - property float3 LightColor { get { return LightColorAndPadding.xyz; } } - - property float3 CameraPos { get { return CameraPosAndPadding.xyz; } } - }; - - ConstantBuffer cb; - StructuredBuffer materials; - - struct VSInput - { - private float4 PositionAndPadding : POSITION0; - - private float4 NormalAndMaterialID : NORMAL0; - - property float3 Position { get { return PositionAndPadding.xyz; } } - - property float3 Normal { get { return NormalAndMaterialID.xyz; } } - - property uint MaterialID { get { return asuint(NormalAndMaterialID.w); } } - }; - - struct PSInput - { - float4 Position : SV_POSITION; - - float3 WorldPos : TEXCOORD0; - - float3 Normal : TEXCOORD1; - - nointerpolation uint MaterialID : TEXCOORD2; - }; - - PSInput VSMain(VSInput input) - { - float4 worldPos = mul(float4(input.Position, 1.0), cb.Model); - - PSInput output; - output.Position = mul(mul(worldPos, cb.View), cb.Projection); - output.WorldPos = worldPos.xyz; - output.Normal = normalize(mul(float4(input.Normal, 0.0), cb.Model).xyz); - output.MaterialID = input.MaterialID; - - return output; - } - - float4 PSMain(PSInput input) : SV_TARGET - { - Material mat = materials[input.MaterialID]; - - if (mat.Emission > 0.0) - { - float3 emissive = mat.Albedo * mat.Emission; - float3 mapped = emissive / (emissive + 1.0); - return float4(pow(mapped, 1.0 / 2.2), 1.0); - } - - float3 N = normalize(input.Normal); - float3 worldPos = input.WorldPos; - float3 L = normalize(cb.LightPos - worldPos); - float3 V = normalize(cb.CameraPos - worldPos); - float3 H = normalize(L + V); - - float NdotL = max(dot(N, L), 0.0); - float NdotH = max(dot(N, H), 0.0); - float spec = pow(NdotH, 64.0); - - float dist = length(cb.LightPos - worldPos); - float atten = 1.0 / (1.0 + 0.000005 * dist * dist); - - float hemiFactor = N.y * 0.5 + 0.5; - float3 ambient = mat.Albedo * lerp(0.06, 0.15, hemiFactor); - float3 diffuse = mat.Albedo * cb.LightColor * NdotL * atten; - float3 specular = cb.LightColor * spec * atten * 0.1; - - float3 color = ambient + diffuse + specular; - - // ACES tonemapping - float3 a = color * (color * 2.51 + 0.03); - float3 b = color * (color * 2.43 + 0.59) + 0.14; - color = saturate(a / b); - - color = pow(color, 1.0 / 2.2); - return float4(color, 1.0); - } - """; - private readonly Buffer vertexBuffer; private readonly Buffer indexBuffer; private readonly Buffer materialBuffer; @@ -195,8 +75,8 @@ public RasterizationRenderer() inputLayout.Add(new() { Format = ElementFormat.Float4, Semantic = ElementSemantic.Position }); inputLayout.Add(new() { Format = ElementFormat.Float4, Semantic = ElementSemantic.Normal }); - using Shader vertexShader = App.Context.LoadShaderFromSource(ShaderSource, "VSMain", ShaderStageFlags.Vertex); - using Shader pixelShader = App.Context.LoadShaderFromSource(ShaderSource, "PSMain", ShaderStageFlags.Pixel); + using Shader vertexShader = App.Context.LoadShaderFromFile(ShaderPath("Rasterization.slang"), "VSMain", ShaderStageFlags.Vertex); + using Shader pixelShader = App.Context.LoadShaderFromFile(ShaderPath("Rasterization.slang"), "PSMain", ShaderStageFlags.Pixel); pipeline = App.Context.CreateGraphicsPipeline(new() { diff --git a/sources/Experiments/CornellBox/Renderers/Renderer.cs b/sources/Experiments/CornellBox/Renderers/Renderer.cs index 9558e16a..4dd9bd99 100644 --- a/sources/Experiments/CornellBox/Renderers/Renderer.cs +++ b/sources/Experiments/CornellBox/Renderers/Renderer.cs @@ -65,4 +65,9 @@ public virtual void Dispose() DepthStencil.Dispose(); Color.Dispose(); } + + protected static string ShaderPath(params string[] paths) + { + return Path.Combine([AppContext.BaseDirectory, "Assets", "Shaders", .. paths]); + } } From 84bb3240d456b6130be0991c27d0e49ad4d1584a Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Thu, 2 Apr 2026 20:32:40 +0800 Subject: [PATCH 06/24] Rename 'constants' buffer to 'raster' in shaders Replaced all usages of the ConstantBuffer variable 'constants' with 'raster' in both VSMain and PSMain. Updated all references to transformation matrices, light, and camera properties accordingly for improved naming consistency. --- .../Assets/Shaders/Rasterization.slang | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang b/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang index a54ad98a..25f4ecd0 100644 --- a/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang +++ b/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang @@ -100,17 +100,17 @@ struct PSInput nointerpolation uint MaterialID : TEXCOORD2; }; -ConstantBuffer constants; +ConstantBuffer raster; StructuredBuffer materials; PSInput VSMain(VSInput input) { - float4 worldPos = mul(float4(input.Position, 1.0), constants.Model); + float4 worldPos = mul(float4(input.Position, 1.0), raster.Model); PSInput output; - output.Position = mul(mul(worldPos, constants.View), constants.Projection); + output.Position = mul(mul(worldPos, raster.View), raster.Projection); output.WorldPos = worldPos.xyz; - output.Normal = normalize(mul(float4(input.Normal, 0.0), constants.Model).xyz); + output.Normal = normalize(mul(float4(input.Normal, 0.0), raster.Model).xyz); output.MaterialID = input.MaterialID; return output; @@ -130,21 +130,21 @@ float4 PSMain(PSInput input) : SV_TARGET float3 N = normalize(input.Normal); float3 worldPos = input.WorldPos; - float3 L = normalize(constants.LightPos - worldPos); - float3 V = normalize(constants.CameraPos - worldPos); + float3 L = normalize(raster.LightPos - worldPos); + float3 V = normalize(raster.CameraPos - worldPos); float3 H = normalize(L + V); float NdotL = max(dot(N, L), 0.0); float NdotH = max(dot(N, H), 0.0); float spec = pow(NdotH, 64.0); - float dist = length(constants.LightPos - worldPos); + float dist = length(raster.LightPos - worldPos); float atten = 1.0 / (1.0 + 0.000005 * dist * dist); float hemiFactor = N.y * 0.5 + 0.5; float3 ambient = mat.Albedo * lerp(0.06, 0.15, hemiFactor); - float3 diffuse = mat.Albedo * constants.LightColor * NdotL * atten; - float3 specular = constants.LightColor * spec * atten * 0.1; + float3 diffuse = mat.Albedo * raster.LightColor * NdotL * atten; + float3 specular = raster.LightColor * spec * atten * 0.1; float3 color = ambient + diffuse + specular; From 3d670fbda9d5e49cac651dfbc1c6825e87133b99 Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Thu, 2 Apr 2026 21:06:54 +0800 Subject: [PATCH 07/24] Refactor sampling, tonemapping, and shading in shaders - Optimize Fresnel-Schlick with multiplications instead of pow - Add buildONB for orthonormal basis; use in sampling functions - Improve GGX and hemisphere sampling with ONB and sincos - Increase shadow ray offsets to reduce self-intersection - Use geometric normal for direct lighting/shadow ray offset - Refactor ACES tonemapping and sRGB conversion into helpers - Apply consistent color processing in both pipelines - Clean up redundant code and clarify light calculations --- .../Assets/Shaders/PathTracing.slang | 68 ++++++++++++------- .../Assets/Shaders/Rasterization.slang | 25 ++++--- 2 files changed, 58 insertions(+), 35 deletions(-) diff --git a/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang b/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang index 4a8fb8a2..e66ffd06 100644 --- a/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang +++ b/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang @@ -115,7 +115,10 @@ float GeometrySmith(float NdotV, float NdotL, float roughness) float3 FresnelSchlick(float cosTheta, float3 F0) { - return F0 + (1.0 - F0) * pow(saturate(1.0 - cosTheta), 5.0); + float t = saturate(1.0 - cosTheta); + float t2 = t * t; + + return F0 + (1.0 - F0) * (t2 * t2 * t); } float3 evaluateBRDF(float3 N, float3 V, float3 L, Material mat) @@ -141,10 +144,16 @@ float3 evaluateBRDF(float3 N, float3 V, float3 L, Material mat) return diffuse + specular; } +void buildONB(float3 N, out float3 T, out float3 B) +{ + float3 helper = abs(N.x) > 0.99 ? float3(0.0, 1.0, 0.0) : float3(1.0, 0.0, 0.0); + T = normalize(cross(helper, N)); + B = cross(N, T); +} + float3 sampleGGXHalfVector(float3 N, float roughness, inout uint seed) { float a = roughness * roughness; - float r1 = randomFloat(seed); float r2 = randomFloat(seed); @@ -152,12 +161,13 @@ float3 sampleGGXHalfVector(float3 N, float roughness, inout uint seed) float cosTheta = sqrt((1.0 - r2) / (1.0 + (a * a - 1.0) * r2)); float sinTheta = sqrt(1.0 - cosTheta * cosTheta); - float3 w = N; - float3 helper = abs(w.x) > 0.99 ? float3(0.0, 1.0, 0.0) : float3(1.0, 0.0, 0.0); - float3 u = normalize(cross(helper, w)); - float3 v = cross(w, u); + float sinPhi, cosPhi; + sincos(phi, sinPhi, cosPhi); + + float3 T, B; + buildONB(N, T, B); - return normalize(u * cos(phi) * sinTheta + v * sin(phi) * sinTheta + w * cosTheta); + return normalize(T * (cosPhi * sinTheta) + B * (sinPhi * sinTheta) + N * cosTheta); } uint pcgHash(uint input) @@ -180,16 +190,17 @@ float3 cosineSampleHemisphere(float3 normal, inout uint seed) float r1 = randomFloat(seed); float r2 = randomFloat(seed); - float phi = 2.0 * 3.14159265 * r1; + float phi = 2.0 * PI * r1; float sinTheta = sqrt(r2); float cosTheta = sqrt(1.0 - r2); - float3 w = normal; - float3 helper = abs(w.x) > 0.99 ? float3(0.0, 1.0, 0.0) : float3(1.0, 0.0, 0.0); - float3 u = normalize(cross(helper, w)); - float3 v = cross(w, u); + float sinPhi, cosPhi; + sincos(phi, sinPhi, cosPhi); + + float3 T, B; + buildONB(normal, T, B); - return normalize(u * cos(phi) * sinTheta + v * sin(phi) * sinTheta + w * cosTheta); + return normalize(T * (cosPhi * sinTheta) + B * (sinPhi * sinTheta) + normal * cosTheta); } bool traceShadowRay(float3 origin, float3 direction, float maxDist) @@ -197,8 +208,8 @@ bool traceShadowRay(float3 origin, float3 direction, float maxDist) RayDesc shadowRay; shadowRay.Origin = origin; shadowRay.Direction = direction; - shadowRay.TMin = 0.001; - shadowRay.TMax = maxDist - 0.001; + shadowRay.TMin = 0.01; + shadowRay.TMax = maxDist - 0.01; RayQuery shadowQuery; shadowQuery.TraceRayInline(scene, RAY_FLAG_NONE, 0xFF, shadowRay); @@ -210,7 +221,7 @@ bool traceShadowRay(float3 origin, float3 direction, float maxDist) return shadowQuery.CommittedStatus() != COMMITTED_NOTHING; } -float3 sampleLightDirect(float3 hitPos, float3 hitNormal, float3 V, Material mat, inout uint rng) +float3 sampleLightDirect(float3 hitPos, float3 hitNormal, float3 geoNormal, float3 V, Material mat, inout uint rng) { float u = randomFloat(rng); float v = randomFloat(rng); @@ -233,7 +244,8 @@ float3 sampleLightDirect(float3 hitPos, float3 hitNormal, float3 V, Material mat return float3(0.0, 0.0, 0.0); } - if (traceShadowRay(hitPos + hitNormal * 0.001, L, dist)) + float3 shadowOrigin = dot(geoNormal, L) > 0.0 ? hitPos + geoNormal * 0.05 : hitPos - geoNormal * 0.05; + if (traceShadowRay(shadowOrigin, L, dist)) { return float3(0.0, 0.0, 0.0); } @@ -292,6 +304,7 @@ float3 tracePath(float3 origin, float3 direction, inout uint rng) float3 baryWeights = float3(1.0 - bary.x - bary.y, bary.x, bary.y); float3 normal = normalize(v0.Normal * baryWeights.x + v1.Normal * baryWeights.y + v2.Normal * baryWeights.z); + float3 geoNormal = normal; if (dot(normal, direction) > 0.0) { normal = -normal; @@ -311,7 +324,7 @@ float3 tracePath(float3 origin, float3 direction, inout uint rng) float3 V = -direction; - radiance += throughput * sampleLightDirect(hitPos, normal, V, mat, rng); + radiance += throughput * sampleLightDirect(hitPos, normal, geoNormal, V, mat, rng); float3 F0 = lerp(float3(0.04, 0.04, 0.04), mat.Albedo, mat.Metallic); float roughness = max(mat.Roughness, 0.04); @@ -356,7 +369,7 @@ float3 tracePath(float3 origin, float3 direction, inout uint rng) throughput *= kD * mat.Albedo / (1.0 - specProb + 0.0001); } - origin = hitPos + normal * 0.001; + origin = hitPos + normal * 0.05; direction = newDir; if (bounce >= 2) @@ -375,6 +388,16 @@ float3 tracePath(float3 origin, float3 direction, inout uint rng) return radiance; } +float3 ACESFilm(float3 x) +{ + return saturate((x * (x * 2.51 + 0.03)) / (x * (x * 2.43 + 0.59) + 0.14)); +} + +float3 toSRGB(float3 linear) +{ + return pow(linear, 1.0 / 2.2); +} + [numthreads(16, 16, 1)] void CSMain(uint3 dispatchThreadID: SV_DispatchThreadID) { @@ -416,11 +439,6 @@ void CSMain(uint3 dispatchThreadID: SV_DispatchThreadID) float3 avg = accumulated.rgb / float(camera.FrameCount + 1); - // ACES tonemapping - float3 a = avg * (avg * 2.51 + 0.03); - float3 b = avg * (avg * 2.43 + 0.59) + 0.14; - avg = saturate(a / b); - - avg = pow(avg, 1.0 / 2.2); + avg = toSRGB(ACESFilm(avg)); outputTexture[pixel] = float4(avg, 1.0); } diff --git a/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang b/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang index 25f4ecd0..eaa83666 100644 --- a/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang +++ b/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang @@ -103,6 +103,16 @@ struct PSInput ConstantBuffer raster; StructuredBuffer materials; +float3 ACESFilm(float3 x) +{ + return saturate((x * (x * 2.51 + 0.03)) / (x * (x * 2.43 + 0.59) + 0.14)); +} + +float3 toSRGB(float3 linear) +{ + return pow(linear, 1.0 / 2.2); +} + PSInput VSMain(VSInput input) { float4 worldPos = mul(float4(input.Position, 1.0), raster.Model); @@ -125,20 +135,20 @@ float4 PSMain(PSInput input) : SV_TARGET float3 emissive = mat.Albedo * mat.Emission; float3 mapped = emissive / (emissive + 1.0); - return float4(pow(mapped, 1.0 / 2.2), 1.0); + return float4(toSRGB(mapped), 1.0); } float3 N = normalize(input.Normal); float3 worldPos = input.WorldPos; - float3 L = normalize(raster.LightPos - worldPos); + float3 toLight = raster.LightPos - worldPos; + float dist = length(toLight); + float3 L = toLight / dist; float3 V = normalize(raster.CameraPos - worldPos); float3 H = normalize(L + V); float NdotL = max(dot(N, L), 0.0); float NdotH = max(dot(N, H), 0.0); float spec = pow(NdotH, 64.0); - - float dist = length(raster.LightPos - worldPos); float atten = 1.0 / (1.0 + 0.000005 * dist * dist); float hemiFactor = N.y * 0.5 + 0.5; @@ -148,12 +158,7 @@ float4 PSMain(PSInput input) : SV_TARGET float3 color = ambient + diffuse + specular; - // ACES tonemapping - float3 a = color * (color * 2.51 + 0.03); - float3 b = color * (color * 2.43 + 0.59) + 0.14; - color = saturate(a / b); - - color = pow(color, 1.0 / 2.2); + color = toSRGB(ACESFilm(color)); return float4(color, 1.0); } From 5cfe962a27125d28245c8581d457e9d2249a7686 Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Thu, 2 Apr 2026 21:33:59 +0800 Subject: [PATCH 08/24] Refactor BRDF calculations and shadow ray origins for improved accuracy --- .../Assets/Shaders/PathTracing.slang | 60 +++++++------------ 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang b/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang index e66ffd06..9816659f 100644 --- a/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang +++ b/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang @@ -115,10 +115,7 @@ float GeometrySmith(float NdotV, float NdotL, float roughness) float3 FresnelSchlick(float cosTheta, float3 F0) { - float t = saturate(1.0 - cosTheta); - float t2 = t * t; - - return F0 + (1.0 - F0) * (t2 * t2 * t); + return F0 + (1.0 - F0) * pow(saturate(1.0 - cosTheta), 5.0); } float3 evaluateBRDF(float3 N, float3 V, float3 L, Material mat) @@ -144,16 +141,10 @@ float3 evaluateBRDF(float3 N, float3 V, float3 L, Material mat) return diffuse + specular; } -void buildONB(float3 N, out float3 T, out float3 B) -{ - float3 helper = abs(N.x) > 0.99 ? float3(0.0, 1.0, 0.0) : float3(1.0, 0.0, 0.0); - T = normalize(cross(helper, N)); - B = cross(N, T); -} - float3 sampleGGXHalfVector(float3 N, float roughness, inout uint seed) { float a = roughness * roughness; + float r1 = randomFloat(seed); float r2 = randomFloat(seed); @@ -161,13 +152,12 @@ float3 sampleGGXHalfVector(float3 N, float roughness, inout uint seed) float cosTheta = sqrt((1.0 - r2) / (1.0 + (a * a - 1.0) * r2)); float sinTheta = sqrt(1.0 - cosTheta * cosTheta); - float sinPhi, cosPhi; - sincos(phi, sinPhi, cosPhi); - - float3 T, B; - buildONB(N, T, B); + float3 w = N; + float3 helper = abs(w.x) > 0.99 ? float3(0.0, 1.0, 0.0) : float3(1.0, 0.0, 0.0); + float3 u = normalize(cross(helper, w)); + float3 v = cross(w, u); - return normalize(T * (cosPhi * sinTheta) + B * (sinPhi * sinTheta) + N * cosTheta); + return normalize(u * cos(phi) * sinTheta + v * sin(phi) * sinTheta + w * cosTheta); } uint pcgHash(uint input) @@ -190,17 +180,16 @@ float3 cosineSampleHemisphere(float3 normal, inout uint seed) float r1 = randomFloat(seed); float r2 = randomFloat(seed); - float phi = 2.0 * PI * r1; + float phi = 2.0 * 3.14159265 * r1; float sinTheta = sqrt(r2); float cosTheta = sqrt(1.0 - r2); - float sinPhi, cosPhi; - sincos(phi, sinPhi, cosPhi); - - float3 T, B; - buildONB(normal, T, B); + float3 w = normal; + float3 helper = abs(w.x) > 0.99 ? float3(0.0, 1.0, 0.0) : float3(1.0, 0.0, 0.0); + float3 u = normalize(cross(helper, w)); + float3 v = cross(w, u); - return normalize(T * (cosPhi * sinTheta) + B * (sinPhi * sinTheta) + normal * cosTheta); + return normalize(u * cos(phi) * sinTheta + v * sin(phi) * sinTheta + w * cosTheta); } bool traceShadowRay(float3 origin, float3 direction, float maxDist) @@ -208,7 +197,7 @@ bool traceShadowRay(float3 origin, float3 direction, float maxDist) RayDesc shadowRay; shadowRay.Origin = origin; shadowRay.Direction = direction; - shadowRay.TMin = 0.01; + shadowRay.TMin = 0.001; shadowRay.TMax = maxDist - 0.01; RayQuery shadowQuery; @@ -244,7 +233,7 @@ float3 sampleLightDirect(float3 hitPos, float3 hitNormal, float3 geoNormal, floa return float3(0.0, 0.0, 0.0); } - float3 shadowOrigin = dot(geoNormal, L) > 0.0 ? hitPos + geoNormal * 0.05 : hitPos - geoNormal * 0.05; + float3 shadowOrigin = dot(geoNormal, L) > 0.0 ? hitPos + geoNormal * 0.001 : hitPos - geoNormal * 0.001; if (traceShadowRay(shadowOrigin, L, dist)) { return float3(0.0, 0.0, 0.0); @@ -369,7 +358,7 @@ float3 tracePath(float3 origin, float3 direction, inout uint rng) throughput *= kD * mat.Albedo / (1.0 - specProb + 0.0001); } - origin = hitPos + normal * 0.05; + origin = hitPos + normal * 0.001; direction = newDir; if (bounce >= 2) @@ -388,16 +377,6 @@ float3 tracePath(float3 origin, float3 direction, inout uint rng) return radiance; } -float3 ACESFilm(float3 x) -{ - return saturate((x * (x * 2.51 + 0.03)) / (x * (x * 2.43 + 0.59) + 0.14)); -} - -float3 toSRGB(float3 linear) -{ - return pow(linear, 1.0 / 2.2); -} - [numthreads(16, 16, 1)] void CSMain(uint3 dispatchThreadID: SV_DispatchThreadID) { @@ -439,6 +418,11 @@ void CSMain(uint3 dispatchThreadID: SV_DispatchThreadID) float3 avg = accumulated.rgb / float(camera.FrameCount + 1); - avg = toSRGB(ACESFilm(avg)); + // ACES tonemapping + float3 a = avg * (avg * 2.51 + 0.03); + float3 b = avg * (avg * 2.43 + 0.59) + 0.14; + avg = saturate(a / b); + + avg = pow(avg, 1.0 / 2.2); outputTexture[pixel] = float4(avg, 1.0); } From f771552b75d9e906c88ba0b837782c18144d6e91 Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Thu, 2 Apr 2026 22:10:07 +0800 Subject: [PATCH 09/24] Enhance BRDF sampling and light sampling with power heuristic and Halton sequence for improved rendering accuracy --- .../Assets/Shaders/PathTracing.slang | 69 +++++++++++++++++-- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang b/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang index 9816659f..ab11d69a 100644 --- a/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang +++ b/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang @@ -141,6 +141,36 @@ float3 evaluateBRDF(float3 N, float3 V, float3 L, Material mat) return diffuse + specular; } +float powerHeuristic(float pdfA, float pdfB) +{ + float a2 = pdfA * pdfA; + + return a2 / (a2 + pdfB * pdfB + 0.0001); +} + +float computeBrdfPdf(float3 N, float3 V, float3 L, float roughness, float specProb) +{ + float NdotL = max(dot(N, L), 0.0); + if (NdotL <= 0.0) + { + return 0.0; + } + + float3 H = normalize(V + L); + float NdotH = max(dot(N, H), 0.0); + float HdotV = max(dot(H, V), 0.0); + + float a = roughness * roughness; + float a2 = a * a; + float denom = NdotH * NdotH * (a2 - 1.0) + 1.0; + float D = a2 / (PI * denom * denom); + + float pdfSpec = D * NdotH / (4.0 * HdotV + 0.0001); + float pdfDiff = NdotL / PI; + + return specProb * pdfSpec + (1.0 - specProb) * pdfDiff; +} + float3 sampleGGXHalfVector(float3 N, float roughness, inout uint seed) { float a = roughness * roughness; @@ -210,7 +240,8 @@ bool traceShadowRay(float3 origin, float3 direction, float maxDist) return shadowQuery.CommittedStatus() != COMMITTED_NOTHING; } -float3 sampleLightDirect(float3 hitPos, float3 hitNormal, float3 geoNormal, float3 V, Material mat, inout uint rng) +float3 sampleLightDirect(float3 hitPos, float3 hitNormal, float3 geoNormal, float3 V, Material mat, float roughness, + float specProb, inout uint rng) { float u = randomFloat(rng); float v = randomFloat(rng); @@ -242,11 +273,13 @@ float3 sampleLightDirect(float3 hitPos, float3 hitNormal, float3 geoNormal, floa Material lightMat = materials[3]; float3 lightEmission = lightMat.Albedo * lightMat.Emission; - float pdf = 1.0 / LightArea; + float pdfLight = (dist * dist) / (lightCosine * LightArea); + float pdfBrdf = computeBrdfPdf(hitNormal, V, L, roughness, specProb); + float misWeight = powerHeuristic(pdfLight, pdfBrdf); + float3 brdf = evaluateBRDF(hitNormal, V, L, mat); - float geometryTerm = NdotL * lightCosine / (dist * dist); - return lightEmission * brdf * geometryTerm / pdf; + return lightEmission * brdf * NdotL * misWeight / pdfLight; } float3 tracePath(float3 origin, float3 direction, inout uint rng) @@ -313,8 +346,6 @@ float3 tracePath(float3 origin, float3 direction, inout uint rng) float3 V = -direction; - radiance += throughput * sampleLightDirect(hitPos, normal, geoNormal, V, mat, rng); - float3 F0 = lerp(float3(0.04, 0.04, 0.04), mat.Albedo, mat.Metallic); float roughness = max(mat.Roughness, 0.04); float specWeight = max(F0.r, max(F0.g, F0.b)); @@ -322,6 +353,8 @@ float3 tracePath(float3 origin, float3 direction, inout uint rng) float total = specWeight + diffWeight; float specProb = specWeight / total; + radiance += throughput * sampleLightDirect(hitPos, normal, geoNormal, V, mat, roughness, specProb, rng); + float3 newDir; float NdotV = max(dot(normal, V), 0.001); @@ -377,6 +410,23 @@ float3 tracePath(float3 origin, float3 direction, inout uint rng) return radiance; } +float halton(uint index, uint base) +{ + float result = 0.0; + float f = 1.0; + + uint i = index; + + while (i > 0) + { + f /= float(base); + result += f * float(i % base); + i /= base; + } + + return result; +} + [numthreads(16, 16, 1)] void CSMain(uint3 dispatchThreadID: SV_DispatchThreadID) { @@ -389,7 +439,12 @@ void CSMain(uint3 dispatchThreadID: SV_DispatchThreadID) uint rng = pcgHash(pixel.x + pixel.y * camera.Width + camera.FrameCount * camera.Width * camera.Height); - float2 jitter = float2(randomFloat(rng), randomFloat(rng)); + uint sampleIndex = camera.FrameCount + 1; + float hx = halton(sampleIndex, 2); + float hy = halton(sampleIndex, 3); + float ox = randomFloat(rng) * 0.5 - 0.25; + float oy = randomFloat(rng) * 0.5 - 0.25; + float2 jitter = frac(float2(hx + ox, hy + oy)); float2 uv = (float2(pixel) + jitter) / float2(camera.Width, camera.Height); float2 ndc = uv * 2.0 - 1.0; ndc.y = -ndc.y; From 07a67763d6a294a659f41faee549765d9fdb60cf Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Thu, 2 Apr 2026 22:20:26 +0800 Subject: [PATCH 10/24] Refactor: use PI constant and unify RNG variable names Replaced hardcoded 3.14159265 with PI constant in cosineSampleHemisphere for clarity. Renamed random variables u/v to r1/r2 in sampleLightDirect to match naming conventions and improve code consistency. --- .../CornellBox/Assets/Shaders/PathTracing.slang | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang b/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang index ab11d69a..94156c10 100644 --- a/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang +++ b/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang @@ -210,7 +210,7 @@ float3 cosineSampleHemisphere(float3 normal, inout uint seed) float r1 = randomFloat(seed); float r2 = randomFloat(seed); - float phi = 2.0 * 3.14159265 * r1; + float phi = 2.0 * PI * r1; float sinTheta = sqrt(r2); float cosTheta = sqrt(1.0 - r2); @@ -243,10 +243,10 @@ bool traceShadowRay(float3 origin, float3 direction, float maxDist) float3 sampleLightDirect(float3 hitPos, float3 hitNormal, float3 geoNormal, float3 V, Material mat, float roughness, float specProb, inout uint rng) { - float u = randomFloat(rng); - float v = randomFloat(rng); + float r1 = randomFloat(rng); + float r2 = randomFloat(rng); - float3 lightPoint = float3(lerp(LightMin.x, LightMax.x, u), LightMin.y, lerp(LightMin.z, LightMax.z, v)); + float3 lightPoint = float3(lerp(LightMin.x, LightMax.x, r1), LightMin.y, lerp(LightMin.z, LightMax.z, r2)); float3 toLight = lightPoint - hitPos; float dist = length(toLight); From 751d11a66c34fb166036580b6fd9609beab8ffb9 Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Thu, 2 Apr 2026 22:33:26 +0800 Subject: [PATCH 11/24] Update light position and add PBR shading to rasterizer Lower light source to y=547.0 for consistency across all files. Replace Blinn-Phong shading in Rasterization.slang with a physically-based rendering (PBR) model using GGX, Schlick geometry, and Fresnel-Schlick functions. Update light attenuation to use inverse-square law, apply ACES filmic tone mapping before sRGB conversion, and refine ambient term for more realistic results. --- .../Assets/Shaders/PathTracing.slang | 4 +- .../Assets/Shaders/Rasterization.slang | 55 ++++++++++++++++--- .../CornellBox/Helpers/CornellBoxGeometry.cs | 8 +-- .../Renderers/RasterizationRenderer.cs | 2 +- 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang b/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang index 94156c10..2db95ca9 100644 --- a/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang +++ b/sources/Experiments/CornellBox/Assets/Shaders/PathTracing.slang @@ -1,5 +1,5 @@ -static const float3 LightMin = float3(213.0, 548.6, 227.0); -static const float3 LightMax = float3(343.0, 548.6, 332.0); +static const float3 LightMin = float3(213.0, 547.0, 227.0); +static const float3 LightMax = float3(343.0, 547.0, 332.0); static const float LightArea = (343.0 - 213.0) * (332.0 - 227.0); static const float3 LightNormal = float3(0.0, -1.0, 0.0); static const float PI = 3.14159265; diff --git a/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang b/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang index eaa83666..17135159 100644 --- a/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang +++ b/sources/Experiments/CornellBox/Assets/Shaders/Rasterization.slang @@ -103,6 +103,8 @@ struct PSInput ConstantBuffer raster; StructuredBuffer materials; +static const float PI = 3.14159265; + float3 ACESFilm(float3 x) { return saturate((x * (x * 2.51 + 0.03)) / (x * (x * 2.43 + 0.59) + 0.14)); @@ -113,6 +115,31 @@ float3 toSRGB(float3 linear) return pow(linear, 1.0 / 2.2); } +float DistributionGGX(float NdotH, float roughness) +{ + float a = roughness * roughness; + float a2 = a * a; + float d = NdotH * NdotH * (a2 - 1.0) + 1.0; + return a2 / (PI * d * d); +} + +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float r = roughness + 1.0; + float k = (r * r) / 8.0; + return NdotV / (NdotV * (1.0 - k) + k); +} + +float GeometrySmith(float NdotV, float NdotL, float roughness) +{ + return GeometrySchlickGGX(NdotV, roughness) * GeometrySchlickGGX(NdotL, roughness); +} + +float3 FresnelSchlick(float cosTheta, float3 F0) +{ + return F0 + (1.0 - F0) * pow(saturate(1.0 - cosTheta), 5.0); +} + PSInput VSMain(VSInput input) { float4 worldPos = mul(float4(input.Position, 1.0), raster.Model); @@ -140,23 +167,37 @@ float4 PSMain(PSInput input) : SV_TARGET float3 N = normalize(input.Normal); float3 worldPos = input.WorldPos; + float3 V = normalize(raster.CameraPos - worldPos); float3 toLight = raster.LightPos - worldPos; float dist = length(toLight); float3 L = toLight / dist; - float3 V = normalize(raster.CameraPos - worldPos); float3 H = normalize(L + V); float NdotL = max(dot(N, L), 0.0); float NdotH = max(dot(N, H), 0.0); - float spec = pow(NdotH, 64.0); - float atten = 1.0 / (1.0 + 0.000005 * dist * dist); + float NdotV = max(dot(N, V), 0.001); + float HdotV = max(dot(H, V), 0.0); + + float roughness = max(mat.Roughness, 0.04); + float metallic = mat.Metallic; + float3 F0 = lerp(float3(0.04), mat.Albedo, metallic); + + float D = DistributionGGX(NdotH, roughness); + float G = GeometrySmith(NdotV, NdotL, roughness); + float3 F = FresnelSchlick(HdotV, F0); + + float3 specular = (D * G * F) / (4.0 * NdotV * NdotL + 0.0001); + + float3 kD = (1.0 - F) * (1.0 - metallic); + float3 diffuse = kD * mat.Albedo / PI; + + float atten = 1.0 / (dist * dist) * 80000.0; + float3 Lo = (diffuse + specular) * raster.LightColor * NdotL * atten; float hemiFactor = N.y * 0.5 + 0.5; - float3 ambient = mat.Albedo * lerp(0.06, 0.15, hemiFactor); - float3 diffuse = mat.Albedo * raster.LightColor * NdotL * atten; - float3 specular = raster.LightColor * spec * atten * 0.1; + float3 ambient = mat.Albedo * lerp(0.03, 0.10, hemiFactor) * (1.0 - metallic * 0.7); - float3 color = ambient + diffuse + specular; + float3 color = ambient + Lo; color = toSRGB(ACESFilm(color)); diff --git a/sources/Experiments/CornellBox/Helpers/CornellBoxGeometry.cs b/sources/Experiments/CornellBox/Helpers/CornellBoxGeometry.cs index 2291e636..0d378131 100644 --- a/sources/Experiments/CornellBox/Helpers/CornellBoxGeometry.cs +++ b/sources/Experiments/CornellBox/Helpers/CornellBoxGeometry.cs @@ -72,10 +72,10 @@ public static void Create(out Vertex[] vertices, out uint[] indices, out Materia // 15: Light AddQuad(verticesList, indicesList, - new(343.0f, 548.6f, 227.0f), - new(343.0f, 548.6f, 332.0f), - new(213.0f, 548.6f, 332.0f), - new(213.0f, 548.6f, 227.0f), + new(343.0f, 547.0f, 227.0f), + new(343.0f, 547.0f, 332.0f), + new(213.0f, 547.0f, 332.0f), + new(213.0f, 547.0f, 227.0f), 3); vertices = [.. verticesList]; diff --git a/sources/Experiments/CornellBox/Renderers/RasterizationRenderer.cs b/sources/Experiments/CornellBox/Renderers/RasterizationRenderer.cs index dc25e5dd..ce9a8cc4 100644 --- a/sources/Experiments/CornellBox/Renderers/RasterizationRenderer.cs +++ b/sources/Experiments/CornellBox/Renderers/RasterizationRenderer.cs @@ -102,7 +102,7 @@ public override void Update(CameraHandler camera) Model = Matrix4x4.Identity, View = camera.View, Projection = camera.Projection, - LightPos = new(278.0f, 548.0f, 280.0f), + LightPos = new(278.0f, 547.0f, 280.0f), LightColor = new(2.0f, 1.8f, 1.4f), CameraPos = camera.Position }], 0); From cf0510d84763af028d680de714bbf05245c99680 Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Fri, 3 Apr 2026 14:43:49 +0800 Subject: [PATCH 12/24] Add Fragment stage to mesh shading argument table setup Previously, the argument table for mesh shading pipelines was set only for the Object and Mesh stages. This change adds the Fragment stage, ensuring that resources are correctly bound and accessible in the fragment shader stage as well. --- sources/Zenith.NET.Metal/MTLCommandEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/Zenith.NET.Metal/MTLCommandEncoder.cs b/sources/Zenith.NET.Metal/MTLCommandEncoder.cs index d3e86628..e0cc86ee 100644 --- a/sources/Zenith.NET.Metal/MTLCommandEncoder.cs +++ b/sources/Zenith.NET.Metal/MTLCommandEncoder.cs @@ -253,7 +253,7 @@ public void Bind() if (currentResourceTable is MTLResourceTable resourceTable) { - Render?.SetArgumentTable(resourceTable.ArgumentTable, MTLRenderStages.Object | MTLRenderStages.Mesh); + Render?.SetArgumentTable(resourceTable.ArgumentTable, MTLRenderStages.Object | MTLRenderStages.Mesh | MTLRenderStages.Fragment); } } break; From 12ef84d0d5033eddb705ebf9f4b2b5e8b0cde9e9 Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Sat, 4 Apr 2026 13:01:09 +0800 Subject: [PATCH 13/24] Add explicit Matrix4x4-to-3x4 conversions for RTAS Introduce helper methods to convert Matrix4x4 to platform-specific 3x4 matrix formats for DirectX 12, Metal, and Vulkan acceleration structures. Replace previous unsafe casts with these explicit conversions to improve clarity and ensure correct memory layout. Update relevant files to use System.Numerics for Matrix4x4 support. --- .../DXBottomLevelAccelerationStructure.cs | 2 +- sources/Zenith.NET.DirectX12/DXFormats.cs | 16 +++++++++++++++- .../DXTopLevelAccelerationStructure.cs | 3 ++- .../MTLBottomLevelAccelerationStructure.cs | 2 +- sources/Zenith.NET.Metal/MTLFormats.cs | 15 ++++++++++++++- .../MTLTopLevelAccelerationStructure.cs | 2 +- .../VKBottomLevelAccelerationStructure.cs | 2 +- sources/Zenith.NET.Vulkan/VKFormats.cs | 15 ++++++++++++++- .../VKTopLevelAccelerationStructure.cs | 2 +- 9 files changed, 50 insertions(+), 9 deletions(-) diff --git a/sources/Zenith.NET.DirectX12/DXBottomLevelAccelerationStructure.cs b/sources/Zenith.NET.DirectX12/DXBottomLevelAccelerationStructure.cs index a53d0575..cdedcc74 100644 --- a/sources/Zenith.NET.DirectX12/DXBottomLevelAccelerationStructure.cs +++ b/sources/Zenith.NET.DirectX12/DXBottomLevelAccelerationStructure.cs @@ -21,7 +21,7 @@ public DXBottomLevelAccelerationStructure(DXGraphicsContext context, BottomLevel MappedMemory mappedMemory = TransformBuffer.Map(); - desc.Geometries.Select(static item => *(Matrix3X4*)&item.Triangles.Transform).ToArray().CopyTo(new Span>((Matrix3X4*)mappedMemory.Pointer, (int)geometryCount)); + desc.Geometries.Select(static item => DXFormats.DirectX12(item.Triangles.Transform)).ToArray().CopyTo(new Span>((Matrix3X4*)mappedMemory.Pointer, (int)geometryCount)); TransformBuffer.Unmap(); diff --git a/sources/Zenith.NET.DirectX12/DXFormats.cs b/sources/Zenith.NET.DirectX12/DXFormats.cs index 13a0c234..b36842d0 100644 --- a/sources/Zenith.NET.DirectX12/DXFormats.cs +++ b/sources/Zenith.NET.DirectX12/DXFormats.cs @@ -1,6 +1,8 @@ -using Silk.NET.Core.Native; +using System.Numerics; +using Silk.NET.Core.Native; using Silk.NET.Direct3D12; using Silk.NET.DXGI; +using Silk.NET.Maths; namespace Zenith.NET.DirectX12; @@ -533,6 +535,18 @@ QueryType.Occlusion or ); } + public static unsafe Matrix3X4 DirectX12(Matrix4x4 matrix4x4) + { + Matrix3X4 result; + + float* pResult = (float*)&result; + pResult[0] = matrix4x4.M11; pResult[1] = matrix4x4.M21; pResult[2] = matrix4x4.M31; pResult[3] = matrix4x4.M41; + pResult[4] = matrix4x4.M12; pResult[5] = matrix4x4.M22; pResult[6] = matrix4x4.M32; pResult[7] = matrix4x4.M42; + pResult[8] = matrix4x4.M13; pResult[9] = matrix4x4.M23; pResult[10] = matrix4x4.M33; pResult[11] = matrix4x4.M43; + + return result; + } + public static RaytracingGeometryType DirectX12(RayTracingGeometryType rayTracingGeometryType) { return rayTracingGeometryType switch diff --git a/sources/Zenith.NET.DirectX12/DXTopLevelAccelerationStructure.cs b/sources/Zenith.NET.DirectX12/DXTopLevelAccelerationStructure.cs index ba557aad..1c21312e 100644 --- a/sources/Zenith.NET.DirectX12/DXTopLevelAccelerationStructure.cs +++ b/sources/Zenith.NET.DirectX12/DXTopLevelAccelerationStructure.cs @@ -1,4 +1,5 @@ using Silk.NET.Direct3D12; +using Silk.NET.Maths; namespace Zenith.NET.DirectX12; @@ -136,7 +137,7 @@ private void FillInstanceBuffer(TopLevelAccelerationStructureDesc desc, out Buil AccelerationStructure = instance.AccelerationStructure.DirectX12().AccelerationStructureBuffer.GPUVirtualAddress }; - new ReadOnlySpan(&instance.Transform, 12).CopyTo(new(instances[i].Transform, 12)); + *(Matrix3X4*)instances[i].Transform = DXFormats.DirectX12(instance.Transform); } InstanceBuffer.Unmap(); diff --git a/sources/Zenith.NET.Metal/MTLBottomLevelAccelerationStructure.cs b/sources/Zenith.NET.Metal/MTLBottomLevelAccelerationStructure.cs index 18b2ff2e..03322d0f 100644 --- a/sources/Zenith.NET.Metal/MTLBottomLevelAccelerationStructure.cs +++ b/sources/Zenith.NET.Metal/MTLBottomLevelAccelerationStructure.cs @@ -19,7 +19,7 @@ public unsafe MTLBottomLevelAccelerationStructure(MTLGraphicsContext context, Bo MappedMemory mappedMemory = TransformBuffer.Map(); - desc.Geometries.Select(static item => *(MTLPackedFloat4x3*)&item.Triangles.Transform).ToArray().CopyTo(new Span((MTLPackedFloat4x3*)mappedMemory.Pointer, (int)geometryCount)); + desc.Geometries.Select(static item => MTLFormats.Metal(item.Triangles.Transform)).ToArray().CopyTo(new Span((MTLPackedFloat4x3*)mappedMemory.Pointer, (int)geometryCount)); TransformBuffer.Unmap(); diff --git a/sources/Zenith.NET.Metal/MTLFormats.cs b/sources/Zenith.NET.Metal/MTLFormats.cs index aff8fa59..1b0951d9 100644 --- a/sources/Zenith.NET.Metal/MTLFormats.cs +++ b/sources/Zenith.NET.Metal/MTLFormats.cs @@ -1,4 +1,5 @@ -using Metal.NET; +using System.Numerics; +using Metal.NET; namespace Zenith.NET.Metal; @@ -452,6 +453,18 @@ public static MTLVisibilityResultMode Metal(QueryType queryType) }; } + public static unsafe MTLPackedFloat4x3 Metal(Matrix4x4 matrix4x4) + { + MTLPackedFloat4x3 result; + + float* pResult = (float*)&result; + pResult[0] = matrix4x4.M11; pResult[1] = matrix4x4.M21; pResult[2] = matrix4x4.M31; pResult[3] = matrix4x4.M41; + pResult[4] = matrix4x4.M12; pResult[5] = matrix4x4.M22; pResult[6] = matrix4x4.M32; pResult[7] = matrix4x4.M42; + pResult[8] = matrix4x4.M13; pResult[9] = matrix4x4.M23; pResult[10] = matrix4x4.M33; pResult[11] = matrix4x4.M43; + + return result; + } + public static MTLAccelerationStructureUsage Metal(AccelerationStructureBuildFlags accelerationStructureBuildFlags) { MTLAccelerationStructureUsage result = MTLAccelerationStructureUsage.None; diff --git a/sources/Zenith.NET.Metal/MTLTopLevelAccelerationStructure.cs b/sources/Zenith.NET.Metal/MTLTopLevelAccelerationStructure.cs index 4707a81a..04d703dd 100644 --- a/sources/Zenith.NET.Metal/MTLTopLevelAccelerationStructure.cs +++ b/sources/Zenith.NET.Metal/MTLTopLevelAccelerationStructure.cs @@ -95,7 +95,7 @@ private void FillInstanceBuffer(TopLevelAccelerationStructureDesc desc) instances[i] = new() { - TransformationMatrix = *(MTLPackedFloat4x3*)&instance.Transform, + TransformationMatrix = MTLFormats.Metal(instance.Transform), Options = MTLFormats.Metal(instance.Flags), Mask = instance.Mask, UserID = instance.ID, diff --git a/sources/Zenith.NET.Vulkan/VKBottomLevelAccelerationStructure.cs b/sources/Zenith.NET.Vulkan/VKBottomLevelAccelerationStructure.cs index 468a79a1..b29a9464 100644 --- a/sources/Zenith.NET.Vulkan/VKBottomLevelAccelerationStructure.cs +++ b/sources/Zenith.NET.Vulkan/VKBottomLevelAccelerationStructure.cs @@ -23,7 +23,7 @@ public VKBottomLevelAccelerationStructure(VKGraphicsContext context, BottomLevel MappedMemory mappedMemory = TransformBuffer.Map(); - desc.Geometries.Select(static item => *(TransformMatrixKHR*)&item.Triangles.Transform).ToArray().CopyTo(new Span((TransformMatrixKHR*)mappedMemory.Pointer, (int)geometryCount)); + desc.Geometries.Select(static item => VKFormats.Vulkan(item.Triangles.Transform)).ToArray().CopyTo(new Span((TransformMatrixKHR*)mappedMemory.Pointer, (int)geometryCount)); TransformBuffer.Unmap(); diff --git a/sources/Zenith.NET.Vulkan/VKFormats.cs b/sources/Zenith.NET.Vulkan/VKFormats.cs index 3eac604e..b5fae0ee 100644 --- a/sources/Zenith.NET.Vulkan/VKFormats.cs +++ b/sources/Zenith.NET.Vulkan/VKFormats.cs @@ -1,4 +1,5 @@ -using Silk.NET.Vulkan; +using System.Numerics; +using Silk.NET.Vulkan; namespace Zenith.NET.Vulkan; @@ -613,6 +614,18 @@ public static IndexType Vulkan(IndexFormat indexFormat) }; } + public static unsafe TransformMatrixKHR Vulkan(Matrix4x4 matrix4x4) + { + TransformMatrixKHR result; + + float* pResult = (float*)&result; + pResult[0] = matrix4x4.M11; pResult[1] = matrix4x4.M21; pResult[2] = matrix4x4.M31; pResult[3] = matrix4x4.M41; + pResult[4] = matrix4x4.M12; pResult[5] = matrix4x4.M22; pResult[6] = matrix4x4.M32; pResult[7] = matrix4x4.M42; + pResult[8] = matrix4x4.M13; pResult[9] = matrix4x4.M23; pResult[10] = matrix4x4.M33; pResult[11] = matrix4x4.M43; + + return result; + } + public static GeometryTypeKHR Vulkan(RayTracingGeometryType rayTracingGeometryType) { return rayTracingGeometryType switch diff --git a/sources/Zenith.NET.Vulkan/VKTopLevelAccelerationStructure.cs b/sources/Zenith.NET.Vulkan/VKTopLevelAccelerationStructure.cs index 04994252..4139bbd1 100644 --- a/sources/Zenith.NET.Vulkan/VKTopLevelAccelerationStructure.cs +++ b/sources/Zenith.NET.Vulkan/VKTopLevelAccelerationStructure.cs @@ -179,7 +179,7 @@ private void FillInstanceBuffer(TopLevelAccelerationStructureDesc desc, out Acce instances[i] = new() { - Transform = *(TransformMatrixKHR*)&instance.Transform, + Transform = VKFormats.Vulkan(instance.Transform), InstanceCustomIndex = instance.ID, Mask = instance.Mask, Flags = VKFormats.Vulkan(instance.Flags), From 635fab46c8d4c2f9261a4613ee6516543c7a1e4b Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Sat, 4 Apr 2026 13:04:31 +0800 Subject: [PATCH 14/24] Refactor matrix assignments for clarity in format methods Expanded matrix element assignments in DXFormats.DirectX12, MTLFormats.Metal, and VKFormats.Vulkan to use one line per element instead of compact row assignments. This change improves code readability and maintainability without altering functionality. --- sources/Zenith.NET.DirectX12/DXFormats.cs | 18 +++++++++++++++--- sources/Zenith.NET.Metal/MTLFormats.cs | 18 +++++++++++++++--- sources/Zenith.NET.Vulkan/VKFormats.cs | 18 +++++++++++++++--- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/sources/Zenith.NET.DirectX12/DXFormats.cs b/sources/Zenith.NET.DirectX12/DXFormats.cs index b36842d0..232db5b1 100644 --- a/sources/Zenith.NET.DirectX12/DXFormats.cs +++ b/sources/Zenith.NET.DirectX12/DXFormats.cs @@ -540,9 +540,21 @@ public static unsafe Matrix3X4 DirectX12(Matrix4x4 matrix4x4) Matrix3X4 result; float* pResult = (float*)&result; - pResult[0] = matrix4x4.M11; pResult[1] = matrix4x4.M21; pResult[2] = matrix4x4.M31; pResult[3] = matrix4x4.M41; - pResult[4] = matrix4x4.M12; pResult[5] = matrix4x4.M22; pResult[6] = matrix4x4.M32; pResult[7] = matrix4x4.M42; - pResult[8] = matrix4x4.M13; pResult[9] = matrix4x4.M23; pResult[10] = matrix4x4.M33; pResult[11] = matrix4x4.M43; + + pResult[0] = matrix4x4.M11; + pResult[1] = matrix4x4.M21; + pResult[2] = matrix4x4.M31; + pResult[3] = matrix4x4.M41; + + pResult[4] = matrix4x4.M12; + pResult[5] = matrix4x4.M22; + pResult[6] = matrix4x4.M32; + pResult[7] = matrix4x4.M42; + + pResult[8] = matrix4x4.M13; + pResult[9] = matrix4x4.M23; + pResult[10] = matrix4x4.M33; + pResult[11] = matrix4x4.M43; return result; } diff --git a/sources/Zenith.NET.Metal/MTLFormats.cs b/sources/Zenith.NET.Metal/MTLFormats.cs index 1b0951d9..cbb39617 100644 --- a/sources/Zenith.NET.Metal/MTLFormats.cs +++ b/sources/Zenith.NET.Metal/MTLFormats.cs @@ -458,9 +458,21 @@ public static unsafe MTLPackedFloat4x3 Metal(Matrix4x4 matrix4x4) MTLPackedFloat4x3 result; float* pResult = (float*)&result; - pResult[0] = matrix4x4.M11; pResult[1] = matrix4x4.M21; pResult[2] = matrix4x4.M31; pResult[3] = matrix4x4.M41; - pResult[4] = matrix4x4.M12; pResult[5] = matrix4x4.M22; pResult[6] = matrix4x4.M32; pResult[7] = matrix4x4.M42; - pResult[8] = matrix4x4.M13; pResult[9] = matrix4x4.M23; pResult[10] = matrix4x4.M33; pResult[11] = matrix4x4.M43; + + pResult[0] = matrix4x4.M11; + pResult[1] = matrix4x4.M21; + pResult[2] = matrix4x4.M31; + pResult[3] = matrix4x4.M41; + + pResult[4] = matrix4x4.M12; + pResult[5] = matrix4x4.M22; + pResult[6] = matrix4x4.M32; + pResult[7] = matrix4x4.M42; + + pResult[8] = matrix4x4.M13; + pResult[9] = matrix4x4.M23; + pResult[10] = matrix4x4.M33; + pResult[11] = matrix4x4.M43; return result; } diff --git a/sources/Zenith.NET.Vulkan/VKFormats.cs b/sources/Zenith.NET.Vulkan/VKFormats.cs index b5fae0ee..dd81d0ca 100644 --- a/sources/Zenith.NET.Vulkan/VKFormats.cs +++ b/sources/Zenith.NET.Vulkan/VKFormats.cs @@ -619,9 +619,21 @@ public static unsafe TransformMatrixKHR Vulkan(Matrix4x4 matrix4x4) TransformMatrixKHR result; float* pResult = (float*)&result; - pResult[0] = matrix4x4.M11; pResult[1] = matrix4x4.M21; pResult[2] = matrix4x4.M31; pResult[3] = matrix4x4.M41; - pResult[4] = matrix4x4.M12; pResult[5] = matrix4x4.M22; pResult[6] = matrix4x4.M32; pResult[7] = matrix4x4.M42; - pResult[8] = matrix4x4.M13; pResult[9] = matrix4x4.M23; pResult[10] = matrix4x4.M33; pResult[11] = matrix4x4.M43; + + pResult[0] = matrix4x4.M11; + pResult[1] = matrix4x4.M21; + pResult[2] = matrix4x4.M31; + pResult[3] = matrix4x4.M41; + + pResult[4] = matrix4x4.M12; + pResult[5] = matrix4x4.M22; + pResult[6] = matrix4x4.M32; + pResult[7] = matrix4x4.M42; + + pResult[8] = matrix4x4.M13; + pResult[9] = matrix4x4.M23; + pResult[10] = matrix4x4.M33; + pResult[11] = matrix4x4.M43; return result; } From 6dab9c58a73a36c2d475cddf1fd5e2783241f30b Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Sat, 4 Apr 2026 13:09:40 +0800 Subject: [PATCH 15/24] Mark format helper classes as unsafe, update method signatures Changed DXFormats, MTLFormats, and VKFormats to unsafe static classes, moving the unsafe context from individual methods to the class level. Updated matrix conversion methods to remove the unsafe modifier from their signatures. No logic changes were made. --- sources/Zenith.NET.DirectX12/DXFormats.cs | 4 ++-- sources/Zenith.NET.Metal/MTLFormats.cs | 4 ++-- sources/Zenith.NET.Vulkan/VKFormats.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sources/Zenith.NET.DirectX12/DXFormats.cs b/sources/Zenith.NET.DirectX12/DXFormats.cs index 232db5b1..eba4d384 100644 --- a/sources/Zenith.NET.DirectX12/DXFormats.cs +++ b/sources/Zenith.NET.DirectX12/DXFormats.cs @@ -6,7 +6,7 @@ namespace Zenith.NET.DirectX12; -internal static class DXFormats +internal static unsafe class DXFormats { public static (ResourceFlags Flags, ResourceStates States, HeapType Type) DirectX12(BufferUsageFlags bufferUsageFlags) { @@ -535,7 +535,7 @@ QueryType.Occlusion or ); } - public static unsafe Matrix3X4 DirectX12(Matrix4x4 matrix4x4) + public static Matrix3X4 DirectX12(Matrix4x4 matrix4x4) { Matrix3X4 result; diff --git a/sources/Zenith.NET.Metal/MTLFormats.cs b/sources/Zenith.NET.Metal/MTLFormats.cs index cbb39617..56a7f965 100644 --- a/sources/Zenith.NET.Metal/MTLFormats.cs +++ b/sources/Zenith.NET.Metal/MTLFormats.cs @@ -3,7 +3,7 @@ namespace Zenith.NET.Metal; -internal static class MTLFormats +internal static unsafe class MTLFormats { public static MTLResourceOptions Metal(BufferUsageFlags bufferUsageFlags) { @@ -453,7 +453,7 @@ public static MTLVisibilityResultMode Metal(QueryType queryType) }; } - public static unsafe MTLPackedFloat4x3 Metal(Matrix4x4 matrix4x4) + public static MTLPackedFloat4x3 Metal(Matrix4x4 matrix4x4) { MTLPackedFloat4x3 result; diff --git a/sources/Zenith.NET.Vulkan/VKFormats.cs b/sources/Zenith.NET.Vulkan/VKFormats.cs index dd81d0ca..fc83082d 100644 --- a/sources/Zenith.NET.Vulkan/VKFormats.cs +++ b/sources/Zenith.NET.Vulkan/VKFormats.cs @@ -3,7 +3,7 @@ namespace Zenith.NET.Vulkan; -internal static class VKFormats +internal static unsafe class VKFormats { public static VkShaderStageFlags Vulkan(ShaderStageFlags shaderStageFlags) { @@ -614,7 +614,7 @@ public static IndexType Vulkan(IndexFormat indexFormat) }; } - public static unsafe TransformMatrixKHR Vulkan(Matrix4x4 matrix4x4) + public static TransformMatrixKHR Vulkan(Matrix4x4 matrix4x4) { TransformMatrixKHR result; From 10c521f3f26864dcfaaa30576fbb1315e07ac26b Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Sat, 4 Apr 2026 13:51:30 +0800 Subject: [PATCH 16/24] Remove CONVENTIONS.md documentation Deleted the entire CONVENTIONS.md file, including all project conventions, GPU alignment rules, resource patterns, render loop details, geometry data, and code samples for both C# and Slang. This removes all internal documentation and development guidelines from the repository. --- sources/Experiments/CornellBox/CONVENTIONS.md | 792 ------------------ 1 file changed, 792 deletions(-) delete mode 100644 sources/Experiments/CornellBox/CONVENTIONS.md diff --git a/sources/Experiments/CornellBox/CONVENTIONS.md b/sources/Experiments/CornellBox/CONVENTIONS.md deleted file mode 100644 index 95ecf1ee..00000000 --- a/sources/Experiments/CornellBox/CONVENTIONS.md +++ /dev/null @@ -1,792 +0,0 @@ -# CornellBox Development Conventions - -## Overview - -A dual-mode Cornell Box renderer built on the Zenith.NET multi-backend GPU framework (DirectX12 / Metal / Vulkan). -Two rendering modes switchable via ImGui radio buttons at runtime: - -- **Path Tracing** (mode 0): `ComputePipeline` + inline `RayQuery<>`, progressive accumulation with NEE (Next Event Estimation), Cook-Torrance PBR BRDF (GGX + Schlick Fresnel + Smith G), GGX importance sampling for specular, cosine-weighted hemisphere for diffuse, Russian roulette, environment sky light on ray miss. Only available when `Context.Capabilities.RayTracingSupported` is true. -- **Rasterization** (mode 1): `GraphicsPipeline` + Blinn-Phong lighting, point light at ceiling, hemisphere ambient, ACES tonemapping. Always available as fallback. - -## Current State (2026-03-30) - -- All files implemented and compiling successfully -- DirectX12 validation layer clean -- Camera initial position: (278, 273, -800), Speed=240, FarPlane=2000, looks into the box -- Swap chain format: B8G8R8A8UNorm color + D32FloatS8UInt depth/stencil -- `Renderer` abstract base class unifies both renderers (`Update` / `Render` / `Resize` + `IDisposable`) -- `activeRenderer` field in App.cs dispatches calls polymorphically - -### Render Loop (App.cs) - -1. `imGui.Update()` → `camera.Update()` → `activeRenderer.Update(camera)` -2. ImGui window: backend info, render mode radio buttons, SPP counter (path tracing only), FPS -3. Create `CommandBuffer` -4. `imGui.Render(commandBuffer, swapChain.FrameBuffer, ClearValues.Default)` — ImGui clears swap chain and renders UI overlay -5. `activeRenderer.Render(commandBuffer)` — renderer writes to its own Color texture, displayed via ImGui `AddImage` -6. `commandBuffer.Submit(true)` → `swapChain.Present()` - -### Disposal Order - -`pathTracer` → `rasterizer` → `imGui` → `swapChain` → `input` → `window` → `Context` - -## Project Structure - -``` -CornellBox/ -├── App.cs # Lifecycle, ImGui mode switching, activeRenderer dispatch -├── Program.cs # Entry point -├── CONVENTIONS.md # This file -├── Renderers/ -│ ├── Renderer.cs # Abstract base class: Color / DepthStencil / FrameBuffer management -│ ├── PathTracingRenderer.cs # ComputePipeline + RayQuery path tracing (NEE + PBR BRDF) -│ └── RasterizationRenderer.cs # GraphicsPipeline + Blinn-Phong -├── Handlers/ -│ ├── CameraHandler.cs # 6DOF camera (WASD+QE, right-click mouselook) -│ └── ImGuiHandler.cs # ImGui integration (input forwarding, font loading) -├── Helpers/ -│ ├── BindingHelper.cs # Multi-backend resource binding index assignment -│ ├── CornellBoxGeometry.cs # Shared geometry factory (Vertex, Material) -│ ├── CocoaHelper.cs # macOS Metal layer creation -│ └── Extensions.cs # ImGui.Overlay extension -└── Assets/ - └── Fonts/msyh.ttf # Chinese font for ImGui -``` - -## GPU Alignment Rules - -### Slang Side - -- **Never** use bare `float3` in ConstantBuffer / StructuredBuffer structs -- Pack `float3` into `float4`, use `private` field + `property` accessor -- If the next field after `float3` is a scalar (`float` / `uint`), merge them into one `float4` with a meaningful combined name (e.g. `NormalAndMaterialID`, `AlbedoAndEmission`); if there is no natural scalar to pair, use `XXXAndPadding` -- **Only** `float3` needs the `float4` + property pattern; scalar types (`float`, `uint`, `int`, `float4x4`, etc.) can be declared directly as normal fields -- Pad trailing bytes with `private float paddingN` to reach 16-byte boundary -- Vertex I/O structs (VSInput) also use `private float4` + `property` for consistency — semantic annotations go on the backing field -- PSInput interpolators can use `float3` directly (controlled by semantic output) -- Attributes go **above** properties, blank line between fields - -```slang -struct Vertex -{ - private float4 PositionAndPadding; - - private float4 NormalAndMaterialID; - - property float3 Position { get { return PositionAndPadding.xyz; } } - - property float3 Normal { get { return NormalAndMaterialID.xyz; } } - - property uint MaterialID { get { return asuint(NormalAndMaterialID.w); } } -}; - -struct Material -{ - private float4 AlbedoAndEmission; - - float Metallic; - - float Roughness; - - private float padding0; - - private float padding1; - - property float3 Albedo { get { return AlbedoAndEmission.xyz; } } - - property float Emission { get { return AlbedoAndEmission.w; } } -}; - -struct CameraParams -{ - float4x4 InvView; - - float4x4 InvProjection; - - private float4 PositionAndPadding; - - uint FrameCount; - - uint Width; - - uint Height; - - private float padding0; - - property float3 Position { get { return PositionAndPadding.xyz; } } -}; -``` - -### C# Side - -- Use `LayoutKind.Explicit` + `FieldOffset` for precise offset control -- Specify `Size` to ensure total size matches Slang side -- C# structs use split, human-readable fields (`Position`, `Normal`, `MaterialID`) — the GPU-side packing is handled by `FieldOffset` matching the Slang `float4` layout -- ConstantBuffer requires 256-byte alignment (`BufferUsageFlags.Constant`) -- Attributes go **above** the field, blank line between fields - -```csharp -[StructLayout(LayoutKind.Explicit, Size = 160)] -file struct CameraParams -{ - [FieldOffset(0)] - public Matrix4x4 InvView; - - [FieldOffset(64)] - public Matrix4x4 InvProjection; - - [FieldOffset(128)] - public Vector3 Position; - - [FieldOffset(144)] - public uint FrameCount; - - [FieldOffset(148)] - public uint Width; - - [FieldOffset(152)] - public uint Height; -} -``` - -### Alignment Quick Reference - -| Type | Size | Alignment | Notes | -|------|------|-----------|-------| -| `float` / `uint` / `int` | 4B | 4B | | -| `float2` / `uint2` | 8B | 8B | | -| `float4` / `uint4` | 16B | 16B | Use instead of float3 | -| `float4x4` | 64B | 16B | `Matrix4x4` | -| struct | - | 16B boundary | Total size must be multiple of 16 | - -### Vertex Input Layout - -Vertex buffers use `InputLayout` + `ElementFormat`. The C# struct uses split fields with `FieldOffset`, while Slang uses `private float4` + `property` for both StructuredBuffer and vertex input: - -```csharp -// 32 bytes — used by both renderers -[StructLayout(LayoutKind.Explicit, Size = 32)] -internal struct Vertex -{ - [FieldOffset(0)] - public Vector3 Position; // maps to PositionAndPadding.xyz - - [FieldOffset(16)] - public Vector3 Normal; // maps to NormalAndMaterialID.xyz - - [FieldOffset(28)] - public uint MaterialID; // maps to NormalAndMaterialID.w -} -``` - -Slang StructuredBuffer struct (path tracing): - -```slang -struct Vertex -{ - private float4 PositionAndPadding; - - private float4 NormalAndMaterialID; - - property float3 Position { get { return PositionAndPadding.xyz; } } - - property float3 Normal { get { return NormalAndMaterialID.xyz; } } - - property uint MaterialID { get { return asuint(NormalAndMaterialID.w); } } -}; -``` - -Slang vertex input struct (rasterization) — same layout with semantic annotations: - -```slang -struct VSInput -{ - private float4 PositionAndPadding : POSITION0; - - private float4 NormalAndMaterialID : NORMAL0; - - property float3 Position { get { return PositionAndPadding.xyz; } } - - property float3 Normal { get { return NormalAndMaterialID.xyz; } } - - property uint MaterialID { get { return asuint(NormalAndMaterialID.w); } } -}; -``` - -`InputLayout` matches the `float4 + float4` backing fields: - -```csharp -InputLayout inputLayout = new(); -inputLayout.Add(new() { Format = ElementFormat.Float4, Semantic = ElementSemantic.Position }); -inputLayout.Add(new() { Format = ElementFormat.Float4, Semantic = ElementSemantic.Normal }); -``` - -## Resource Creation Patterns - -### Buffer - -```csharp -// Vertex buffer (AccelerationStructure flag needed for ray tracing) -buffer = App.Context.CreateBuffer(new() -{ - SizeInBytes = (uint)(sizeof(Vertex) * vertices.Length), - StrideInBytes = (uint)sizeof(Vertex), - Flags = BufferUsageFlags.Vertex | BufferUsageFlags.AccelerationStructure -}); -buffer.Upload(vertices, 0); - -// ConstantBuffer (256B aligned, MapWrite for per-frame update) -cbuffer = App.Context.CreateBuffer(new() -{ - SizeInBytes = (uint)sizeof(CameraParams), - StrideInBytes = (uint)sizeof(CameraParams), - Flags = BufferUsageFlags.Constant | BufferUsageFlags.MapWrite -}); - -// StructuredBuffer (read-only) -sbuffer = App.Context.CreateBuffer(new() -{ - SizeInBytes = (uint)(sizeof(Material) * count), - StrideInBytes = (uint)sizeof(Material), - Flags = BufferUsageFlags.ShaderResource -}); -``` - -### Texture (UAV / Accumulation Buffer) - -```csharp -texture = App.Context.CreateTexture(new() -{ - Type = TextureType.Texture2D, - Format = PixelFormat.R32G32B32A32Float, - Width = width, Height = height, Depth = 1, - MipLevels = 1, ArrayLayers = 1, - SampleCount = SampleCount.Count1, - Flags = TextureUsageFlags.ShaderResource | TextureUsageFlags.UnorderedAccess -}); -``` - -## Acceleration Structure Build Pattern - -```csharp -CommandBuffer buildCmd = App.Context.Graphics.CommandBuffer(); - -// BLAS — one per geometry group -blas = buildCmd.BuildAccelerationStructure(new BottomLevelAccelerationStructureDesc -{ - Geometries = [new() - { - Type = RayTracingGeometryType.Triangles, - Triangles = new() - { - VertexBuffer = vertexBuffer, - VertexFormat = PixelFormat.R32G32B32Float, - VertexCount = vertexCount, - VertexStrideInBytes = (uint)sizeof(Vertex), - IndexBuffer = indexBuffer, - IndexFormat = IndexFormat.UInt32, - IndexCount = indexCount, - Transform = Matrix4x4.Identity - }, - Flags = RayTracingGeometryFlags.Opaque - }], - Flags = AccelerationStructureBuildFlags.PreferFastTrace -}); - -// TLAS — references all BLAS instances -tlas = buildCmd.BuildAccelerationStructure(new TopLevelAccelerationStructureDesc -{ - Instances = [ - new() - { - AccelerationStructure = blas, - ID = 0, - Mask = 0xFF, - Transform = Matrix4x4.Identity, - Flags = RayTracingInstanceFlags.None - } - ], - Flags = AccelerationStructureBuildFlags.PreferFastTrace -}); - -buildCmd.Submit(waitForCompletion: true); -``` - -## Pipeline Creation Patterns - -### ComputePipeline (Path Tracing) - -```csharp -resourceLayout = App.Context.CreateResourceLayout(new() -{ - Bindings = BindingHelper.Bindings( - new() { Type = ResourceType.AccelerationStructure, Count = 1, StageFlags = ShaderStageFlags.Compute }, - new() { Type = ResourceType.ConstantBuffer, Count = 1, StageFlags = ShaderStageFlags.Compute }, - new() { Type = ResourceType.StructuredBuffer, Count = 1, StageFlags = ShaderStageFlags.Compute }, - new() { Type = ResourceType.StructuredBuffer, Count = 1, StageFlags = ShaderStageFlags.Compute }, - new() { Type = ResourceType.StructuredBuffer, Count = 1, StageFlags = ShaderStageFlags.Compute }, - new() { Type = ResourceType.TextureReadWrite, Count = 1, StageFlags = ShaderStageFlags.Compute }, - new() { Type = ResourceType.TextureReadWrite, Count = 1, StageFlags = ShaderStageFlags.Compute } - ) -}); - -using Shader cs = App.Context.LoadShaderFromSource(ShaderSource, "CSMain", ShaderStageFlags.Compute); -pipeline = App.Context.CreateComputePipeline(new() -{ - Compute = cs, - ResourceLayout = resourceLayout, - ThreadGroupSizeX = 16, ThreadGroupSizeY = 16, ThreadGroupSizeZ = 1 -}); -``` - -### GraphicsPipeline (Rasterization) - -```csharp -using Shader vs = App.Context.LoadShaderFromSource(ShaderSource, "VSMain", ShaderStageFlags.Vertex); -using Shader ps = App.Context.LoadShaderFromSource(ShaderSource, "PSMain", ShaderStageFlags.Pixel); - -pipeline = App.Context.CreateGraphicsPipeline(new() -{ - RenderStates = new() - { - RasterizerState = RasterizerStates.CullBack, - DepthStencilState = DepthStencilStates.Default, - BlendState = BlendStates.Opaque - }, - Vertex = vs, Pixel = ps, - ResourceLayout = resourceLayout, - InputLayouts = [inputLayout], - PrimitiveTopology = PrimitiveTopology.TriangleList, - Output = App.SwapChain.FrameBuffer.Output -}); -``` - -## Resource Binding Pattern - -Declaration order in ResourceLayout Bindings = declaration order in shader = resource order in ResourceTable. - -```csharp -// Layout declaration order -Bindings = BindingHelper.Bindings( - new() { Type = ResourceType.AccelerationStructure, ... }, // [0] scene - new() { Type = ResourceType.ConstantBuffer, ... }, // [1] camera - new() { Type = ResourceType.StructuredBuffer, ... }, // [2] vertices - new() { Type = ResourceType.StructuredBuffer, ... }, // [3] indices - new() { Type = ResourceType.StructuredBuffer, ... }, // [4] materials - new() { Type = ResourceType.TextureReadWrite, ... }, // [5] accumTexture - new() { Type = ResourceType.TextureReadWrite, ... }, // [6] outputTexture -); - -// Shader declares in same order: -// RaytracingAccelerationStructure scene; -// ConstantBuffer camera; -// StructuredBuffer vertices; -// StructuredBuffer indices; -// StructuredBuffer materials; -// RWTexture2D accumTexture; -// RWTexture2D outputTexture; - -// ResourceTable passes in same order -resourceTable = App.Context.CreateResourceTable(new() -{ - Layout = resourceLayout, - Resources = [tlas, cameraBuffer, vertexBuffer, indexBuffer, materialBuffer, accumTexture, Color] -}); -``` - -`BindingHelper.Bindings()` handles per-backend index differences transparently. - -## Render Loop Patterns - -### Compute Output → Color Texture - -```csharp -cmd.SetPipeline(computePipeline); -cmd.SetResourceTable(resourceTable); -cmd.Dispatch(dispatchX, dispatchY, 1); -``` - -The compute shader writes directly to the base class `Color` texture (bound as `outputTexture` UAV in the ResourceTable). No `CopyTexture` needed — ImGui displays it via `AddImage`. - -### Graphics RenderPass → SwapChain - -```csharp -cmd.BeginRenderPass(App.SwapChain.FrameBuffer, new() -{ - ColorValues = [new(0.51f, 0.518f, 0.557f, 1)], - Depth = 1.0f, Stencil = 0, - Flags = ClearFlags.All -}); -cmd.SetPipeline(graphicsPipeline); -cmd.SetResourceTable(resourceTable); -cmd.SetVertexBuffer(vertexBuffer, 0, 0); -cmd.SetIndexBuffer(indexBuffer, 0, IndexFormat.UInt32); -cmd.DrawIndexed(indexCount, 1, 0, 0, 0); -cmd.EndRenderPass(); -``` - -## Resize Pattern - -Resources to rebuild on size change: - -- Texture (accumulationTexture) -- ResourceTable (references Texture) -- Reset path tracing FrameCount to 0 - -No rebuild needed: Buffer, Pipeline, ResourceLayout, acceleration structures. - -```csharp -public void Resize(uint width, uint height) -{ - base.Resize(width, height); // recreates Color + DepthStencil + FrameBuffer - - resourceTable?.Dispose(); - resourceTable = null; - accumulationTexture?.Dispose(); - accumulationTexture = null; - - FrameCount = 0; - // Lazy rebuild on next Render call -} -``` - -## Dispose Order - -Release in reverse creation order — downstream first, upstream last: - -``` -ResourceTable → Pipeline → ResourceLayout -→ Texture (accumulation) -→ TLAS → BLAS[] -→ Buffer (camera / material / index / vertex) -``` - -## Cornell Box Geometry Data (CornellBoxGeometry.cs) - -- Coordinate range 0~560 (standard Cornell Box specification) -- 16 quads (64 vertices, 96 indices): 5 walls + 5 short block faces + 5 tall block faces + 1 light -- 6 material groups: 0=red left wall, 1=green right wall, 2=white surfaces (ceiling/floor/back wall), 3=light, 4=short block (smooth diffuse), 5=tall block (metallic mirror) -- Each quad → 4 vertices + 6 indices (2 triangles) -- Normals auto-computed via `normalize(cross(v1-v0, v2-v0))` -- Material ID stored in `Vertex.MaterialID` (C#), packed into `NormalAndMaterialID.w` on GPU via `FieldOffset(28)` overlapping the `float4` w-component -- Material colors: red(0.63,0.06,0.06), green(0.14,0.45,0.09), white(0.73,0.71,0.68), light(1.0,0.85,0.6)+emission=25, short block(0.73,0.71,0.68)+roughness=0.3, tall block(0.95,0.93,0.88)+metallic=1.0+roughness=0.05 - -### Shared Data Types - -```csharp -// 32 bytes — used by both renderers -[StructLayout(LayoutKind.Explicit, Size = 32)] -internal struct Vertex -{ - [FieldOffset(0)] - public Vector3 Position; - - [FieldOffset(16)] - public Vector3 Normal; - - [FieldOffset(28)] - public uint MaterialID; -} - -// 32 bytes — PBR material with metallic/roughness -[StructLayout(LayoutKind.Explicit, Size = 32)] -internal struct Material -{ - [FieldOffset(0)] - public Vector3 Albedo; - - [FieldOffset(12)] - public float Emission; - - [FieldOffset(16)] - public float Metallic; - - [FieldOffset(20)] - public float Roughness; -} -``` - -## Renderer Base Class (Renderer.cs) - -```csharp -internal abstract class Renderer : IDisposable -{ - public Texture Color { get; private set; } - public Texture DepthStencil { get; private set; } - public FrameBuffer FrameBuffer { get; private set; } - - abstract void Update(CameraHandler camera); - abstract void Render(CommandBuffer commandBuffer); - virtual void Resize(uint width, uint height); // recreates Color + DepthStencil + FrameBuffer - virtual void Dispose(); // disposes FrameBuffer + DepthStencil + Color -} -``` - -- Constructor calls `Resize(App.Width, App.Height)` to create initial resources -- `Resize()`: Disposes then recreates `Color` (B8G8R8A8UNorm, RenderTarget | ShaderResource | UnorderedAccess), `DepthStencil` (D32FloatS8UInt), and `FrameBuffer` -- `Color` texture is used by path tracer as compute output target, and displayed via ImGui `AddImage` in App.cs -- Subclasses call `base.Resize()` / `base.Dispose()` to manage these shared resources - -## PathTracingRenderer Details - -### Overview - -- **Pipeline**: `ComputePipeline` with `[numthreads(16,16,1)]`, entry point `CSMain` -- **Shader**: Inline Slang raw string literal -- **Algorithm**: 8-bounce path tracing + NEE (Next Event Estimation) + Cook-Torrance PBR BRDF + Russian roulette (bounce ≥ 2) -- **Accumulation**: R32G32B32A32Float UAV texture, progressive average with jittered subpixel sampling -- **Tonemapping**: ACES filmic + gamma correction -- **Environment**: Gradient sky light on ray miss (warm ground → cool sky) -- **Output**: Tonemapped + gamma-corrected to `Color` texture (base class, B8G8R8A8UNorm) - -### Shader Structs - -```slang -struct Vertex // StructuredBuffer — private float4 + property -struct Material // StructuredBuffer — private float4 + property -struct CameraParams // ConstantBuffer — float4x4 × 2, private float4 Position, uint × 3 + padding -``` - -### Resource Bindings (7 slots) - -| Index | Type | Shader Variable | Description | -|-------|------|-----------------|-------------| -| 0 | AccelerationStructure | `scene` | TLAS for ray queries | -| 1 | ConstantBuffer | `camera` | CameraParams (160B) | -| 2 | StructuredBuffer | `vertices` | Vertex[] (32B stride) | -| 3 | StructuredBuffer | `indices` | uint[] | -| 4 | StructuredBuffer | `materials` | Material[] (32B stride) | -| 5 | TextureReadWrite | `accumTexture` | R32G32B32A32Float accumulation buffer | -| 6 | TextureReadWrite | `outputTexture` | Base class `Color` texture (compute write target) | - -### Shader Functions - -| Function | Purpose | -|----------|---------| -| `pcgHash(uint)` | PCG hash for RNG seed | -| `randomFloat(inout uint)` | Returns [0,1) float, advances seed | -| `cosineSampleHemisphere(float3, inout uint)` | Cosine-weighted hemisphere sampling around normal | -| `traceShadowRay(float3, float3, float)` | Shadow ray test using `RayQuery` | -| `sampleLightDirect(float3, float3, float3, Material, inout uint)` | NEE: uniform sample on ceiling light quad, Cook-Torrance BRDF, geometry term | -| `DistributionGGX(float, float)` | GGX normal distribution function | -| `GeometrySchlickGGX(float, float)` | Schlick-GGX geometry sub-function | -| `GeometrySmith(float, float, float)` | Smith geometry function (combined) | -| `FresnelSchlick(float, float3)` | Schlick Fresnel approximation | -| `evaluateBRDF(float3, float3, float3, Material)` | Full Cook-Torrance BRDF evaluation (diffuse + specular) | -| `sampleGGXHalfVector(float3, float, inout uint)` | GGX importance sampling for specular half-vector | -| `tracePath(float3, float3, inout uint)` | Main path tracing loop (8 bounces max) | -| `CSMain(uint3)` | Entry point: generate ray, trace, accumulate, ACES tonemap, gamma-correct | - -### Path Tracing Algorithm (`tracePath`) - -1. For each bounce (max 8): - - Trace primary ray via `RayQuery` - - On miss → add environment sky contribution (`lerp` warm ground to cool sky based on `direction.y`) → break - - Compute hit position, interpolate normal via barycentric weights from index/vertex buffers - - Flip normal if back-facing (`dot(normal, direction) > 0`) - - If emissive material: accumulate emission on bounce 0 only, then break (prevents double-counting with NEE) - - Non-emissive: add NEE contribution via `sampleLightDirect()` using Cook-Torrance BRDF - - Probabilistic BRDF sampling: compute `specProb` from F0 and metallic, then: - - With probability `specProb`: GGX importance sample half-vector → reflect → specular throughput (clamped to 10) - - Otherwise: cosine-weighted hemisphere → diffuse throughput - - Russian roulette (bounce ≥ 2): survival probability = max component of throughput - - Per-sample radiance clamped to 30 to suppress fireflies - -### Light Constants (hardcoded) - -```slang -static const float3 LightMin = float3(213.0, 548.6, 227.0); -static const float3 LightMax = float3(343.0, 548.6, 332.0); -static const float LightArea = 13650.0; // (343-213) * (332-227) -static const float3 LightNormal = float3(0.0, -1.0, 0.0); -``` - -### Camera Ray Generation (`CSMain`) - -1. RNG seed: `pcgHash(pixel.x + pixel.y * Width + FrameCount * Width * Height)` -2. Jittered subpixel offset → UV → NDC (y-flipped) -3. `InvProjection` → local direction → `InvView` → world direction -4. Origin = `camera.Position` - -### Accumulation - -- `FrameCount == 0`: overwrite `accumTexture` with new sample -- `FrameCount > 0`: add to existing accumulation -- Running average: `accumulated.rgb / (FrameCount + 1)` -- ACES tonemapping: `saturate((x * (2.51x + 0.03)) / (x * (2.43x + 0.59) + 0.14))` -- Gamma correction: `pow(avg, 1/2.2)` → `outputTexture` - -### Camera Change Detection - -```csharp -if (view != lastView || projection != lastProjection) -{ - lastView = view; - lastProjection = projection; - FrameCount = 0; // reset accumulation -} -``` - -### C# Side CameraParams (160B) - -```csharp -[StructLayout(LayoutKind.Explicit, Size = 160)] -file struct CameraParams -{ - [FieldOffset(0)] public Matrix4x4 InvView; - [FieldOffset(64)] public Matrix4x4 InvProjection; - [FieldOffset(128)] public Vector3 Position; - [FieldOffset(144)] public uint FrameCount; - [FieldOffset(148)] public uint Width; - [FieldOffset(152)] public uint Height; -} -``` - -### Buffer Setup - -- `vertexBuffer`: ShaderResource | AccelerationStructure -- `indexBuffer`: ShaderResource | AccelerationStructure -- `materialBuffer`: ShaderResource -- `cameraBuffer`: Constant | MapWrite (per-frame upload) -- Acceleration structures: single BLAS (all geometry, PreferFastTrace) → single TLAS (one instance) - -### Resize / Lifecycle - -- `Resize()`: calls `base.Resize()`, disposes `resourceTable` + `accumulationTexture`, set to null for lazy rebuild -- `Render()`: if resources are null → create `accumulationTexture` (R32G32B32A32Float) + `resourceTable`, reset `FrameCount` -- `Dispatch()`: `ceil(Width/16) × ceil(Height/16) × 1` -- `FrameCount++` after each dispatch - -### Dispose Order - -``` -base (FrameBuffer + DepthStencil + Color) -→ resourceTable → accumulationTexture -→ pipeline → resourceLayout -→ tlas → blas -→ cameraBuffer → materialBuffer → indexBuffer → vertexBuffer -``` - -## RasterizationRenderer Details - -### Overview - -- **Pipeline**: `GraphicsPipeline` with Vertex + Pixel shaders, entry points `VSMain` / `PSMain` -- **Shader**: Inline Slang raw string literal -- **Algorithm**: Blinn-Phong shading with point light, hemisphere ambient, ACES tonemapping -- **Output**: Renders directly to `FrameBuffer` (base class) via render pass - -### Shader Structs - -```slang -struct Material // StructuredBuffer — private float4 + property -struct RasterConstants // ConstantBuffer — float4x4 × 3, private float4 × 3 + properties -struct VSInput // Vertex input — private float4 × 2 + properties (Position, Normal, MaterialID) -struct PSInput // Interpolated — float4 Position, float3 WorldPos, float3 Normal, nointerpolation uint MaterialID -``` - -### Resource Bindings (2 slots) - -| Index | Type | Shader Variable | Stages | Description | -|-------|------|-----------------|--------|-------------| -| 0 | ConstantBuffer | `cb` | Vertex + Pixel | RasterConstants (240B) | -| 1 | StructuredBuffer | `materials` | Pixel | Material[] (32B stride) | - -### Vertex Shader (`VSMain`) - -1. Transform position: `worldPos = mul(float4(input.Position, 1.0), cb.Model)` -2. Clip space: `mul(mul(worldPos, cb.View), cb.Projection)` -3. Pass world position, transformed normal, MaterialID to PSInput -4. MaterialID via `asuint(NormalAndMaterialID.w)` through VSInput property, passed as `nointerpolation uint` - -### Pixel Shader (`PSMain`) - -1. **Emissive check**: if `mat.Emission > 0` → Reinhard tonemapping: `color / (color + 1)` → gamma correct → return -2. **Blinn-Phong**: - - Hemisphere ambient: `albedo * lerp(0.06, 0.15, N.y * 0.5 + 0.5)` (brighter on upward-facing surfaces) - - Diffuse: `albedo * lightColor * NdotL * atten` - - Specular: `lightColor * pow(NdotH, 64) * atten * 0.1` - - Distance attenuation: `1 / (1 + 0.000005 * dist²)` -3. ACES tonemapping: `saturate((x * (2.51x + 0.03)) / (x * (2.43x + 0.59) + 0.14))` -4. Gamma correction: `pow(color, 1/2.2)` - -### Light Configuration - -- Point light position: (278, 548, 280) -- Light color: (2.0, 1.8, 1.4) -- Ambient factor: hemisphere-based (0.06 bottom to 0.15 top) -- Specular exponent: 64, weight: 0.1 - -### C# Side RasterConstants (240B) - -```csharp -[StructLayout(LayoutKind.Explicit, Size = 240)] -file struct RasterConstants -{ - [FieldOffset(0)] public Matrix4x4 Model; // Identity - [FieldOffset(64)] public Matrix4x4 View; - [FieldOffset(128)] public Matrix4x4 Projection; - [FieldOffset(192)] public Vector3 LightPos; // maps to private float4 LightPosAndPadding - [FieldOffset(208)] public Vector3 LightColor; // maps to private float4 LightColorAndPadding - [FieldOffset(224)] public Vector3 CameraPos; // maps to private float4 CameraPosAndPadding -} -``` - -### Pipeline Configuration - -- RasterizerState: `CullNone` (camera is inside the box) -- DepthStencilState: `Default` -- BlendState: `Opaque` -- PrimitiveTopology: `TriangleList` -- InputLayout: Float4 (POSITION) + Float4 (NORMAL), stride = 32 - -### Buffer Setup - -- `vertexBuffer`: Vertex only -- `indexBuffer`: Index only -- `materialBuffer`: ShaderResource -- `constantBuffer`: Constant | MapWrite (per-frame upload) -- `resourceTable`: created once in constructor (no size-dependent resources) - -### Render Pass - -```csharp -cmd.BeginRenderPass(FrameBuffer, clearValues, resourceTable); -cmd.SetPipeline(pipeline); -cmd.SetResourceTable(resourceTable); -cmd.SetVertexBuffer(vertexBuffer, 0, 0); -cmd.SetIndexBuffer(indexBuffer, 0, IndexFormat.UInt32); -cmd.DrawIndexed(indexCount, 1, 0, 0, 0); -cmd.EndRenderPass(); -``` - -Clear values: color (0.51, 0.518, 0.557, 1) matching environment sky, depth 1.0, stencil 0, ClearFlags.All - -### Resize / Lifecycle - -- `Resize()`: only `base.Resize()` — no renderer-specific size-dependent resources -- No `accumulationTexture`, no `resourceTable` rebuild needed - -### Dispose Order - -``` -base (FrameBuffer + DepthStencil + Color) -→ pipeline → resourceTable → resourceLayout -→ constantBuffer → materialBuffer → indexBuffer → vertexBuffer -``` - -## DirectX12 Specific Notes - -- `BindingHelper` assigns DirectX12 register indices by type: CBV(b), SRV(t), UAV(u), Sampler(s) independently numbered -- Metal uses argument buffer index; Vulkan numbers all bindings sequentially - -## C# Code Style - -- Prefer `is` / `is not` pattern matching over `==` / `!=` for comparisons -- Use full `using` imports, short type names in code (no `Namespace.Type` inline references) -- `file struct` for shader-mirrored GPU structs (scoped to the file that uses them) -- Shaders inlined as `const string ShaderSource = """...""";` (raw string literal) -- Collection expressions: `[]` for empty, `[.. spread]` for conversion -- Target-typed `new()` for object initializers -- `static readonly` fields for framework objects created once -- Blank line between each field / property / method From 886d5b68e56dac738a1be31a050ea2810ed86fc9 Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Sat, 4 Apr 2026 15:09:12 +0800 Subject: [PATCH 17/24] Modernize advanced tutorials: mesh shading & ray tracing Revamp mesh shading to use amplification shader culling and render 1,000 procedural spheres with per-instance frustum culling. Upgrade ray tracing to support soft shadows, rough reflections, Fresnel, and ACES tonemapping with an animated camera. Refactor framework to use App.FrameBuffer and improve resource management. Update all tutorials and documentation for consistency, clarity, and modern GPU best practices. --- documents/tutorials/advanced/mesh-shading.md | 711 ++++++++++-------- documents/tutorials/advanced/ray-tracing.md | 625 ++++++++++----- .../getting-started/hello-triangle.md | 27 +- .../getting-started/prerequisites.md | 118 +-- .../getting-started/spinning-cube.md | 104 ++- .../getting-started/textured-quad.md | 27 +- documents/tutorials/index.md | 8 +- .../tutorials/intermediate/compute-shader.md | 34 +- .../intermediate/indirect-drawing.md | 79 +- 9 files changed, 1006 insertions(+), 727 deletions(-) diff --git a/documents/tutorials/advanced/mesh-shading.md b/documents/tutorials/advanced/mesh-shading.md index 30771e2b..7c4f00d4 100644 --- a/documents/tutorials/advanced/mesh-shading.md +++ b/documents/tutorials/advanced/mesh-shading.md @@ -1,6 +1,6 @@ # Mesh Shading -In this tutorial, you'll learn how to use mesh shading with Zenith.NET. We'll render a simple cube using the mesh shading pipeline, demonstrating the modern GPU-driven geometry processing approach. +In this tutorial, you'll learn how to use mesh shading with Zenith.NET. We'll render a 10×10×10 grid of 1,000 procedurally generated spheres using the mesh shading pipeline with an amplification shader that performs GPU-driven frustum culling. > [!NOTE] > This tutorial requires a GPU with mesh shading support. Check `Context.Capabilities.MeshShadingSupported` before using mesh shading features. @@ -9,10 +9,12 @@ In this tutorial, you'll learn how to use mesh shading with Zenith.NET. We'll re We'll create a `MeshShadingRenderer` class that: -- Defines vertex and meshlet data structures -- Creates structured buffers for vertices, indices, and meshlets -- Builds a mesh shading pipeline with mesh and pixel shaders -- Dispatches mesh shading workgroups to render geometry +- Procedurally generates UV sphere geometry (vertices and triangles) +- Creates structured buffers for vertex and index data +- Uses an amplification shader for per-instance frustum culling +- Dispatches visible instances through a mesh shader +- Animates the camera orbit with dynamic light direction +- Renders 1,000 sphere instances with position-based coloring ## Key Concepts @@ -31,31 +33,29 @@ Mesh shading replaces the traditional vertex processing pipeline (Input Assemble | Rasterizer | Rasterizer | | Pixel Shader | Pixel Shader | -### Meshlet Architecture +### Amplification + Mesh Shader Architecture -The mesh shading pipeline works with **meshlets** - small chunks of geometry that can be processed independently: +In this tutorial, the amplification shader runs first and determines which sphere instances are visible. It then dispatches mesh shader workgroups only for visible instances: ``` -Mesh -├── Meshlet 0 (up to 64-256 vertices, 64-256 primitives) -├── Meshlet 1 -├── Meshlet 2 -└── ... +Amplification Shader (1 thread per instance) +├── Frustum cull each instance +├── Collect visible instance indices into shared payload +└── DispatchMesh(visibleCount, 1, 1, payload) + └── Mesh Shader (1 workgroup per visible instance) + ├── Read sphere vertices from structured buffer + ├── Offset by instance position + ├── Transform to clip space + └── Output vertices and primitives to rasterizer ``` -Each meshlet contains: -- **VertexOffset**: Starting index in the vertex buffer -- **VertexCount**: Number of vertices in this meshlet -- **PrimitiveOffset**: Starting index in the index buffer -- **PrimitiveCount**: Number of triangles in this meshlet - ### Pipeline Stages | Shader Stage | Description | |--------------|-------------| -| **Amplification** (optional) | Determines how many mesh shading workgroups to spawn (LOD, culling) | -| **Mesh** | Outputs vertices and primitives directly to the rasterizer | -| **Pixel** | Standard fragment shading | +| **Amplification** | Performs per-instance frustum culling and dispatches only visible instances | +| **Mesh** | Reads geometry from buffers, transforms vertices, outputs primitives | +| **Pixel** | Computes per-pixel lighting | ## The Renderer Class @@ -66,11 +66,22 @@ namespace ZenithTutorials.Renderers; internal unsafe class MeshShadingRenderer : IRenderer { - private const uint MaxPrimitives = 126; + private const uint ASGroupSize = 32; + private const uint MeshGroupSize = 120; + private const uint GridSize = 10; + private const uint TotalInstances = GridSize * GridSize * GridSize; + private const uint DispatchGroupCount = (TotalInstances + ASGroupSize - 1) / ASGroupSize; private const string ShaderSource = """ - static const uint MaxVertices = 64; - static const uint MaxPrimitives = 126; + static const uint GridSize = 10; + static const uint TotalInstances = GridSize * GridSize * GridSize; + static const float InstanceSpacing = 2.5; + static const uint ASGroupSize = 32; + static const float BoundingSphereRadius = 0.5; + + static const uint SphereVertexCount = 62; + static const uint SphereTriangleCount = 120; + static const float GridOffset = float(GridSize - 1) * 0.5 * InstanceSpacing; struct Vertex { @@ -78,97 +89,184 @@ internal unsafe class MeshShadingRenderer : IRenderer private float4 NormalAndPadding; - float2 TexCoord; - - private float padding0; + property float3 Position + { + get { + return PositionAndPadding.xyz; + } + } - private float padding1; + property float3 Normal + { + get { + return NormalAndPadding.xyz; + } + } + }; - property float3 Position { get { return PositionAndPadding.xyz; } } + struct Triangle + { + private uint4 IndicesAndPadding; - property float3 Normal { get { return NormalAndPadding.xyz; } } + property uint3 Indices + { + get { + return IndicesAndPadding.xyz; + } + } }; - struct Meshlet + struct Payload { - uint VertexOffset; + uint InstanceIndices[ASGroupSize]; + }; - uint VertexCount; + struct VertexOutput + { + float4 Position : SV_POSITION; - uint PrimitiveOffset; + float3 WorldNormal : WORLDNORMAL; - uint PrimitiveCount; + float3 Color : COLOR; }; - struct Triangle + struct Constants { - private uint4 IndicesAndPadding; + float4x4 ViewProjection; + + float4 FrustumPlanes[6]; - property uint3 Indices { get { return IndicesAndPadding.xyz; } } + private float4 TimeAndLightDirection; + + property float Time + { + get { + return TimeAndLightDirection.x; + } + } + + property float3 LightDirection + { + get { + return TimeAndLightDirection.yzw; + } + } }; - struct TransformConstants + void DecomposeInstanceID(uint id, out uint x, out uint y, out uint z) { - float4x4 MVP; - }; + x = id % GridSize; + y = (id / GridSize) % GridSize; + z = id / (GridSize * GridSize); + } - struct VertexOutput + float3 InstancePosition(uint id) { - float4 Position : SV_Position; + uint x, y, z; + DecomposeInstanceID(id, x, y, z); + return float3(x, y, z) * InstanceSpacing - GridOffset; + } - float3 Normal : NORMAL; + float3 InstanceColor(uint id) + { + uint x, y, z; + DecomposeInstanceID(id, x, y, z); + return float3(x, y, z) / float(GridSize - 1); + } - float2 TexCoord : TEXCOORD0; - }; + bool IsFrustumCulled(float3 center, float radius) + { + for (uint i = 0; i < 6; i++) + { + float4 plane = constants.FrustumPlanes[i]; + if (dot(plane.xyz, center) + plane.w < -radius) + { + return true; + } + } + return false; + } - ConstantBuffer transform; + ConstantBuffer constants; StructuredBuffer vertices; StructuredBuffer indices; - StructuredBuffer meshlets; + + groupshared Payload s_payload; + groupshared uint s_visibleCount; + + [shader("amplification")] + [numthreads(ASGroupSize, 1, 1)] + void ASMain(uint groupID: SV_GroupID, uint groupThreadID: SV_GroupThreadID) + { + uint instanceIndex = groupID * ASGroupSize + groupThreadID; + + bool visible = false; + if (instanceIndex < TotalInstances) + { + float3 worldPos = InstancePosition(instanceIndex); + visible = !IsFrustumCulled(worldPos, BoundingSphereRadius); + } + + if (groupThreadID == 0) + { + s_visibleCount = 0; + } + + GroupMemoryBarrierWithGroupSync(); + + if (visible) + { + uint offset; + InterlockedAdd(s_visibleCount, 1, offset); + s_payload.InstanceIndices[offset] = instanceIndex; + } + + GroupMemoryBarrierWithGroupSync(); + + DispatchMesh(s_visibleCount, 1, 1, s_payload); + } [shader("mesh")] - [numthreads(MaxPrimitives, 1, 1)] + [numthreads(120, 1, 1)] [outputtopology("triangle")] - void MSMain(in uint groupID : SV_GroupID, - in uint groupThreadID : SV_GroupThreadID, - OutputVertices outVertices, - OutputIndices outIndices) + void MSMain(uint groupID: SV_GroupID, uint groupThreadID: SV_GroupThreadID, in payload Payload meshPayload, + OutputVertices outVertices, OutputIndices outIndices) { - Meshlet meshlet = meshlets[groupID]; + uint instanceIndex = meshPayload.InstanceIndices[groupID]; + float3 instancePos = InstancePosition(instanceIndex); + float3 color = InstanceColor(instanceIndex); - SetMeshOutputCounts(meshlet.VertexCount, meshlet.PrimitiveCount); + SetMeshOutputCounts(SphereVertexCount, SphereTriangleCount); - if (groupThreadID < meshlet.VertexCount) + if (groupThreadID < SphereVertexCount) { - Vertex vertex = vertices[meshlet.VertexOffset + groupThreadID]; + Vertex v = vertices[groupThreadID]; + float3 worldPos = v.Position + instancePos; VertexOutput output; - output.Position = mul(float4(vertex.Position, 1.0), transform.MVP); - output.Normal = vertex.Normal; - output.TexCoord = vertex.TexCoord; + output.Position = mul(float4(worldPos, 1.0), constants.ViewProjection); + output.WorldNormal = v.Normal; + output.Color = color; outVertices[groupThreadID] = output; } - if (groupThreadID < meshlet.PrimitiveCount) + if (groupThreadID < SphereTriangleCount) { - outIndices[groupThreadID] = indices[meshlet.PrimitiveOffset + groupThreadID].Indices; + outIndices[groupThreadID] = indices[groupThreadID].Indices; } } [shader("pixel")] - float4 PSMain(VertexOutput input) : SV_Target + float4 PSMain(VertexOutput input) : SV_TARGET { - // Simple directional lighting - float3 lightDir = normalize(float3(1.0, 1.0, -1.0)); - float ndotl = max(dot(normalize(input.Normal), lightDir), 0.0); + float3 lightDir = normalize(constants.LightDirection); + float3 normal = normalize(input.WorldNormal); - // Base color from texture coordinates - float3 baseColor = float3(input.TexCoord, 0.5); + float ndotl = max(dot(normal, lightDir), 0.0); - // Ambient + diffuse lighting - float3 ambient = baseColor * 0.2; - float3 diffuse = baseColor * ndotl * 0.8; + float3 ambient = input.Color * 0.15; + float3 diffuse = input.Color * ndotl * 0.85; return float4(ambient + diffuse, 1.0); } @@ -176,14 +274,12 @@ internal unsafe class MeshShadingRenderer : IRenderer private readonly Buffer vertexBuffer; private readonly Buffer indexBuffer; - private readonly Buffer meshletBuffer; - private readonly Buffer constantBuffer; + private readonly Buffer constantsBuffer; private readonly ResourceLayout resourceLayout; private readonly ResourceTable resourceTable; private readonly MeshShadingPipeline pipeline; - private readonly uint meshletCount; - private float rotationAngle; + private float totalTime; public MeshShadingRenderer() { @@ -192,109 +288,87 @@ internal unsafe class MeshShadingRenderer : IRenderer throw new NotSupportedException("Mesh shading is not supported on this device."); } - Vertex[] cubeVertices = - [ - // Front face - new() { Position = new(-0.5f, -0.5f, 0.5f), Normal = new( 0, 0, 1), TexCoord = new(0, 1) }, - new() { Position = new( 0.5f, -0.5f, 0.5f), Normal = new( 0, 0, 1), TexCoord = new(1, 1) }, - new() { Position = new( 0.5f, 0.5f, 0.5f), Normal = new( 0, 0, 1), TexCoord = new(1, 0) }, - new() { Position = new(-0.5f, 0.5f, 0.5f), Normal = new( 0, 0, 1), TexCoord = new(0, 0) }, - - // Back face - new() { Position = new( 0.5f, -0.5f, -0.5f), Normal = new( 0, 0, -1), TexCoord = new(0, 1) }, - new() { Position = new(-0.5f, -0.5f, -0.5f), Normal = new( 0, 0, -1), TexCoord = new(1, 1) }, - new() { Position = new(-0.5f, 0.5f, -0.5f), Normal = new( 0, 0, -1), TexCoord = new(1, 0) }, - new() { Position = new( 0.5f, 0.5f, -0.5f), Normal = new( 0, 0, -1), TexCoord = new(0, 0) }, - - // Left face - new() { Position = new(-0.5f, -0.5f, -0.5f), Normal = new(-1, 0, 0), TexCoord = new(0, 1) }, - new() { Position = new(-0.5f, -0.5f, 0.5f), Normal = new(-1, 0, 0), TexCoord = new(1, 1) }, - new() { Position = new(-0.5f, 0.5f, 0.5f), Normal = new(-1, 0, 0), TexCoord = new(1, 0) }, - new() { Position = new(-0.5f, 0.5f, -0.5f), Normal = new(-1, 0, 0), TexCoord = new(0, 0) }, - - // Right face - new() { Position = new( 0.5f, -0.5f, 0.5f), Normal = new( 1, 0, 0), TexCoord = new(0, 1) }, - new() { Position = new( 0.5f, -0.5f, -0.5f), Normal = new( 1, 0, 0), TexCoord = new(1, 1) }, - new() { Position = new( 0.5f, 0.5f, -0.5f), Normal = new( 1, 0, 0), TexCoord = new(1, 0) }, - new() { Position = new( 0.5f, 0.5f, 0.5f), Normal = new( 1, 0, 0), TexCoord = new(0, 0) }, - - // Top face - new() { Position = new(-0.5f, 0.5f, 0.5f), Normal = new( 0, 1, 0), TexCoord = new(0, 1) }, - new() { Position = new( 0.5f, 0.5f, 0.5f), Normal = new( 0, 1, 0), TexCoord = new(1, 1) }, - new() { Position = new( 0.5f, 0.5f, -0.5f), Normal = new( 0, 1, 0), TexCoord = new(1, 0) }, - new() { Position = new(-0.5f, 0.5f, -0.5f), Normal = new( 0, 1, 0), TexCoord = new(0, 0) }, - - // Bottom face - new() { Position = new(-0.5f, -0.5f, -0.5f), Normal = new( 0, -1, 0), TexCoord = new(0, 1) }, - new() { Position = new( 0.5f, -0.5f, -0.5f), Normal = new( 0, -1, 0), TexCoord = new(1, 1) }, - new() { Position = new( 0.5f, -0.5f, 0.5f), Normal = new( 0, -1, 0), TexCoord = new(1, 0) }, - new() { Position = new(-0.5f, -0.5f, 0.5f), Normal = new( 0, -1, 0), TexCoord = new(0, 0) } - ]; - - Triangle[] cubeTriangles = - [ - // Front face - new() { I0 = 0, I1 = 1, I2 = 2 }, - new() { I0 = 0, I1 = 2, I2 = 3 }, - // Back face - new() { I0 = 4, I1 = 5, I2 = 6 }, - new() { I0 = 4, I1 = 6, I2 = 7 }, - // Left face - new() { I0 = 8, I1 = 9, I2 = 10 }, - new() { I0 = 8, I1 = 10, I2 = 11 }, - // Right face - new() { I0 = 12, I1 = 13, I2 = 14 }, - new() { I0 = 12, I1 = 14, I2 = 15 }, - // Top face - new() { I0 = 16, I1 = 17, I2 = 18 }, - new() { I0 = 16, I1 = 18, I2 = 19 }, - // Bottom face - new() { I0 = 20, I1 = 21, I2 = 22 }, - new() { I0 = 20, I1 = 22, I2 = 23 } - ]; - - Meshlet[] meshlets = - [ - new() + const int lonSegments = 12; + const int latSegments = 6; + const float radius = 0.5f; + + List sphereVertices = []; + List sphereTriangles = []; + + sphereVertices.Add(new() { Position = new(0, radius, 0), Normal = Vector3.UnitY }); + + for (int lat = 1; lat < latSegments; lat++) + { + float phi = MathF.PI * lat / latSegments; + float sinPhi = MathF.Sin(phi); + float cosPhi = MathF.Cos(phi); + + for (int lon = 0; lon < lonSegments; lon++) { - VertexOffset = 0, - VertexCount = (uint)cubeVertices.Length, - PrimitiveOffset = 0, - PrimitiveCount = (uint)cubeTriangles.Length + float theta = 2.0f * MathF.PI * lon / lonSegments; + Vector3 normal = new(sinPhi * MathF.Cos(theta), cosPhi, sinPhi * MathF.Sin(theta)); + + sphereVertices.Add(new() { Position = normal * radius, Normal = normal }); + } + } + + sphereVertices.Add(new() { Position = new(0, -radius, 0), Normal = -Vector3.UnitY }); + + for (int lon = 0; lon < lonSegments; lon++) + { + uint next = (uint)((lon + 1) % lonSegments); + + sphereTriangles.Add(new() { Index0 = 0, Index1 = (uint)(1 + lon), Index2 = 1 + next }); + } + + for (int lat = 0; lat < latSegments - 2; lat++) + { + for (int lon = 0; lon < lonSegments; lon++) + { + uint next = (uint)((lon + 1) % lonSegments); + uint tl = (uint)(1 + (lat * lonSegments) + lon); + uint tr = (uint)(1 + (lat * lonSegments)) + next; + uint bl = (uint)(1 + ((lat + 1) * lonSegments) + lon); + uint br = (uint)(1 + ((lat + 1) * lonSegments)) + next; + + sphereTriangles.Add(new() { Index0 = tl, Index1 = bl, Index2 = tr }); + sphereTriangles.Add(new() { Index0 = tr, Index1 = bl, Index2 = br }); } - ]; - meshletCount = (uint)meshlets.Length; + } + + uint bottomPole = (uint)(sphereVertices.Count - 1); + uint lastRing = 1 + ((latSegments - 2) * lonSegments); + + for (int lon = 0; lon < lonSegments; lon++) + { + uint next = (uint)((lon + 1) % lonSegments); + + sphereTriangles.Add(new() { Index0 = bottomPole, Index1 = lastRing + next, Index2 = lastRing + (uint)lon }); + } + + Vertex[] vertexData = [.. sphereVertices]; + Triangle[] triangleData = [.. sphereTriangles]; - // Create vertex buffer vertexBuffer = App.Context.CreateBuffer(new() { - SizeInBytes = (uint)(sizeof(Vertex) * cubeVertices.Length), + SizeInBytes = (uint)(sizeof(Vertex) * vertexData.Length), StrideInBytes = (uint)sizeof(Vertex), Flags = BufferUsageFlags.ShaderResource }); - vertexBuffer.Upload(cubeVertices, 0); + vertexBuffer.Upload(vertexData, 0); - // Create index buffer (Triangle struct per triangle) indexBuffer = App.Context.CreateBuffer(new() { - SizeInBytes = (uint)(sizeof(Triangle) * cubeTriangles.Length), + SizeInBytes = (uint)(sizeof(Triangle) * triangleData.Length), StrideInBytes = (uint)sizeof(Triangle), Flags = BufferUsageFlags.ShaderResource }); - indexBuffer.Upload(cubeTriangles, 0); + indexBuffer.Upload(triangleData, 0); - meshletBuffer = App.Context.CreateBuffer(new() + constantsBuffer = App.Context.CreateBuffer(new() { - SizeInBytes = (uint)(sizeof(Meshlet) * meshlets.Length), - StrideInBytes = (uint)sizeof(Meshlet), - Flags = BufferUsageFlags.ShaderResource - }); - meshletBuffer.Upload(meshlets, 0); - - constantBuffer = App.Context.CreateBuffer(new() - { - SizeInBytes = (uint)sizeof(TransformConstants), - StrideInBytes = (uint)sizeof(TransformConstants), + SizeInBytes = (uint)sizeof(Constants), + StrideInBytes = (uint)sizeof(Constants), Flags = BufferUsageFlags.Constant | BufferUsageFlags.MapWrite }); @@ -302,8 +376,7 @@ internal unsafe class MeshShadingRenderer : IRenderer { Bindings = BindingHelper.Bindings ( - new() { Type = ResourceType.ConstantBuffer, Count = 1, StageFlags = ShaderStageFlags.Mesh }, - new() { Type = ResourceType.StructuredBuffer, Count = 1, StageFlags = ShaderStageFlags.Mesh }, + new() { Type = ResourceType.ConstantBuffer, Count = 1, StageFlags = ShaderStageFlags.Amplification | ShaderStageFlags.Mesh | ShaderStageFlags.Pixel }, new() { Type = ResourceType.StructuredBuffer, Count = 1, StageFlags = ShaderStageFlags.Mesh }, new() { Type = ResourceType.StructuredBuffer, Count = 1, StageFlags = ShaderStageFlags.Mesh } ) @@ -312,9 +385,10 @@ internal unsafe class MeshShadingRenderer : IRenderer resourceTable = App.Context.CreateResourceTable(new() { Layout = resourceLayout, - Resources = [constantBuffer, vertexBuffer, indexBuffer, meshletBuffer] + Resources = [constantsBuffer, vertexBuffer, indexBuffer] }); + using Shader ampShader = App.Context.LoadShaderFromSource(ShaderSource, "ASMain", ShaderStageFlags.Amplification); using Shader meshShader = App.Context.LoadShaderFromSource(ShaderSource, "MSMain", ShaderStageFlags.Mesh); using Shader pixelShader = App.Context.LoadShaderFromSource(ShaderSource, "PSMain", ShaderStageFlags.Pixel); @@ -326,13 +400,16 @@ internal unsafe class MeshShadingRenderer : IRenderer DepthStencilState = DepthStencilStates.Default, BlendState = BlendStates.Opaque }, - Amplification = null, + Amplification = ampShader, Mesh = meshShader, Pixel = pixelShader, ResourceLayout = resourceLayout, PrimitiveTopology = PrimitiveTopology.TriangleList, - Output = App.SwapChain.FrameBuffer.Output, - MeshThreadGroupSizeX = MaxPrimitives, + Output = App.FrameBuffer.Output, + AmplificationThreadGroupSizeX = ASGroupSize, + AmplificationThreadGroupSizeY = 1, + AmplificationThreadGroupSizeZ = 1, + MeshThreadGroupSizeX = MeshGroupSize, MeshThreadGroupSizeY = 1, MeshThreadGroupSizeZ = 1 }); @@ -340,22 +417,37 @@ internal unsafe class MeshShadingRenderer : IRenderer public void Update(double deltaTime) { - rotationAngle += (float)deltaTime; + totalTime += (float)deltaTime; + + float angle = totalTime * 0.3f; + + Vector3 cameraPos = new(35.0f * MathF.Sin(angle), 20.0f * MathF.Sin(totalTime * 0.2f), 35.0f * MathF.Cos(angle)); + + Matrix4x4 view = Matrix4x4.CreateLookAt(cameraPos, Vector3.Zero, Vector3.UnitY); + Matrix4x4 projection = Matrix4x4.CreatePerspectiveFieldOfView(float.DegreesToRadians(45.0f), (float)App.Width / App.Height, 0.1f, 200.0f); + Matrix4x4 viewProjection = view * projection; + + constantsBuffer.Upload([new Constants() + { + ViewProjection = viewProjection, + FrustumPlane0 = NormalizePlane(new(viewProjection.M11 + viewProjection.M14, viewProjection.M21 + viewProjection.M24, viewProjection.M31 + viewProjection.M34, viewProjection.M41 + viewProjection.M44)), + FrustumPlane1 = NormalizePlane(new(viewProjection.M14 - viewProjection.M11, viewProjection.M24 - viewProjection.M21, viewProjection.M34 - viewProjection.M31, viewProjection.M44 - viewProjection.M41)), + FrustumPlane2 = NormalizePlane(new(viewProjection.M12 + viewProjection.M14, viewProjection.M22 + viewProjection.M24, viewProjection.M32 + viewProjection.M34, viewProjection.M42 + viewProjection.M44)), + FrustumPlane3 = NormalizePlane(new(viewProjection.M14 - viewProjection.M12, viewProjection.M24 - viewProjection.M22, viewProjection.M34 - viewProjection.M32, viewProjection.M44 - viewProjection.M42)), + FrustumPlane4 = NormalizePlane(new(viewProjection.M13, viewProjection.M23, viewProjection.M33, viewProjection.M43)), + FrustumPlane5 = NormalizePlane(new(viewProjection.M14 - viewProjection.M13, viewProjection.M24 - viewProjection.M23, viewProjection.M34 - viewProjection.M33, viewProjection.M44 - viewProjection.M43)), + Time = totalTime, + LightDirection = -Vector3.Normalize(cameraPos) + }], 0); } public void Render() { - Matrix4x4 model = Matrix4x4.CreateRotationY(rotationAngle) * Matrix4x4.CreateRotationX(rotationAngle * 0.5f); - Matrix4x4 view = Matrix4x4.CreateLookAt(new(0, 0, 3), Vector3.Zero, Vector3.UnitY); - Matrix4x4 projection = Matrix4x4.CreatePerspectiveFieldOfView(float.DegreesToRadians(45.0f), (float)App.Width / App.Height, 0.1f, 100.0f); - - constantBuffer.Upload([new TransformConstants() { MVP = model * view * projection }], 0); - CommandBuffer commandBuffer = App.Context.Graphics.CommandBuffer(); - commandBuffer.BeginRenderPass(App.SwapChain.FrameBuffer, new() + commandBuffer.BeginRenderPass(App.FrameBuffer, new() { - ColorValues = [new(0.1f, 0.1f, 0.1f, 1.0f)], + ColorValues = [new(0.05f, 0.05f, 0.08f, 1.0f)], Depth = 1.0f, Stencil = 0, Flags = ClearFlags.All @@ -363,7 +455,7 @@ internal unsafe class MeshShadingRenderer : IRenderer commandBuffer.SetPipeline(pipeline); commandBuffer.SetResourceTable(resourceTable); - commandBuffer.DispatchMesh(meshletCount, 1, 1); + commandBuffer.DispatchMesh(DispatchGroupCount, 1, 1); commandBuffer.EndRenderPass(); @@ -379,17 +471,18 @@ internal unsafe class MeshShadingRenderer : IRenderer pipeline.Dispose(); resourceTable.Dispose(); resourceLayout.Dispose(); - constantBuffer.Dispose(); - meshletBuffer.Dispose(); + constantsBuffer.Dispose(); indexBuffer.Dispose(); vertexBuffer.Dispose(); } + + private static Vector4 NormalizePlane(Vector4 plane) + { + return plane / new Vector3(plane.X, plane.Y, plane.Z).Length(); + } } -/// -/// Vertex structure with position and normal. -/// -[StructLayout(LayoutKind.Explicit, Size = 48)] +[StructLayout(LayoutKind.Explicit, Size = 32)] file struct Vertex { [FieldOffset(0)] @@ -397,54 +490,50 @@ file struct Vertex [FieldOffset(16)] public Vector3 Normal; - - [FieldOffset(32)] - public Vector2 TexCoord; } -/// -/// Triangle indices for mesh shading. -/// [StructLayout(LayoutKind.Explicit, Size = 16)] file struct Triangle { [FieldOffset(0)] - public uint I0; + public uint Index0; [FieldOffset(4)] - public uint I1; + public uint Index1; [FieldOffset(8)] - public uint I2; + public uint Index2; } -/// -/// Meshlet structure defining a chunk of geometry. -/// -[StructLayout(LayoutKind.Explicit, Size = 16)] -file struct Meshlet +[StructLayout(LayoutKind.Explicit, Size = 176)] +file struct Constants { [FieldOffset(0)] - public uint VertexOffset; + public Matrix4x4 ViewProjection; - [FieldOffset(4)] - public uint VertexCount; + [FieldOffset(64)] + public Vector4 FrustumPlane0; - [FieldOffset(8)] - public uint PrimitiveOffset; + [FieldOffset(80)] + public Vector4 FrustumPlane1; - [FieldOffset(12)] - public uint PrimitiveCount; -} + [FieldOffset(96)] + public Vector4 FrustumPlane2; -/// -/// Transform constants for the mesh. -/// -[StructLayout(LayoutKind.Explicit, Size = 64)] -file struct TransformConstants -{ - [FieldOffset(0)] - public Matrix4x4 MVP; + [FieldOffset(112)] + public Vector4 FrustumPlane3; + + [FieldOffset(128)] + public Vector4 FrustumPlane4; + + [FieldOffset(144)] + public Vector4 FrustumPlane5; + + [FieldOffset(160)] + public float Time; + + [FieldOffset(164)] + public Vector3 LightDirection; } ``` @@ -457,8 +546,6 @@ using ZenithTutorials; using ZenithTutorials.Renderers; App.Run(); - -App.Cleanup(); ``` Run the application: @@ -484,136 +571,158 @@ if (!App.Context.Capabilities.MeshShadingSupported) Always check `Capabilities.MeshShadingSupported` before using mesh shading features. -### Meshlet Data Structure +### Procedural Sphere Generation + +The renderer generates a UV sphere with 12 longitude segments and 6 latitude segments, producing 62 vertices and 120 triangles: ```csharp -[StructLayout(LayoutKind.Explicit, Size = 16)] -file struct Meshlet +sphereVertices.Add(new() { Position = new(0, radius, 0), Normal = Vector3.UnitY }); + +for (int lat = 1; lat < latSegments; lat++) { - [FieldOffset(0)] - public uint VertexOffset; + float phi = MathF.PI * lat / latSegments; + for (int lon = 0; lon < lonSegments; lon++) + { + float theta = 2.0f * MathF.PI * lon / lonSegments; + Vector3 normal = new(sinPhi * MathF.Cos(theta), cosPhi, sinPhi * MathF.Sin(theta)); - [FieldOffset(4)] - public uint VertexCount; + sphereVertices.Add(new() { Position = normal * radius, Normal = normal }); + } +} +``` - [FieldOffset(8)] - public uint PrimitiveOffset; +The sphere is constructed with: +- **Top pole** (1 vertex) +- **Latitude rings** (5 rings × 12 segments = 60 vertices) +- **Bottom pole** (1 vertex) - [FieldOffset(12)] - public uint PrimitiveCount; +Triangles connect the poles to the first/last rings with triangle fans, and connect adjacent rings with quad strips (2 triangles each). + +### Amplification Shader and Frustum Culling + +The amplification shader runs one thread per potential instance (1,000 total, dispatched in groups of 32): + +```slang +[shader("amplification")] +[numthreads(ASGroupSize, 1, 1)] +void ASMain(uint groupID: SV_GroupID, uint groupThreadID: SV_GroupThreadID) +{ + uint instanceIndex = groupID * ASGroupSize + groupThreadID; + + bool visible = false; + if (instanceIndex < TotalInstances) + { + float3 worldPos = InstancePosition(instanceIndex); + visible = !IsFrustumCulled(worldPos, BoundingSphereRadius); + } + ... + DispatchMesh(s_visibleCount, 1, 1, s_payload); } ``` -Each meshlet describes a chunk of geometry: -- **VertexOffset/Count**: Range of vertices in the vertex buffer -- **PrimitiveOffset/Count**: Range of triangles in the index buffer +Key elements: +- **`IsFrustumCulled`** tests each instance's bounding sphere against 6 frustum planes. If the sphere is completely behind any plane, it's culled. +- **`InterlockedAdd`** atomically collects visible instance indices into shared memory (`s_payload`). +- **`GroupMemoryBarrierWithGroupSync`** synchronizes threads before reading/writing shared memory. +- **`DispatchMesh`** dispatches exactly `s_visibleCount` mesh shader workgroups — one per visible instance. + +### Mesh Shader -### Mesh Shader Entry Point +Each mesh shader workgroup processes one visible sphere instance: ```slang [shader("mesh")] -[numthreads(MaxPrimitives, 1, 1)] +[numthreads(120, 1, 1)] [outputtopology("triangle")] -void MSMain(in uint groupID : SV_GroupID, - in uint groupThreadID : SV_GroupThreadID, - OutputVertices outVertices, - OutputIndices outIndices) +void MSMain(uint groupID: SV_GroupID, uint groupThreadID: SV_GroupThreadID, + in payload Payload meshPayload, ...) +{ + uint instanceIndex = meshPayload.InstanceIndices[groupID]; + float3 instancePos = InstancePosition(instanceIndex); + + SetMeshOutputCounts(SphereVertexCount, SphereTriangleCount); + + if (groupThreadID < SphereVertexCount) + { + Vertex v = vertices[groupThreadID]; + float3 worldPos = v.Position + instancePos; + ... + } +} ``` -Key attributes: +Key elements: +- **120 threads** matches the maximum triangle count (one thread per triangle) +- **`SetMeshOutputCounts`** declares the exact number of output vertices and primitives +- Threads with index < 62 write vertices; threads with index < 120 write triangle indices +- The `payload` struct carries visible instance indices from the amplification shader -| Attribute | Description | -|-----------|-------------| -| `[shader("mesh")]` | Marks this as a mesh shader entry point | -| `[numthreads(X,Y,Z)]` | Thread group size (typically MaxPrimitives threads) | -| `[outputtopology("triangle")]` | Output primitive type | -| `OutputVertices` | Output vertex array (max N vertices) | -| `OutputIndices` | Output index array (max N triangles) | +### Camera and Frustum Planes -### Setting Output Counts +```csharp +Matrix4x4 viewProjection = view * projection; -```slang -SetMeshOutputCounts(meshlet.VertexCount, meshlet.PrimitiveCount); +constantsBuffer.Upload([new Constants() +{ + FrustumPlane0 = NormalizePlane(new(...)) + FrustumPlane1 = NormalizePlane(new(...)) + FrustumPlane2 = NormalizePlane(new(...)) + FrustumPlane3 = NormalizePlane(new(...)) + FrustumPlane4 = NormalizePlane(new(...)) + FrustumPlane5 = NormalizePlane(new(...)) + ... +}], 0); ``` -This must be called once per workgroup to declare how many vertices and primitives will be output. - -### Dispatching Mesh Shading +Six frustum planes are extracted from the combined view-projection matrix using the Gribb/Hartmann method. Each plane is normalized so the distance test in the shader is accurate: ```csharp -commandBuffer.DispatchMesh(meshletCount, 1, 1); +private static Vector4 NormalizePlane(Vector4 plane) +{ + return plane / new Vector3(plane.X, plane.Y, plane.Z).Length(); +} ``` -Unlike traditional `Draw` calls, mesh shading uses `DispatchMesh(X, Y, Z)` to launch workgroups: -- Each workgroup processes one meshlet -- Total workgroups = `meshletCount × 1 × 1` - -### Creating the Pipeline +### Pipeline Configuration ```csharp pipeline = App.Context.CreateMeshShadingPipeline(new() { RenderStates = new() { ... }, - Amplification = null, + Amplification = ampShader, Mesh = meshShader, Pixel = pixelShader, ResourceLayout = resourceLayout, PrimitiveTopology = PrimitiveTopology.TriangleList, - Output = App.SwapChain.FrameBuffer.Output, - MeshThreadGroupSizeX = MaxPrimitives, - MeshThreadGroupSizeY = 1, - MeshThreadGroupSizeZ = 1 + Output = App.FrameBuffer.Output, + AmplificationThreadGroupSizeX = ASGroupSize, + MeshThreadGroupSizeX = MeshGroupSize, + ... }); ``` The `MeshShadingPipelineDesc` requires: -- `Amplification` - The compiled amplification shader (optional) -- `Mesh` - The compiled mesh shader -- `Pixel` - The compiled pixel shader -- `ResourceLayout` - Resource bindings (same as graphics pipelines) -- `AmplificationThreadGroupSizeX/Y/Z` - Must match `[numthreads()]` in the amplification shader (if used) -- `MeshThreadGroupSizeX/Y/Z` - Must match `[numthreads()]` in the mesh shader - -## Amplification Shader (Optional) - -For more advanced scenarios, you can add an amplification shader to dynamically control meshlet dispatch: - -```slang -struct AmplificationPayload -{ - uint MeshletIndices[32]; -}; +- `Amplification` and `Mesh` shaders (plus optional `Pixel`) +- `AmplificationThreadGroupSizeX/Y/Z` must match `[numthreads()]` in the amplification shader +- `MeshThreadGroupSizeX/Y/Z` must match `[numthreads()]` in the mesh shader +- All three shaders share the same `ResourceLayout` -[shader("amplification")] -[numthreads(32, 1, 1)] -void ASMain(in uint groupID : SV_GroupID, - in uint groupThreadID : SV_GroupThreadID) -{ - // Frustum culling, LOD selection, etc. - bool visible = /* culling logic */; +### Dispatching - if (visible) - { - AmplificationPayload payload; - payload.MeshletIndices[groupThreadID] = groupID * 32 + groupThreadID; - - // Dispatch mesh shading workgroups - DispatchMesh(visibleCount, 1, 1, payload); - } -} +```csharp +commandBuffer.DispatchMesh(DispatchGroupCount, 1, 1); ``` -## Best Practices - -1. **Meshlet Size**: Keep meshlets within hardware limits (typically 64-256 vertices, 64-126 primitives) -2. **Thread Utilization**: Size `numthreads` to match your maximum primitive count -3. **Early Out**: Check thread bounds before writing to output arrays -4. **Preprocessing**: Generate meshlets offline for complex models -5. **Culling**: Use amplification shaders for GPU-driven culling +`DispatchGroupCount = (1000 + 32 - 1) / 32 = 32` groups are dispatched initially. Each group runs 32 amplification threads that cull instances and selectively dispatch mesh shader workgroups for visible instances only. ## Next Steps -Congratulations! You've completed all Zenith.NET tutorials. +Congratulations! You've completed all Zenith.NET tutorials. Here are some ideas to explore further: + +- Modify the grid size or spacing to render more instances +- Add LOD selection in the amplification shader based on distance +- Try different procedural geometries (torus, icosphere, etc.) +- Implement occlusion culling alongside frustum culling ## Source Code diff --git a/documents/tutorials/advanced/ray-tracing.md b/documents/tutorials/advanced/ray-tracing.md index 3095dcb6..e158faa2 100644 --- a/documents/tutorials/advanced/ray-tracing.md +++ b/documents/tutorials/advanced/ray-tracing.md @@ -1,6 +1,6 @@ # Ray Tracing -In this tutorial, you'll learn how to use hardware-accelerated ray tracing with Zenith.NET. We'll render a scene with a checkered floor and a sphere, demonstrating acceleration structure construction and compute-shader-based ray tracing with `RayQuery`. +In this tutorial, you'll learn how to use hardware-accelerated ray tracing with Zenith.NET. We'll render a scene with a checkered floor and colored spheres, featuring an animated camera, soft shadows, reflections, and ACES tonemapping — all driven by a compute shader with `RayQuery`. > [!NOTE] > This tutorial requires a GPU with ray tracing support. Check `Context.Capabilities.RayTracingSupported` before using ray tracing features. @@ -10,11 +10,12 @@ In this tutorial, you'll learn how to use hardware-accelerated ray tracing with We'll create a `RayTracingRenderer` class that: - Creates floor geometry using triangle buffers -- Creates a sphere using procedural AABBs -- Builds separate BLAS for floor and sphere, combined in a TLAS +- Creates spheres using procedural AABBs +- Builds separate BLAS for floor and spheres, combined in a TLAS - Uses a compute shader with `RayQuery` for ray tracing -- Implements shadow rays for hard shadows -- Copies the result to the swap chain for display +- Implements soft shadows, reflections, and Fresnel effects +- Applies ACES tonemapping for cinematic output +- Animates the camera orbit around the scene ## Key Concepts @@ -39,14 +40,9 @@ Ray tracing uses a two-level acceleration structure hierarchy: ``` TLAS (scene) ├── Instance 0 → BLAS 0 (floor, triangles) -├── Instance 1 → BLAS 1 (sphere, AABBs) -├── Instance 2 → BLAS 0 (same geometry, different transform) -└── ... +└── Instance 1 → BLAS 1 (spheres, AABBs) ``` -> [!IMPORTANT] -> Acceleration structure transforms only support rotation and scale. Translation is **not supported** - use the geometry's world-space coordinates directly. - ## The Renderer Class Create a new file `Renderers/RayTracingRenderer.cs`: @@ -59,47 +55,224 @@ internal unsafe class RayTracingRenderer : IRenderer private const uint ThreadGroupSize = 16; private const string ShaderSource = """ + static const float RayEpsilon = 0.001; + static const float TwoPi = 6.2831853; + static const uint SphereCount = 3; + static const uint ShadowSamples = 6; + static const uint ReflectionSamples = 4; + static const float ShadowMin = 0.3; + static const float SunRadius = 0.04; + static const float SphereRoughness = 0.05; + static const float SphereF0 = 0.15; + static const float FloorFadeStart = 8.0; + static const float FloorFadeRange = 20.0; + + static const float3 FloorNormal = float3(0.0, 1.0, 0.0); + static const float3 LightDir = float3(0.6667, 0.6667, -0.3333); + static const float3 LightColor = float3(1.0, 0.98, 0.95); + static const float3 AmbientColor = float3(0.15, 0.15, 0.2); + + struct Constants + { + private float4 PositionAndPadding; + + property float3 Position + { + get { + return PositionAndPadding.xyz; + } + } + }; + struct Sphere { private float4 CenterAndRadius; private float4 ColorAndPadding; - property float3 Center { get { return CenterAndRadius.xyz; } } + property float3 Center + { + get { + return CenterAndRadius.xyz; + } + } - property float Radius { get { return CenterAndRadius.w; } } + property float Radius + { + get { + return CenterAndRadius.w; + } + } - property float3 Color { get { return ColorAndPadding.xyz; } } + property float3 Color + { + get { + return ColorAndPadding.xyz; + } + } }; RaytracingAccelerationStructure scene; + ConstantBuffer constants; StructuredBuffer spheres; RWTexture2D outputTexture; - static const float3 LightDir = normalize(float3(1.0, 1.0, -0.5)); - static const float3 LightColor = float3(1.0, 0.98, 0.95); - static const float3 AmbientColor = float3(0.1, 0.1, 0.15); + float3 SampleSky(float3 direction) + { + float t = 0.5 * (direction.y + 1.0); + float3 horizon = float3(0.7, 0.85, 1.0); + float3 zenith = float3(0.3, 0.5, 1.0); + float3 sky = lerp(horizon, zenith, saturate(t)); + + float sunDot = dot(direction, LightDir); + sky += LightColor * smoothstep(0.995, 0.999, sunDot) * 3.0; + + return sky; + } + + float3 ACESFilm(float3 x) + { + x *= 1.6; + float3 a = x * (x * 2.51 + 0.03); + float3 b = x * (x * 2.43 + 0.59) + 0.14; + float3 result = saturate(a / b); + + float luma = dot(result, float3(0.2126, 0.7152, 0.0722)); + result = saturate(lerp(float3(luma, luma, luma), result, 1.5)); + + return result; + } + + float SchlickFresnel(float cosTheta, float f0) + { + return f0 + (1.0 - f0) * pow(1.0 - cosTheta, 5.0); + } + + float3 ShadeCheckerboard(float3 hitPoint, float3 normal, float3 rayDirection, bool softShadow, out float shadow) + { + float2 fw = max(abs(fwidth_approx(hitPoint.xz)), 0.001); + float2 fractPos = fract(hitPoint.xz) - 0.5; + float2 filtered = clamp(fractPos / fw, -0.5, 0.5); + float checker = 0.5 - 0.5 * filtered.x * filtered.y; + float3 baseColor = lerp(float3(0.787, 0.787, 0.787), float3(0.1, 0.1, 0.1), checker); + + float NdotL = max(dot(normal, LightDir), 0.0); + float3 shadowOrigin = hitPoint + normal * RayEpsilon; + shadow = softShadow ? + lerp(ShadowMin, 1.0, TraceSoftShadow(shadowOrigin, LightDir, hitPoint.xz * 100.0)) : + (TraceShadowRay(shadowOrigin, LightDir) ? ShadowMin : 1.0); + + float3 litColor = baseColor * AmbientColor + baseColor * LightColor * NdotL * shadow; + + float ao = 1.0; + for (uint i = 0; i < SphereCount; i++) + { + float3 toSphere = spheres[i].Center - hitPoint; + float horizDist = length(toSphere.xz); + float r = spheres[i].Radius; + float occl = saturate(1.0 - horizDist / (r * 2.0)); + float hFactor = saturate(1.0 - toSphere.y / (r * 3.0)); + ao -= occl * hFactor * 0.4; + } + + litColor *= max(ao, 0.3); + + float dist = length(hitPoint.xz); + float fade = saturate((dist - FloorFadeStart) / FloorFadeRange); + return lerp(litColor, SampleSky(rayDirection), fade); + } + + float3 ShadeSphere(float3 hitPoint, float3 normal, float3 sphereColor, float3 viewDir, bool softShadow) + { + float NdotL = max(dot(normal, LightDir), 0.0); + + float3 halfDir = normalize(LightDir + viewDir); + float spec = pow(max(dot(normal, halfDir), 0.0), 64.0); + + float3 shadowOrigin = hitPoint + normal * RayEpsilon; + float shadow = softShadow ? + lerp(ShadowMin, 1.0, TraceSoftShadow(shadowOrigin, LightDir, hitPoint.xz * 100.0)) : + (TraceShadowRay(shadowOrigin, LightDir) ? ShadowMin : 1.0); + + float3 diffuse = sphereColor * LightColor * NdotL * shadow; + float3 specular = LightColor * spec * shadow; + float3 ambient = sphereColor * AmbientColor; + + return ambient + diffuse + specular; + } + + float3 TraceReflection(float3 origin, float3 direction) + { + RayDesc reflectRay; + reflectRay.Origin = origin; + reflectRay.Direction = direction; + reflectRay.TMin = RayEpsilon; + reflectRay.TMax = 1000.0; + + float3 sphereNormal = float3(0.0); + float3 sphereColor = float3(0.0); + + RayQuery query; + query.TraceRayInline(scene, RAY_FLAG_NONE, 0xFF, reflectRay); + + while (query.Proceed()) + { + if (query.CandidateType() == CANDIDATE_PROCEDURAL_PRIMITIVE) + { + uint sphereIndex = query.CandidatePrimitiveIndex(); + Sphere sphere = spheres[sphereIndex]; + + float3 ro = query.CandidateObjectRayOrigin(); + float3 rd = query.CandidateObjectRayDirection(); + + float t = IntersectSphere(ro, rd, sphere); + + if (t >= query.RayTMin() && t <= query.CommittedRayT()) + { + float3 hitPoint = ro + rd * t; + sphereNormal = normalize(hitPoint - sphere.Center); + sphereColor = sphere.Color; + query.CommitProceduralPrimitiveHit(t); + } + } + } + + if (query.CommittedStatus() == COMMITTED_TRIANGLE_HIT) + { + float3 hitPoint = reflectRay.Origin + reflectRay.Direction * query.CommittedRayT(); + float unused; + return ShadeCheckerboard(hitPoint, FloorNormal, reflectRay.Direction, false, unused); + } + else if (query.CommittedStatus() == COMMITTED_PROCEDURAL_PRIMITIVE_HIT) + { + float3 hitPoint = reflectRay.Origin + reflectRay.Direction * query.CommittedRayT(); + float3 viewDir = normalize(origin - hitPoint); + return ShadeSphere(hitPoint, sphereNormal, sphereColor, viewDir, false); + } + else + { + return SampleSky(direction); + } + } float IntersectSphere(float3 origin, float3 direction, Sphere sphere) { float3 oc = origin - sphere.Center; - - float a = dot(direction, direction); float b = dot(oc, direction); float c = dot(oc, oc) - sphere.Radius * sphere.Radius; - float discriminant = b * b - a * c; + float discriminant = b * b - c; if (discriminant > 0.0) { float sqrtD = sqrt(discriminant); - float t1 = (-b - sqrtD) / a; + float t1 = -b - sqrtD; if (t1 > 0.0) { return t1; } - float t2 = (-b + sqrtD) / a; + float t2 = -b + sqrtD; if (t2 > 0.0) { @@ -110,12 +283,26 @@ internal unsafe class RayTracingRenderer : IRenderer return -1.0; } + float2 fwidth_approx(float2 p) + { + float2 dx = float2(0.02, 0.0); + float2 dy = float2(0.0, 0.02); + return abs(fract(p + dx) - fract(p)) + abs(fract(p + dy) - fract(p)); + } + + float Hash(float2 p) + { + float3 p3 = fract(float3(p.xyx) * 0.1031); + p3 += dot(p3, p3.yzx + 33.33); + return fract((p3.x + p3.y) * p3.z); + } + bool TraceShadowRay(float3 origin, float3 direction) { RayDesc shadowRay; shadowRay.Origin = origin; shadowRay.Direction = direction; - shadowRay.TMin = 0.001; + shadowRay.TMin = RayEpsilon; shadowRay.TMax = 1000.0; RayQuery shadowQuery; @@ -143,8 +330,81 @@ internal unsafe class RayTracingRenderer : IRenderer return shadowQuery.CommittedStatus() != COMMITTED_NOTHING; } + float TraceSoftShadow(float3 origin, float3 direction, float2 pixelSeed) + { + float3 tangent = normalize(cross(direction, float3(0.0, 1.0, 0.0))); + float3 bitangent = cross(direction, tangent); + + float lit = 0.0; + for (uint i = 0; i < ShadowSamples; i++) + { + float h = Hash(pixelSeed + float2(float(i) * 7.13, float(i) * 3.71)); + float angle = (float(i) + h) * (TwoPi / float(ShadowSamples)); + float radius = sqrt(Hash(pixelSeed + float2(float(i) * 11.07, 0.0))) * SunRadius; + float3 jitteredDir = normalize(direction + tangent * cos(angle) * radius + bitangent * sin(angle) * radius); + + if (!TraceShadowRay(origin, jitteredDir)) + { + lit += 1.0; + } + } + + return lit / float(ShadowSamples); + } + + float3 TraceRoughReflection(float3 origin, float3 reflectDir, float3 normal, float roughness, float2 pixelSeed) + { + float3 tangent = normalize(cross(reflectDir, normal)); + float3 bitangent = cross(reflectDir, tangent); + + float3 accum = float3(0.0); + for (uint i = 0; i < ReflectionSamples; i++) + { + float h1 = Hash(pixelSeed + float2(float(i) * 5.17, float(i) * 9.23)); + float h2 = Hash(pixelSeed + float2(float(i) * 13.37, float(i) * 2.91)); + float angle = h1 * TwoPi; + float radius = sqrt(h2) * roughness; + float3 jitteredDir = normalize(reflectDir + tangent * cos(angle) * radius + bitangent * sin(angle) * radius); + accum += TraceReflection(origin, jitteredDir); + } + + return accum / float(ReflectionSamples); + } + + float3 ShadeFloor(float3 hitPoint, float3 rayDir, float3 cameraPos) + { + float shadow; + float3 directColor = ShadeCheckerboard(hitPoint, FloorNormal, rayDir, true, shadow); + + float3 viewDir = normalize(cameraPos - hitPoint); + float3 halfDir = normalize(LightDir + viewDir); + float floorSpec = pow(max(dot(FloorNormal, halfDir), 0.0), 128.0); + float specDist = length(hitPoint.xz); + float specFade = 1.0 - saturate((specDist - FloorFadeStart) / FloorFadeRange); + directColor += LightColor * floorSpec * 0.4 * specFade * shadow; + + float3 reflectDir = reflect(rayDir, FloorNormal); + float3 reflectColor = TraceReflection(hitPoint + FloorNormal * RayEpsilon, reflectDir); + float fresnel = SchlickFresnel(max(dot(FloorNormal, viewDir), 0.0), 0.02); + + return lerp(directColor, reflectColor, fresnel); + } + + float3 ShadePrimarySphere(float3 hitPoint, float3 rayDir, float3 cameraPos, float3 normal, float3 sphereColor) + { + float3 viewDir = normalize(cameraPos - hitPoint); + + float3 directColor = ShadeSphere(hitPoint, normal, sphereColor, viewDir, true); + + float3 reflectDir = reflect(rayDir, normal); + float3 reflectColor = TraceRoughReflection(hitPoint + normal * RayEpsilon, reflectDir, normal, SphereRoughness, hitPoint.xz * 100.0); + float fresnel = SchlickFresnel(max(dot(normal, viewDir), 0.0), SphereF0); + + return lerp(directColor, reflectColor, fresnel); + } + [numthreads(16, 16, 1)] - void CSMain(uint3 dispatchThreadID : SV_DispatchThreadID) + void CSMain(uint3 dispatchThreadID: SV_DispatchThreadID) { uint2 pixelCoord = dispatchThreadID.xy; @@ -163,8 +423,8 @@ internal unsafe class RayTracingRenderer : IRenderer float aspectRatio = float(width) / float(height); float fov = tan(radians(45.0) * 0.5); - float3 cameraPos = float3(0.0, 4.0, -12.0); - float3 cameraTarget = float3(0.0, 0.0, 0.0); + float3 cameraPos = constants.Position; + float3 cameraTarget = float3(0.0, 0.5, 0.0); float3 cameraUp = float3(0.0, 1.0, 0.0); float3 forward = normalize(cameraTarget - cameraPos); @@ -176,7 +436,7 @@ internal unsafe class RayTracingRenderer : IRenderer RayDesc ray; ray.Origin = cameraPos; ray.Direction = rayDir; - ray.TMin = 0.001; + ray.TMin = RayEpsilon; ray.TMax = 1000.0; float3 sphereHitNormal = float3(0.0); @@ -214,48 +474,19 @@ internal unsafe class RayTracingRenderer : IRenderer if (query.CommittedStatus() == COMMITTED_TRIANGLE_HIT) { float3 hitPoint = ray.Origin + ray.Direction * query.CommittedRayT(); - - float scale = 1.0; - int checkX = int(floor(hitPoint.x * scale)); - int checkZ = int(floor(hitPoint.z * scale)); - bool isWhite = ((checkX + checkZ) & 1) == 0; - float3 baseColor = isWhite ? float3(0.9, 0.9, 0.9) : float3(0.2, 0.2, 0.2); - - float3 normal = float3(0.0, 1.0, 0.0); - float NdotL = max(dot(normal, LightDir), 0.0); - - float3 shadowOrigin = hitPoint + normal * 0.001; - bool inShadow = TraceShadowRay(shadowOrigin, LightDir); - - float shadow = inShadow ? 0.3 : 1.0; - float3 diffuse = baseColor * LightColor * NdotL * shadow; - float3 ambient = baseColor * AmbientColor; - - color = ambient + diffuse; + color = ShadeFloor(hitPoint, rayDir, cameraPos); } else if (query.CommittedStatus() == COMMITTED_PROCEDURAL_PRIMITIVE_HIT) { float3 hitPoint = ray.Origin + ray.Direction * query.CommittedRayT(); - - float NdotL = max(dot(sphereHitNormal, LightDir), 0.0); - - float3 shadowOrigin = hitPoint + sphereHitNormal * 0.001; - bool inShadow = TraceShadowRay(shadowOrigin, LightDir); - - float shadow = inShadow ? 0.3 : 1.0; - float3 diffuse = sphereHitColor * LightColor * NdotL * shadow; - float3 ambient = sphereHitColor * AmbientColor; - - color = ambient + diffuse; + color = ShadePrimarySphere(hitPoint, rayDir, cameraPos, sphereHitNormal, sphereHitColor); } else { - float t = 0.5 * (rayDir.y + 1.0); - - color = lerp(float3(1.0, 1.0, 1.0), float3(0.5, 0.7, 1.0), t); + color = SampleSky(rayDir); } - color = pow(color, 1.0 / 2.2); + color = ACESFilm(color); outputTexture[pixelCoord] = float4(color, 1.0); } @@ -263,15 +494,18 @@ internal unsafe class RayTracingRenderer : IRenderer private readonly Buffer floorVertexBuffer; private readonly Buffer floorIndexBuffer; - private readonly Buffer sphereBuffer; private readonly Buffer aabbBuffer; private readonly BottomLevelAccelerationStructure floorBlas; private readonly BottomLevelAccelerationStructure sphereBlas; private readonly TopLevelAccelerationStructure tlas; + private readonly Buffer constantsBuffer; + private readonly Buffer sphereBuffer; private readonly ResourceLayout resourceLayout; private readonly ComputePipeline pipeline; + private Texture? outputTexture; private ResourceTable? resourceTable; + private float totalTime; public RayTracingRenderer() { @@ -282,10 +516,10 @@ internal unsafe class RayTracingRenderer : IRenderer Vector3[] floorVertices = [ - new(-5.0f, 0.0f, -5.0f), - new( 5.0f, 0.0f, -5.0f), - new( 5.0f, 0.0f, 5.0f), - new(-5.0f, 0.0f, 5.0f) + new(-50.0f, 0.0f, -50.0f), + new( 50.0f, 0.0f, -50.0f), + new( 50.0f, 0.0f, 50.0f), + new(-50.0f, 0.0f, 50.0f) ]; uint[] floorIndices = [0, 1, 2, 0, 2, 3]; @@ -305,38 +539,31 @@ internal unsafe class RayTracingRenderer : IRenderer }); floorIndexBuffer.Upload(floorIndices, 0); - Sphere[] sphereData = + Sphere[] spheres = [ - new() { Center = new(-1.5f, 1.0f, 0.0f), Radius = 1.0f, Color = new(0.8f, 0.2f, 0.2f) }, - new() { Center = new( 1.5f, 1.0f, 0.0f), Radius = 1.0f, Color = new(0.2f, 0.4f, 0.8f) } + new() { Center = new(-2.0f, 1.0f, 1.0f), Radius = 1.0f, Color = new(0.8f, 0.2f, 0.2f) }, + new() { Center = new( 2.0f, 1.2f, -1.0f), Radius = 1.2f, Color = new(0.2f, 0.4f, 0.8f) }, + new() { Center = new( 0.0f, 0.6f, -3.0f), Radius = 0.6f, Color = new(0.9f, 0.7f, 0.2f) } ]; - sphereBuffer = App.Context.CreateBuffer(new() - { - SizeInBytes = (uint)(sizeof(Sphere) * sphereData.Length), - StrideInBytes = (uint)sizeof(Sphere), - Flags = BufferUsageFlags.ShaderResource - }); - sphereBuffer.Upload(sphereData, 0); - - Vector3[] aabbData = new Vector3[sphereData.Length * 2]; - for (int i = 0; i < sphereData.Length; i++) + Vector3[] aabbs = new Vector3[spheres.Length * 2]; + for (int i = 0; i < spheres.Length; i++) { - aabbData[i * 2] = sphereData[i].Center - new Vector3(sphereData[i].Radius); - aabbData[(i * 2) + 1] = sphereData[i].Center + new Vector3(sphereData[i].Radius); + aabbs[i * 2] = spheres[i].Center - new Vector3(spheres[i].Radius); + aabbs[(i * 2) + 1] = spheres[i].Center + new Vector3(spheres[i].Radius); } aabbBuffer = App.Context.CreateBuffer(new() { - SizeInBytes = (uint)(sizeof(Vector3) * aabbData.Length), + SizeInBytes = (uint)(sizeof(Vector3) * aabbs.Length), StrideInBytes = (uint)(sizeof(Vector3) * 2), Flags = BufferUsageFlags.ShaderResource | BufferUsageFlags.AccelerationStructure }); - aabbBuffer.Upload(aabbData, 0); + aabbBuffer.Upload(aabbs, 0); - CommandBuffer buildCmd = App.Context.Graphics.CommandBuffer(); + CommandBuffer commandBuffer = App.Context.Graphics.CommandBuffer(); - floorBlas = buildCmd.BuildAccelerationStructure(new BottomLevelAccelerationStructureDesc + floorBlas = commandBuffer.BuildAccelerationStructure(new BottomLevelAccelerationStructureDesc { Geometries = [ @@ -360,7 +587,7 @@ internal unsafe class RayTracingRenderer : IRenderer Flags = AccelerationStructureBuildFlags.PreferFastTrace }); - sphereBlas = buildCmd.BuildAccelerationStructure(new BottomLevelAccelerationStructureDesc + sphereBlas = commandBuffer.BuildAccelerationStructure(new BottomLevelAccelerationStructureDesc { Geometries = [ @@ -370,7 +597,7 @@ internal unsafe class RayTracingRenderer : IRenderer AABBs = new() { Buffer = aabbBuffer, - Count = (uint)sphereData.Length, + Count = (uint)spheres.Length, StrideInBytes = (uint)(sizeof(Vector3) * 2) }, Flags = RayTracingGeometryFlags.Opaque @@ -379,7 +606,7 @@ internal unsafe class RayTracingRenderer : IRenderer Flags = AccelerationStructureBuildFlags.PreferFastTrace }); - tlas = buildCmd.BuildAccelerationStructure(new TopLevelAccelerationStructureDesc + tlas = commandBuffer.BuildAccelerationStructure(new TopLevelAccelerationStructureDesc { Instances = [ @@ -403,13 +630,29 @@ internal unsafe class RayTracingRenderer : IRenderer Flags = AccelerationStructureBuildFlags.PreferFastTrace }); - buildCmd.Submit(waitForCompletion: true); + commandBuffer.Submit(waitForCompletion: true); + + constantsBuffer = App.Context.CreateBuffer(new() + { + SizeInBytes = (uint)sizeof(Constants), + StrideInBytes = (uint)sizeof(Constants), + Flags = BufferUsageFlags.Constant | BufferUsageFlags.MapWrite + }); + + sphereBuffer = App.Context.CreateBuffer(new() + { + SizeInBytes = (uint)(sizeof(Sphere) * spheres.Length), + StrideInBytes = (uint)sizeof(Sphere), + Flags = BufferUsageFlags.ShaderResource + }); + sphereBuffer.Upload(spheres, 0); resourceLayout = App.Context.CreateResourceLayout(new() { Bindings = BindingHelper.Bindings ( new() { Type = ResourceType.AccelerationStructure, Count = 1, StageFlags = ShaderStageFlags.Compute }, + new() { Type = ResourceType.ConstantBuffer, Count = 1, StageFlags = ShaderStageFlags.Compute }, new() { Type = ResourceType.StructuredBuffer, Count = 1, StageFlags = ShaderStageFlags.Compute }, new() { Type = ResourceType.TextureReadWrite, Count = 1, StageFlags = ShaderStageFlags.Compute } ) @@ -429,6 +672,14 @@ internal unsafe class RayTracingRenderer : IRenderer public void Update(double deltaTime) { + totalTime += (float)deltaTime; + + float angle = totalTime * 0.3f; + + constantsBuffer.Upload([new Constants() + { + Position = new(12.0f * MathF.Sin(angle), 4.0f + MathF.Sin(totalTime * 0.2f), -12.0f * MathF.Cos(angle)) + }], 0); } public void Render() @@ -449,7 +700,7 @@ internal unsafe class RayTracingRenderer : IRenderer resourceTable ??= App.Context.CreateResourceTable(new() { Layout = resourceLayout, - Resources = [tlas, sphereBuffer, outputTexture] + Resources = [tlas, constantsBuffer, sphereBuffer, outputTexture] }); CommandBuffer commandBuffer = App.Context.Graphics.CommandBuffer(); @@ -462,12 +713,10 @@ internal unsafe class RayTracingRenderer : IRenderer commandBuffer.Dispatch(dispatchX, dispatchY, 1); - Texture colorTarget = App.SwapChain.FrameBuffer.Desc.ColorAttachments[0].Target; - commandBuffer.CopyTexture(outputTexture, default, default, - colorTarget, + App.FrameBuffer.Desc.ColorAttachments[0].Target, default, default, new() { Width = App.Width, Height = App.Height, Depth = 1 }); @@ -479,6 +728,7 @@ internal unsafe class RayTracingRenderer : IRenderer { resourceTable?.Dispose(); resourceTable = null; + outputTexture?.Dispose(); outputTexture = null; } @@ -490,19 +740,24 @@ internal unsafe class RayTracingRenderer : IRenderer pipeline.Dispose(); resourceLayout.Dispose(); + sphereBuffer.Dispose(); + constantsBuffer.Dispose(); tlas.Dispose(); sphereBlas.Dispose(); floorBlas.Dispose(); aabbBuffer.Dispose(); - sphereBuffer.Dispose(); floorIndexBuffer.Dispose(); floorVertexBuffer.Dispose(); } } -/// -/// Sphere definition for procedural geometry. -/// +[StructLayout(LayoutKind.Explicit, Size = 16)] +file struct Constants +{ + [FieldOffset(0)] + public Vector3 Position; +} + [StructLayout(LayoutKind.Explicit, Size = 32)] file struct Sphere { @@ -526,8 +781,6 @@ using ZenithTutorials; using ZenithTutorials.Renderers; App.Run(); - -App.Cleanup(); ``` Run the application: @@ -558,8 +811,7 @@ Always check `Capabilities.RayTracingSupported` before using ray tracing feature Build a two-level acceleration structure hierarchy: ```csharp -// Floor BLAS (triangle geometry) -floorBlas = buildCmd.BuildAccelerationStructure(new BottomLevelAccelerationStructureDesc +floorBlas = commandBuffer.BuildAccelerationStructure(new BottomLevelAccelerationStructureDesc { Geometries = [ @@ -583,8 +835,7 @@ floorBlas = buildCmd.BuildAccelerationStructure(new BottomLevelAccelerationStruc Flags = AccelerationStructureBuildFlags.PreferFastTrace }); -// Sphere BLAS (procedural AABB geometry) -sphereBlas = buildCmd.BuildAccelerationStructure(new BottomLevelAccelerationStructureDesc +sphereBlas = commandBuffer.BuildAccelerationStructure(new BottomLevelAccelerationStructureDesc { Geometries = [ @@ -594,7 +845,7 @@ sphereBlas = buildCmd.BuildAccelerationStructure(new BottomLevelAccelerationStru AABBs = new() { Buffer = aabbBuffer, - Count = (uint)sphereData.Length, + Count = (uint)spheres.Length, StrideInBytes = (uint)(sizeof(Vector3) * 2) }, Flags = RayTracingGeometryFlags.Opaque @@ -607,7 +858,7 @@ sphereBlas = buildCmd.BuildAccelerationStructure(new BottomLevelAccelerationStru Combine BLAS into a TLAS: ```csharp -tlas = buildCmd.BuildAccelerationStructure(new TopLevelAccelerationStructureDesc +tlas = commandBuffer.BuildAccelerationStructure(new TopLevelAccelerationStructureDesc { Instances = [ @@ -632,48 +883,29 @@ tlas = buildCmd.BuildAccelerationStructure(new TopLevelAccelerationStructureDesc }); ``` -### Ray Tracing with RayQuery - -Ray tracing in Zenith.NET uses `RayQuery` within a compute shader. Procedural geometry requires a custom intersection function: +### Camera Animation -```slang -float IntersectSphere(float3 origin, float3 direction, Sphere sphere) +```csharp +public void Update(double deltaTime) { - float3 oc = origin - sphere.Center; + totalTime += (float)deltaTime; - float a = dot(direction, direction); - float b = dot(oc, direction); - float c = dot(oc, oc) - sphere.Radius * sphere.Radius; - float discriminant = b * b - a * c; + float angle = totalTime * 0.3f; - if (discriminant > 0.0) + constantsBuffer.Upload([new Constants() { - float sqrtD = sqrt(discriminant); - float t1 = (-b - sqrtD) / a; - - if (t1 > 0.0) - { - return t1; - } - - float t2 = (-b + sqrtD) / a; - - if (t2 > 0.0) - { - return t2; - } - } - - return -1.0; + Position = new(12.0f * MathF.Sin(angle), 4.0f + MathF.Sin(totalTime * 0.2f), -12.0f * MathF.Cos(angle)) + }], 0); } ``` -The main ray query loop processes only procedural candidates, storing hit information for later use: +The camera orbits around the scene at a radius of 12 units with gentle vertical bobbing. The position is passed to the compute shader via a constant buffer and used to construct the camera ray directions. -```slang -float3 sphereHitNormal = float3(0.0); -float3 sphereHitColor = float3(0.0); +### Ray Tracing with RayQuery + +The main ray query loop processes procedural candidates and stores hit information: +```slang RayQuery query; query.TraceRayInline(scene, RAY_FLAG_NONE, 0xFF, ray); @@ -681,35 +913,17 @@ while (query.Proceed()) { if (query.CandidateType() == CANDIDATE_PROCEDURAL_PRIMITIVE) { - uint sphereIndex = query.CandidatePrimitiveIndex(); - Sphere sphere = spheres[sphereIndex]; - - float3 ro = query.CandidateObjectRayOrigin(); - float3 rd = query.CandidateObjectRayDirection(); - float t = IntersectSphere(ro, rd, sphere); - if (t >= query.RayTMin() && t <= query.CommittedRayT()) { - float3 hitPoint = ro + rd * t; - - sphereHitNormal = normalize(hitPoint - sphere.Center); - sphereHitColor = sphere.Color; - query.CommitProceduralPrimitiveHit(t); } } } -// Check result -if (query.CommittedStatus() == COMMITTED_TRIANGLE_HIT) -{ - // Handle triangle hit -} -else if (query.CommittedStatus() == COMMITTED_PROCEDURAL_PRIMITIVE_HIT) -{ - // Handle procedural hit using stored normal and color -} +if (query.CommittedStatus() == COMMITTED_TRIANGLE_HIT) { ... } +else if (query.CommittedStatus() == COMMITTED_PROCEDURAL_PRIMITIVE_HIT) { ... } +else { ... } ``` Key elements: @@ -720,50 +934,62 @@ Key elements: | `TraceRayInline` | Initiates the ray traversal | | `Proceed()` | Advances traversal; returns `true` while candidates remain | | `CandidateType()` | Returns the type of the current candidate hit | -| `CommitNonOpaqueTriangleHit()` | Accepts a triangle hit | | `CommitProceduralPrimitiveHit(t)` | Accepts a procedural hit at distance `t` | | `CommittedStatus()` | Returns the final hit result after traversal | -### Shadow Rays with RayQuery +### Shadow Rays + +Hard shadow rays test visibility to a light source with early termination: + +```slang +RayQuery shadowQuery; +``` + +`RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH` stops at the first hit since we only need to know if something blocks the light. -Shadow rays test visibility to a light source. Note that shadow rays must also handle procedural geometry intersections: +Soft shadows jitter the light direction around a small disc to simulate a sun with finite radius: ```slang -bool TraceShadowRay(float3 origin, float3 direction) +float TraceSoftShadow(float3 origin, float3 direction, float2 pixelSeed) { - RayDesc shadowRay; - shadowRay.Origin = origin; - shadowRay.Direction = direction; - shadowRay.TMin = 0.001; - shadowRay.TMax = 1000.0; + ... +} +``` - RayQuery shadowQuery; - shadowQuery.TraceRayInline(scene, RAY_FLAG_NONE, 0xFF, shadowRay); +The number of samples (`ShadowSamples = 6`) and sun size (`SunRadius = 0.04`) control the shadow softness. - while (shadowQuery.Proceed()) - { - if (shadowQuery.CandidateType() == CANDIDATE_PROCEDURAL_PRIMITIVE) - { - uint sphereIndex = shadowQuery.CandidatePrimitiveIndex(); - Sphere sphere = spheres[sphereIndex]; +### Reflections and Fresnel - float3 ro = shadowQuery.CandidateObjectRayOrigin(); - float3 rd = shadowQuery.CandidateObjectRayDirection(); +Both the floor and spheres use Fresnel-based reflections: - float t = IntersectSphere(ro, rd, sphere); +```slang +float3 reflectDir = reflect(rayDir, normal); +float3 reflectColor = TraceReflection(hitPoint + normal * RayEpsilon, reflectDir); +float fresnel = SchlickFresnel(max(dot(normal, viewDir), 0.0), f0); - if (t >= shadowQuery.RayTMin() && t <= shadowQuery.CommittedRayT()) - { - shadowQuery.CommitProceduralPrimitiveHit(t); - } - } - } +return lerp(directColor, reflectColor, fresnel); +``` - return shadowQuery.CommittedStatus() != COMMITTED_NOTHING; +The Schlick approximation controls the blend between direct and reflected color. Surfaces reflect more at glancing angles. Spheres use `TraceRoughReflection` which jitters the reflection direction for a roughened appearance. + +### ACES Tonemapping + +```slang +float3 ACESFilm(float3 x) +{ + x *= 1.6; + float3 a = x * (x * 2.51 + 0.03); + float3 b = x * (x * 2.43 + 0.59) + 0.14; + float3 result = saturate(a / b); + + float luma = dot(result, float3(0.2126, 0.7152, 0.0722)); + result = saturate(lerp(float3(luma, luma, luma), result, 1.5)); + + return result; } ``` -Key optimization: `RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH` stops at the first hit since we only need to know if something blocks the light. +ACES (Academy Color Encoding System) tonemapping maps HDR lighting values to the displayable [0, 1] range with a cinematic response curve. A contrast enhancement step increases color saturation after the initial tone curve. ### Compute Pipeline for Ray Tracing @@ -773,39 +999,20 @@ resourceLayout = App.Context.CreateResourceLayout(new() Bindings = BindingHelper.Bindings ( new() { Type = ResourceType.AccelerationStructure, Count = 1, StageFlags = ShaderStageFlags.Compute }, + new() { Type = ResourceType.ConstantBuffer, Count = 1, StageFlags = ShaderStageFlags.Compute }, new() { Type = ResourceType.StructuredBuffer, Count = 1, StageFlags = ShaderStageFlags.Compute }, new() { Type = ResourceType.TextureReadWrite, Count = 1, StageFlags = ShaderStageFlags.Compute } ) }); - -pipeline = App.Context.CreateComputePipeline(new() -{ - Compute = computeShader, - ResourceLayout = resourceLayout, - ThreadGroupSizeX = ThreadGroupSize, - ThreadGroupSizeY = ThreadGroupSize, - ThreadGroupSizeZ = 1 -}); ``` -Note that all resource bindings use `ShaderStageFlags.Compute` since ray tracing runs entirely within a compute shader. - -### Dispatching and Display - -```csharp -uint dispatchX = (App.Width + ThreadGroupSize - 1) / ThreadGroupSize; -uint dispatchY = (App.Height + ThreadGroupSize - 1) / ThreadGroupSize; - -commandBuffer.SetPipeline(pipeline); -commandBuffer.SetResourceTable(resourceTable); -commandBuffer.Dispatch(dispatchX, dispatchY, 1); -``` - -The compute shader processes each pixel independently. The result is then copied to the swap chain's color target for display. +The resource layout binds four resources: the TLAS acceleration structure, a constant buffer for camera position, a structured buffer for sphere data, and a read/write texture for output. ## Next Steps -- [Mesh Shading](mesh-shading.md) - Process geometry in meshlets using the modern mesh shading pipeline +Congratulations! You've implemented a complete ray tracer with advanced lighting. Continue with mesh shading: + +- [Mesh Shading](mesh-shading.md) - Use amplification shaders for frustum culling with mesh shading pipeline at scale ## Source Code diff --git a/documents/tutorials/getting-started/hello-triangle.md b/documents/tutorials/getting-started/hello-triangle.md index 31a81796..1e4d4fb4 100644 --- a/documents/tutorials/getting-started/hello-triangle.md +++ b/documents/tutorials/getting-started/hello-triangle.md @@ -34,7 +34,7 @@ internal unsafe class HelloTriangleRenderer : IRenderer { float4 Position : SV_POSITION; - float4 Color : COLOR0; + float4 Color : COLOR; }; PSInput VSMain(VSInput input) @@ -57,12 +57,11 @@ internal unsafe class HelloTriangleRenderer : IRenderer public HelloTriangleRenderer() { - // Define triangle vertices (NDC coordinates: -1 to 1) Vertex[] vertices = [ - new(new( 0.0f, 0.5f, 0.0f), new(1.0f, 0.0f, 0.0f, 1.0f)), // Top - Red - new(new( 0.5f, -0.5f, 0.0f), new(0.0f, 1.0f, 0.0f, 1.0f)), // Right - Green - new(new(-0.5f, -0.5f, 0.0f), new(0.0f, 0.0f, 1.0f, 1.0f)), // Left - Blue + new(new( 0.0f, 0.5f, 0.0f), new(1.0f, 0.0f, 0.0f, 1.0f)), + new(new( 0.5f, -0.5f, 0.0f), new(0.0f, 1.0f, 0.0f, 1.0f)), + new(new(-0.5f, -0.5f, 0.0f), new(0.0f, 0.0f, 1.0f, 1.0f)), ]; vertexBuffer = App.Context.CreateBuffer(new() @@ -73,7 +72,6 @@ internal unsafe class HelloTriangleRenderer : IRenderer }); vertexBuffer.Upload(vertices, 0); - // Define vertex input layout (must match shader VSInput) InputLayout inputLayout = new(); inputLayout.Add(new() { Format = ElementFormat.Float3, Semantic = ElementSemantic.Position }); inputLayout.Add(new() { Format = ElementFormat.Float4, Semantic = ElementSemantic.Color }); @@ -85,16 +83,16 @@ internal unsafe class HelloTriangleRenderer : IRenderer { RenderStates = new() { - RasterizerState = RasterizerStates.CullNone, // Disable back-face culling - DepthStencilState = DepthStencilStates.Default, // Enable depth testing - BlendState = BlendStates.Opaque // No alpha blending + RasterizerState = RasterizerStates.CullNone, + DepthStencilState = DepthStencilStates.Default, + BlendState = BlendStates.Opaque }, Vertex = vertexShader, Pixel = pixelShader, ResourceLayout = null, InputLayouts = [inputLayout], PrimitiveTopology = PrimitiveTopology.TriangleList, - Output = App.SwapChain.FrameBuffer.Output + Output = App.FrameBuffer.Output }); } @@ -106,7 +104,7 @@ internal unsafe class HelloTriangleRenderer : IRenderer { CommandBuffer commandBuffer = App.Context.Graphics.CommandBuffer(); - commandBuffer.BeginRenderPass(App.SwapChain.FrameBuffer, new() + commandBuffer.BeginRenderPass(App.FrameBuffer, new() { ColorValues = [new(0.1f, 0.1f, 0.1f, 1.0f)], Depth = 1.0f, @@ -134,9 +132,6 @@ internal unsafe class HelloTriangleRenderer : IRenderer } } -/// -/// Vertex structure with position and color data. -/// [StructLayout(LayoutKind.Sequential)] file struct Vertex(Vector3 position, Vector4 color) { @@ -218,7 +213,7 @@ pipeline = App.Context.CreateGraphicsPipeline(new() ResourceLayout = null, InputLayouts = [inputLayout], PrimitiveTopology = PrimitiveTopology.TriangleList, - Output = App.SwapChain.FrameBuffer.Output + Output = App.FrameBuffer.Output }); ``` @@ -231,7 +226,7 @@ public void Render() { CommandBuffer commandBuffer = App.Context.Graphics.CommandBuffer(); - commandBuffer.BeginRenderPass(App.SwapChain.FrameBuffer, new() + commandBuffer.BeginRenderPass(App.FrameBuffer, new() { ColorValues = [new(0.1f, 0.1f, 0.1f, 1.0f)], Depth = 1.0f, diff --git a/documents/tutorials/getting-started/prerequisites.md b/documents/tutorials/getting-started/prerequisites.md index bc61952b..34503289 100644 --- a/documents/tutorials/getting-started/prerequisites.md +++ b/documents/tutorials/getting-started/prerequisites.md @@ -98,8 +98,10 @@ Create `Usings.cs` for shared using statements across all files: ```csharp global using System.Numerics; +global using System.Runtime.CompilerServices; global using System.Runtime.InteropServices; global using Zenith.NET; +global using Zenith.NET.Extensions.ImageSharp; global using Zenith.NET.Extensions.Slang; global using Buffer = Zenith.NET.Buffer; ``` @@ -197,7 +199,8 @@ internal static class BindingHelper { ResourceType.ConstantBuffer or ResourceType.StructuredBuffer or - ResourceType.StructuredBufferReadWrite => bufferIndex++, + ResourceType.StructuredBufferReadWrite or + ResourceType.AccelerationStructure => bufferIndex++, ResourceType.Texture or ResourceType.TextureReadWrite => textureIndex++, @@ -312,54 +315,54 @@ namespace ZenithTutorials; internal static class App { private static readonly IWindow window; + private static readonly SwapChain swapChain; static App() { - // Ensure platform is supported if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS() && !OperatingSystem.IsLinux()) { - throw new PlatformNotSupportedException("This tutorial only supports Windows, macOS, and Linux."); + throw new PlatformNotSupportedException("This application only supports Windows, macOS, and Linux."); } - // Create window with no graphics API (we manage rendering ourselves) + if (OperatingSystem.IsWindows()) + { + Context = GraphicsContext.CreateDirectX12(useValidationLayer: true); + } + else if (OperatingSystem.IsMacOS()) + { + Context = GraphicsContext.CreateMetal(useValidationLayer: true); + } + else + { + Context = GraphicsContext.CreateVulkan(useValidationLayer: true); + } + + Context.ValidationMessage += static (sender, args) => Console.WriteLine($"[{args.Source} - {args.Severity}] {args.Message}"); + window = Window.Create(WindowOptions.Default with { API = GraphicsAPI.None, - Title = "Zenith.NET Tutorial", + Title = "Zenith Tutorials", Size = new(1280, 720) }); - window.Initialize(); + window.Center(); - // Create graphics context and surface based on platform Surface surface; if (OperatingSystem.IsWindows()) { - Context = GraphicsContext.CreateDirectX12(useValidationLayer: true); - surface = Surface.Win32(window.Native!.Win32!.Value.Hwnd, Width, Height); } else if (OperatingSystem.IsMacOS()) { - Context = GraphicsContext.CreateMetal(useValidationLayer: true); - surface = Surface.Apple(CocoaHelper.CreateLayer(window.Native!.Cocoa!.Value), Width, Height); } else { - Context = GraphicsContext.CreateVulkan(useValidationLayer: true); - surface = Surface.Xlib(window.Native!.X11!.Value.Display, (nint)window.Native.X11.Value.Window, Width, Height); } - // Log validation messages for debugging - Context.ValidationMessage += (sender, args) => - { - Console.WriteLine($"[{args.Source} - {args.Severity}] {args.Message}"); - }; - - // Create swap chain for double-buffered rendering - SwapChain = Context.CreateSwapChain(new() + swapChain = Context.CreateSwapChain(new() { Surface = surface, ColorTargetFormat = PixelFormat.B8G8R8A8UNorm, @@ -369,50 +372,59 @@ internal static class App public static GraphicsContext Context { get; } - public static SwapChain SwapChain { get; } + public static uint Width => (uint)window.FramebufferSize.X; - public static uint Width => (uint)window.Size.X; + public static uint Height => (uint)window.FramebufferSize.Y; - public static uint Height => (uint)window.Size.Y; + public static FrameBuffer FrameBuffer => swapChain.FrameBuffer; public static void Run() where TRenderer : IRenderer, new() { - using TRenderer renderer = new(); + try + { + using TRenderer renderer = new(); + + window.Update += delta => + { + if (Width is 0 || Height is 0) + { + return; + } - window.Update += renderer.Update; + renderer.Update(delta); + }; - window.Render += delta => - { - // Skip rendering when window is minimized - if (Width <= 0 || Height <= 0) + window.Render += delta => { - return; - } + if (Width is 0 || Height is 0) + { + return; + } - renderer.Render(); - SwapChain.Present(); - }; + renderer.Render(); + swapChain.Present(); + }; - window.Resize += size => - { - if (Width <= 0 || Height <= 0) + window.Resize += size => { - return; - } + if (Width is 0 || Height is 0) + { + return; + } - // Notify renderer first, then resize swap chain - renderer.Resize(Width, Height); - SwapChain.Resize(Width, Height); - }; + renderer.Resize(Width, Height); + swapChain.Resize(Width, Height); + }; - window.Run(); - } + window.Run(); + } + finally + { + swapChain.Dispose(); + window.Dispose(); - public static void Cleanup() - { - SwapChain.Dispose(); - Context.Dispose(); - window.Dispose(); + Context.Dispose(); + } } } ``` @@ -426,8 +438,6 @@ using ZenithTutorials; using ZenithTutorials.Renderers; App.Run(); - -App.Cleanup(); ``` > [!NOTE] @@ -441,7 +451,7 @@ This framework provides: - **SwapChain management** for presenting frames - **Resize handling** for responsive rendering - **Generic renderer pattern** using `App.Run()` for easy tutorial switching -- **Static access** to `App.Context` and `App.SwapChain` from renderers +- **Static access** to `App.Context` and `App.FrameBuffer` from renderers ## Verify Installation diff --git a/documents/tutorials/getting-started/spinning-cube.md b/documents/tutorials/getting-started/spinning-cube.md index 9a94e529..969dcfd0 100644 --- a/documents/tutorials/getting-started/spinning-cube.md +++ b/documents/tutorials/getting-started/spinning-cube.md @@ -21,15 +21,6 @@ namespace ZenithTutorials.Renderers; internal unsafe class SpinningCubeRenderer : IRenderer { private const string ShaderSource = """ - struct MVPConstants - { - float4x4 Model; - - float4x4 View; - - float4x4 Projection; - }; - struct VSInput { float3 Position : POSITION0; @@ -41,18 +32,26 @@ internal unsafe class SpinningCubeRenderer : IRenderer { float4 Position : SV_POSITION; - float4 Color : COLOR0; + float4 Color : COLOR; }; - ConstantBuffer mvp; + struct Constants + { + float4x4 Model; + + float4x4 View; + + float4x4 Projection; + }; + + ConstantBuffer constants; PSInput VSMain(VSInput input) { - float4 worldPos = mul(float4(input.Position, 1.0), mvp.Model); - float4 viewPos = mul(worldPos, mvp.View); - + float4x4 mvp = mul(mul(constants.Model, constants.View), constants.Projection); + PSInput output; - output.Position = mul(viewPos, mvp.Projection); + output.Position = mul(float4(input.Position, 1.0), mvp); output.Color = input.Color; return output; @@ -66,7 +65,7 @@ internal unsafe class SpinningCubeRenderer : IRenderer private readonly Buffer vertexBuffer; private readonly Buffer indexBuffer; - private readonly Buffer constantBuffer; + private readonly Buffer constantsBuffer; private readonly ResourceLayout resourceLayout; private readonly ResourceTable resourceTable; private readonly GraphicsPipeline pipeline; @@ -77,12 +76,10 @@ internal unsafe class SpinningCubeRenderer : IRenderer { Vertex[] vertices = [ - // Front face new(new(-0.5f, -0.5f, 0.5f), new(1.0f, 0.0f, 0.0f, 1.0f)), new(new( 0.5f, -0.5f, 0.5f), new(0.0f, 1.0f, 0.0f, 1.0f)), new(new( 0.5f, 0.5f, 0.5f), new(0.0f, 0.0f, 1.0f, 1.0f)), new(new(-0.5f, 0.5f, 0.5f), new(1.0f, 1.0f, 0.0f, 1.0f)), - // Back face new(new(-0.5f, -0.5f, -0.5f), new(1.0f, 0.0f, 1.0f, 1.0f)), new(new( 0.5f, -0.5f, -0.5f), new(0.0f, 1.0f, 1.0f, 1.0f)), new(new( 0.5f, 0.5f, -0.5f), new(1.0f, 1.0f, 1.0f, 1.0f)), @@ -91,17 +88,11 @@ internal unsafe class SpinningCubeRenderer : IRenderer uint[] indices = [ - // Front 0, 1, 2, 0, 2, 3, - // Back 5, 4, 7, 5, 7, 6, - // Left 4, 0, 3, 4, 3, 7, - // Right 1, 5, 6, 1, 6, 2, - // Top 3, 2, 6, 3, 6, 7, - // Bottom 4, 5, 1, 4, 1, 0 ]; @@ -121,10 +112,10 @@ internal unsafe class SpinningCubeRenderer : IRenderer }); indexBuffer.Upload(indices, 0); - constantBuffer = App.Context.CreateBuffer(new() + constantsBuffer = App.Context.CreateBuffer(new() { - SizeInBytes = (uint)sizeof(MVPConstants), - StrideInBytes = (uint)sizeof(MVPConstants), + SizeInBytes = (uint)sizeof(Constants), + StrideInBytes = (uint)sizeof(Constants), Flags = BufferUsageFlags.Constant | BufferUsageFlags.MapWrite }); @@ -139,7 +130,7 @@ internal unsafe class SpinningCubeRenderer : IRenderer resourceTable = App.Context.CreateResourceTable(new() { Layout = resourceLayout, - Resources = [constantBuffer] + Resources = [constantsBuffer] }); InputLayout inputLayout = new(); @@ -162,26 +153,26 @@ internal unsafe class SpinningCubeRenderer : IRenderer ResourceLayout = resourceLayout, InputLayouts = [inputLayout], PrimitiveTopology = PrimitiveTopology.TriangleList, - Output = App.SwapChain.FrameBuffer.Output + Output = App.FrameBuffer.Output }); } public void Update(double deltaTime) { rotationAngle += (float)deltaTime; - } - public void Render() - { Matrix4x4 model = Matrix4x4.CreateRotationY(rotationAngle) * Matrix4x4.CreateRotationX(rotationAngle * 0.5f); Matrix4x4 view = Matrix4x4.CreateLookAt(new(0, 0, 3), Vector3.Zero, Vector3.UnitY); Matrix4x4 projection = Matrix4x4.CreatePerspectiveFieldOfView(float.DegreesToRadians(45.0f), (float)App.Width / App.Height, 0.1f, 100.0f); - constantBuffer.Upload([new MVPConstants() { Model = model, View = view, Projection = projection }], 0); + constantsBuffer.Upload([new Constants() { Model = model, View = view, Projection = projection }], 0); + } + public void Render() + { CommandBuffer commandBuffer = App.Context.Graphics.CommandBuffer(); - commandBuffer.BeginRenderPass(App.SwapChain.FrameBuffer, new() + commandBuffer.BeginRenderPass(App.FrameBuffer, new() { ColorValues = [new(0.1f, 0.1f, 0.1f, 1.0f)], Depth = 1.0f, @@ -209,15 +200,12 @@ internal unsafe class SpinningCubeRenderer : IRenderer pipeline.Dispose(); resourceTable.Dispose(); resourceLayout.Dispose(); - constantBuffer.Dispose(); + constantsBuffer.Dispose(); indexBuffer.Dispose(); vertexBuffer.Dispose(); } } -/// -/// Vertex structure with position and color data. -/// [StructLayout(LayoutKind.Sequential)] file struct Vertex(Vector3 position, Vector4 color) { @@ -226,11 +214,8 @@ file struct Vertex(Vector3 position, Vector4 color) public Vector4 Color = color; } -/// -/// MVP transformation matrices. -/// [StructLayout(LayoutKind.Explicit, Size = 192)] -file struct MVPConstants +file struct Constants { [FieldOffset(0)] public Matrix4x4 Model; @@ -252,8 +237,6 @@ using ZenithTutorials; using ZenithTutorials.Renderers; App.Run(); - -App.Cleanup(); ``` Run the application: @@ -268,11 +251,11 @@ dotnet run ## Code Breakdown -### MVP Constants Structure +### Constants Structure ```csharp [StructLayout(LayoutKind.Explicit, Size = 192)] -file struct MVPConstants +file struct Constants { [FieldOffset(0)] public Matrix4x4 Model; @@ -296,10 +279,10 @@ The MVP (Model-View-Projection) matrices transform vertices from object space to ### Constant Buffer ```csharp -constantBuffer = App.Context.CreateBuffer(new() +constantsBuffer = App.Context.CreateBuffer(new() { - SizeInBytes = (uint)sizeof(MVPConstants), - StrideInBytes = (uint)sizeof(MVPConstants), + SizeInBytes = (uint)sizeof(Constants), + StrideInBytes = (uint)sizeof(Constants), Flags = BufferUsageFlags.Constant | BufferUsageFlags.MapWrite }); ``` @@ -309,17 +292,13 @@ Constant buffers pass data from the CPU to shaders. Use `BufferUsageFlags.Consta ### Cube Geometry ```csharp -// 8 vertices for the cube corners Vertex[] vertices = [ ... ]; -// 36 indices (6 faces × 2 triangles × 3 vertices) uint[] indices = [ - // Front 0, 1, 2, 0, 2, 3, - // Back 5, 4, 7, 5, 7, 6, - // ... remaining faces + ... ]; ``` @@ -328,22 +307,21 @@ A cube has 8 unique vertices and 6 faces. Each face is made of 2 triangles, requ ### Shader MVP Transformation ```slang -ConstantBuffer mvp; +ConstantBuffer constants; PSInput VSMain(VSInput input) { - float4 worldPos = mul(float4(input.Position, 1.0), mvp.Model); - float4 viewPos = mul(worldPos, mvp.View); + float4x4 mvp = mul(mul(constants.Model, constants.View), constants.Projection); PSInput output; - output.Position = mul(viewPos, mvp.Projection); + output.Position = mul(float4(input.Position, 1.0), mvp); output.Color = input.Color; return output; } ``` -C# `Matrix4x4` and Slang (with `-matrix-layout-row-major`) both use row-major layout, so the multiplication order is `vector * matrix`. The vertex shader applies transformations in order: Model → View → Projection. Use `ConstantBuffer` in Slang to access structured constant data. +C# `Matrix4x4` and Slang (with `-matrix-layout-row-major`) both use row-major layout, so the multiplication order is `vector * matrix`. The vertex shader pre-computes the combined MVP matrix and applies it in a single multiplication. Use `ConstantBuffer` in Slang to access structured constant data. ### Frame Update @@ -351,10 +329,16 @@ C# `Matrix4x4` and Slang (with `-matrix-layout-row-major`) both use row-major la public void Update(double deltaTime) { rotationAngle += (float)deltaTime; + + Matrix4x4 model = Matrix4x4.CreateRotationY(rotationAngle) * Matrix4x4.CreateRotationX(rotationAngle * 0.5f); + Matrix4x4 view = Matrix4x4.CreateLookAt(new(0, 0, 3), Vector3.Zero, Vector3.UnitY); + Matrix4x4 projection = Matrix4x4.CreatePerspectiveFieldOfView(float.DegreesToRadians(45.0f), (float)App.Width / App.Height, 0.1f, 100.0f); + + constantsBuffer.Upload([new Constants() { Model = model, View = view, Projection = projection }], 0); } ``` -The `Update` method is called each frame with the elapsed time. Accumulating `deltaTime` creates smooth, frame-rate-independent rotation. +The `Update` method is called each frame with the elapsed time. It accumulates `deltaTime` for smooth rotation, creates the transformation matrices, and uploads them to the constant buffer. ### Creating Matrices diff --git a/documents/tutorials/getting-started/textured-quad.md b/documents/tutorials/getting-started/textured-quad.md index b077d649..5b9eef96 100644 --- a/documents/tutorials/getting-started/textured-quad.md +++ b/documents/tutorials/getting-started/textured-quad.md @@ -76,11 +76,11 @@ internal unsafe class TexturedQuadRenderer : IRenderer { float4 Position : SV_POSITION; - float2 TexCoord : TEXCOORD0; + float2 TexCoord : TEXCOORD; }; - Texture2D shaderTexture; - SamplerState samplerState; + Texture2D texture; + SamplerState sampler; PSInput VSMain(VSInput input) { @@ -93,7 +93,7 @@ internal unsafe class TexturedQuadRenderer : IRenderer float4 PSMain(PSInput input) : SV_TARGET { - return shaderTexture.Sample(samplerState, input.TexCoord); + return texture.Sample(sampler, input.TexCoord); } """; @@ -107,7 +107,6 @@ internal unsafe class TexturedQuadRenderer : IRenderer public TexturedQuadRenderer() { - // UV origin (0,0) is top-left, (1,1) is bottom-right Vertex[] vertices = [ new(new(-0.5f, 0.5f, 0.0f), new(0.0f, 0.0f)), @@ -180,7 +179,7 @@ internal unsafe class TexturedQuadRenderer : IRenderer ResourceLayout = resourceLayout, InputLayouts = [inputLayout], PrimitiveTopology = PrimitiveTopology.TriangleList, - Output = App.SwapChain.FrameBuffer.Output + Output = App.FrameBuffer.Output }); } @@ -192,7 +191,7 @@ internal unsafe class TexturedQuadRenderer : IRenderer { CommandBuffer commandBuffer = App.Context.Graphics.CommandBuffer(); - commandBuffer.BeginRenderPass(App.SwapChain.FrameBuffer, new() + commandBuffer.BeginRenderPass(App.FrameBuffer, new() { ColorValues = [new(0.1f, 0.1f, 0.1f, 1.0f)], Depth = 1.0f, @@ -227,9 +226,6 @@ internal unsafe class TexturedQuadRenderer : IRenderer } } -/// -/// Vertex structure with position and texture coordinates. -/// [StructLayout(LayoutKind.Sequential)] file struct Vertex(Vector3 position, Vector2 texCoord) { @@ -325,7 +321,6 @@ Samplers control how textures are read: ### Resource Binding ```csharp -// 1. Define the layout using BindingHelper for cross-platform compatibility resourceLayout = App.Context.CreateResourceLayout(new() { Bindings = BindingHelper.Bindings @@ -335,14 +330,12 @@ resourceLayout = App.Context.CreateResourceLayout(new() ) }); -// 2. Create the table (bind actual resources) resourceTable = App.Context.CreateResourceTable(new() { Layout = resourceLayout, Resources = [texture, sampler] }); -// 3. Bind during rendering commandBuffer.SetResourceTable(resourceTable); ``` @@ -359,7 +352,7 @@ The `BindingHelper.Bindings()` method (defined in [Prerequisites](prerequisites. When beginning a render pass, you can pass resource tables to the `preprocessResourceTables` parameter: ```csharp -commandBuffer.BeginRenderPass(App.SwapChain.FrameBuffer, new() +commandBuffer.BeginRenderPass(App.FrameBuffer, new() { ColorValues = [new(0.1f, 0.1f, 0.1f, 1.0f)], Depth = 1.0f, @@ -373,12 +366,12 @@ This allows Zenith.NET to optimize the resources in the table for shader access ### Shader Texture Sampling ```slang -Texture2D shaderTexture; -SamplerState samplerState; +Texture2D texture; +SamplerState sampler; float4 PSMain(PSInput input) : SV_TARGET { - return shaderTexture.Sample(samplerState, input.TexCoord); + return texture.Sample(sampler, input.TexCoord); } ``` diff --git a/documents/tutorials/index.md b/documents/tutorials/index.md index 74d10369..e27c1dbf 100644 --- a/documents/tutorials/index.md +++ b/documents/tutorials/index.md @@ -28,8 +28,8 @@ Explore cutting-edge GPU features for modern rendering (requires hardware suppor | Tutorial | Description | Requirement | |----------|-------------|-------------| -| [Ray Tracing](advanced/ray-tracing.md) | Build acceleration structures (BLAS/TLAS), use `RayQuery` for ray tracing, and implement hard shadows | `RayTracingSupported` | -| [Mesh Shading](advanced/mesh-shading.md) | Use meshlet-based geometry processing with mesh shading pipelines | `MeshShadingSupported` | +| [Ray Tracing](advanced/ray-tracing.md) | Build acceleration structures (BLAS/TLAS), use `RayQuery` for ray tracing with soft shadows, reflections, and ACES tonemapping | `RayTracingSupported` | +| [Mesh Shading](advanced/mesh-shading.md) | Render 1,000 sphere instances with amplification shader frustum culling and mesh shading pipeline | `MeshShadingSupported` | ## Tutorial Structure @@ -54,8 +54,8 @@ All tutorials share the same `App` framework, making it easy to switch between e | **Spinning Cube** | Pass data to shaders via constant buffers, implement 3D transformations, and enable depth testing | | **Compute Shader** | Run general-purpose GPU computations for image processing | | **Indirect Drawing** | Let the GPU control draw parameters for efficient multi-instance rendering | -| **Ray Tracing** | Build acceleration structures, trace rays with `RayQuery`, handle intersections, and implement shadows | -| **Mesh Shading** | Process geometry in meshlets using the modern mesh shading pipeline | +| **Ray Tracing** | Build acceleration structures, trace rays with `RayQuery`, implement soft shadows, reflections, Fresnel, and ACES tonemapping | +| **Mesh Shading** | Use amplification shaders for GPU-driven frustum culling with mesh shading at scale (1,000 instances) | ## Requirements diff --git a/documents/tutorials/intermediate/compute-shader.md b/documents/tutorials/intermediate/compute-shader.md index 5d0d51b6..d70ec27c 100644 --- a/documents/tutorials/intermediate/compute-shader.md +++ b/documents/tutorials/intermediate/compute-shader.md @@ -28,24 +28,22 @@ internal class ComputeShaderRenderer : IRenderer RWTexture2D outputTexture; [numthreads(16, 16, 1)] - void CSMain(uint3 dispatchThreadID : SV_DispatchThreadID) + void CSMain(uint3 dispatchThreadID: SV_DispatchThreadID) { uint width, height; outputTexture.GetDimensions(width, height); - // Bounds check if (dispatchThreadID.x >= width || dispatchThreadID.y >= height) { return; } - // Read input pixel float4 color = inputTexture[dispatchThreadID.xy]; - // Convert to grayscale using luminance weights - float gray = dot(color.rgb, float3(0.299, 0.587, 0.114)); + float3 linear = pow(color.rgb, 2.2); + float gray = dot(linear, float3(0.2126, 0.7152, 0.0722)); + gray = pow(gray, 1.0 / 2.2); - // Write to output outputTexture[dispatchThreadID.xy] = float4(gray, gray, gray, color.a); } """; @@ -122,14 +120,11 @@ internal class ComputeShaderRenderer : IRenderer processed = true; } - // Copy the processed texture to the swap chain's color target (centered) - Texture colorTarget = App.SwapChain.FrameBuffer.Desc.ColorAttachments[0].Target; + Texture colorTarget = App.FrameBuffer.Desc.ColorAttachments[0].Target; - // Clamp copy region to fit within both textures uint copyWidth = Math.Min(outputTexture.Desc.Width, App.Width); uint copyHeight = Math.Min(outputTexture.Desc.Height, App.Height); - // Center the copy region uint srcX = (outputTexture.Desc.Width - copyWidth) / 2; uint srcY = (outputTexture.Desc.Height - copyHeight) / 2; uint destX = (App.Width - copyWidth) / 2; @@ -170,8 +165,6 @@ using ZenithTutorials; using ZenithTutorials.Renderers; App.Run(); - -App.Cleanup(); ``` Run the application: @@ -193,20 +186,20 @@ Texture2D inputTexture; RWTexture2D outputTexture; [numthreads(16, 16, 1)] -void CSMain(uint3 dispatchThreadID : SV_DispatchThreadID) +void CSMain(uint3 dispatchThreadID: SV_DispatchThreadID) { uint width, height; outputTexture.GetDimensions(width, height); - // Bounds check if (dispatchThreadID.x >= width || dispatchThreadID.y >= height) { return; } - // Read, process, write float4 color = inputTexture[dispatchThreadID.xy]; - float gray = dot(color.rgb, float3(0.299, 0.587, 0.114)); + float3 linear = pow(color.rgb, 2.2); + float gray = dot(linear, float3(0.2126, 0.7152, 0.0722)); + gray = pow(gray, 1.0 / 2.2); outputTexture[dispatchThreadID.xy] = float4(gray, gray, gray, color.a); } ``` @@ -219,6 +212,9 @@ Key elements: | `RWTexture2D` | Read/write output texture | | `[numthreads(16, 16, 1)]` | Thread group size (16×16 threads) | | `SV_DispatchThreadID` | Global thread index across all groups | +| `pow(color.rgb, 2.2)` | Linearizes sRGB input by removing gamma encoding | +| `float3(0.2126, 0.7152, 0.0722)` | ITU-R BT.709 luminance weights for perceptual grayscale | +| `pow(gray, 1.0 / 2.2)` | Re-applies gamma correction for correct sRGB display | ### Output Texture Creation @@ -293,13 +289,11 @@ The `Dispatch` call executes the compute shader: ### Copying to the Swap Chain ```csharp -Texture colorTarget = App.SwapChain.FrameBuffer.Desc.ColorAttachments[0].Target; +Texture colorTarget = App.FrameBuffer.Desc.ColorAttachments[0].Target; -// Clamp copy region to fit within both textures uint copyWidth = Math.Min(outputTexture.Desc.Width, App.Width); uint copyHeight = Math.Min(outputTexture.Desc.Height, App.Height); -// Center the copy region uint srcX = (outputTexture.Desc.Width - copyWidth) / 2; uint srcY = (outputTexture.Desc.Height - copyHeight) / 2; uint destX = (App.Width - copyWidth) / 2; @@ -316,7 +310,7 @@ commandBuffer.CopyTexture(outputTexture, Instead of using a full-screen quad with a graphics pipeline, we directly copy the processed texture to the swap chain's color target: -- `App.SwapChain.FrameBuffer.Desc.ColorAttachments[0].Target` - Gets the swap chain's render target texture +- `App.FrameBuffer.Desc.ColorAttachments[0].Target` - Gets the swap chain's render target texture - `CopyTexture` - Efficiently copies texture data on the GPU without needing shaders or render passes - `copyWidth` / `copyHeight` - Clamps the copy region to fit within both source and destination textures - `srcX` / `srcY` - Centers the source region when the texture is larger than the window diff --git a/documents/tutorials/intermediate/indirect-drawing.md b/documents/tutorials/intermediate/indirect-drawing.md index a601c712..1f6ebfa1 100644 --- a/documents/tutorials/intermediate/indirect-drawing.md +++ b/documents/tutorials/intermediate/indirect-drawing.md @@ -20,17 +20,17 @@ namespace ZenithTutorials.Renderers; internal unsafe class IndirectDrawingRenderer : IRenderer { - private const int InstanceCount = 25; // 5x5 grid of cubes + private const int InstanceCount = 25; private const string ShaderSource = """ - struct ViewConstants + struct Constants { float4x4 View; float4x4 Projection; }; - struct InstanceData + struct Instance { float4x4 Model; @@ -50,21 +50,21 @@ internal unsafe class IndirectDrawingRenderer : IRenderer { float4 Position : SV_POSITION; - float4 Color : COLOR0; + float4 Color : COLOR; }; - ConstantBuffer view; - StructuredBuffer instances; + ConstantBuffer constants; + StructuredBuffer instances; PSInput VSMain(VSInput input) { - InstanceData instance = instances[input.InstanceID]; + Instance instance = instances[input.InstanceID]; float4 worldPos = mul(float4(input.Position, 1.0), instance.Model); - float4 viewPos = mul(worldPos, view.View); + float4 viewPos = mul(worldPos, constants.View); PSInput output; - output.Position = mul(viewPos, view.Projection); + output.Position = mul(viewPos, constants.Projection); output.Color = input.Color * instance.Color; return output; @@ -79,7 +79,7 @@ internal unsafe class IndirectDrawingRenderer : IRenderer private readonly Buffer vertexBuffer; private readonly Buffer indexBuffer; private readonly Buffer indirectBuffer; - private readonly Buffer viewConstantsBuffer; + private readonly Buffer constantsBuffer; private readonly Buffer instanceBuffer; private readonly ResourceLayout resourceLayout; private readonly ResourceTable resourceTable; @@ -91,12 +91,10 @@ internal unsafe class IndirectDrawingRenderer : IRenderer { Vertex[] vertices = [ - // Front face new(new(-0.5f, -0.5f, 0.5f), new(1.0f, 1.0f, 1.0f, 1.0f)), new(new( 0.5f, -0.5f, 0.5f), new(1.0f, 1.0f, 1.0f, 1.0f)), new(new( 0.5f, 0.5f, 0.5f), new(1.0f, 1.0f, 1.0f, 1.0f)), new(new(-0.5f, 0.5f, 0.5f), new(1.0f, 1.0f, 1.0f, 1.0f)), - // Back face new(new(-0.5f, -0.5f, -0.5f), new(1.0f, 1.0f, 1.0f, 1.0f)), new(new( 0.5f, -0.5f, -0.5f), new(1.0f, 1.0f, 1.0f, 1.0f)), new(new( 0.5f, 0.5f, -0.5f), new(1.0f, 1.0f, 1.0f, 1.0f)), @@ -145,20 +143,22 @@ internal unsafe class IndirectDrawingRenderer : IRenderer FirstInstance = 0 }], 0); - viewConstantsBuffer = App.Context.CreateBuffer(new() + constantsBuffer = App.Context.CreateBuffer(new() { - SizeInBytes = (uint)sizeof(ViewConstants), - StrideInBytes = (uint)sizeof(ViewConstants), + SizeInBytes = (uint)sizeof(Constants), + StrideInBytes = (uint)sizeof(Constants), Flags = BufferUsageFlags.Constant | BufferUsageFlags.MapWrite }); instanceBuffer = App.Context.CreateBuffer(new() { - SizeInBytes = (uint)(sizeof(InstanceData) * InstanceCount), - StrideInBytes = (uint)sizeof(InstanceData), + SizeInBytes = (uint)(sizeof(Instance) * InstanceCount), + StrideInBytes = (uint)sizeof(Instance), Flags = BufferUsageFlags.ShaderResource | BufferUsageFlags.MapWrite }); + Resize(App.Width, App.Height); + resourceLayout = App.Context.CreateResourceLayout(new() { Bindings = BindingHelper.Bindings @@ -171,7 +171,7 @@ internal unsafe class IndirectDrawingRenderer : IRenderer resourceTable = App.Context.CreateResourceTable(new() { Layout = resourceLayout, - Resources = [viewConstantsBuffer, instanceBuffer] + Resources = [constantsBuffer, instanceBuffer] }); InputLayout inputLayout = new(); @@ -181,7 +181,6 @@ internal unsafe class IndirectDrawingRenderer : IRenderer using Shader vertexShader = App.Context.LoadShaderFromSource(ShaderSource, "VSMain", ShaderStageFlags.Vertex); using Shader pixelShader = App.Context.LoadShaderFromSource(ShaderSource, "PSMain", ShaderStageFlags.Pixel); - // Create graphics pipeline pipeline = App.Context.CreateGraphicsPipeline(new() { RenderStates = new() @@ -195,7 +194,7 @@ internal unsafe class IndirectDrawingRenderer : IRenderer ResourceLayout = resourceLayout, InputLayouts = [inputLayout], PrimitiveTopology = PrimitiveTopology.TriangleList, - Output = App.SwapChain.FrameBuffer.Output + Output = App.FrameBuffer.Output }); } @@ -203,7 +202,7 @@ internal unsafe class IndirectDrawingRenderer : IRenderer { rotationAngle += (float)deltaTime; - InstanceData[] instances = new InstanceData[InstanceCount]; + Instance[] instances = new Instance[InstanceCount]; int index = 0; int gridSize = (int)Math.Sqrt(InstanceCount); @@ -233,14 +232,9 @@ internal unsafe class IndirectDrawingRenderer : IRenderer public void Render() { - Matrix4x4 view = Matrix4x4.CreateLookAt(new(0, 0, 8), Vector3.Zero, Vector3.UnitY); - Matrix4x4 projection = Matrix4x4.CreatePerspectiveFieldOfView(float.DegreesToRadians(45.0f), (float)App.Width / App.Height, 0.1f, 100.0f); - - viewConstantsBuffer.Upload([new ViewConstants() { View = view, Projection = projection }], 0); - CommandBuffer commandBuffer = App.Context.Graphics.CommandBuffer(); - commandBuffer.BeginRenderPass(App.SwapChain.FrameBuffer, new() + commandBuffer.BeginRenderPass(App.FrameBuffer, new() { ColorValues = [new(0.1f, 0.1f, 0.15f, 1.0f)], Depth = 1.0f, @@ -261,6 +255,10 @@ internal unsafe class IndirectDrawingRenderer : IRenderer public void Resize(uint width, uint height) { + Matrix4x4 view = Matrix4x4.CreateLookAt(new(0, 0, 8), Vector3.Zero, Vector3.UnitY); + Matrix4x4 projection = Matrix4x4.CreatePerspectiveFieldOfView(float.DegreesToRadians(45.0f), (float)width / height, 0.1f, 100.0f); + + constantsBuffer.Upload([new Constants() { View = view, Projection = projection }], 0); } public void Dispose() @@ -269,16 +267,13 @@ internal unsafe class IndirectDrawingRenderer : IRenderer resourceTable.Dispose(); resourceLayout.Dispose(); instanceBuffer.Dispose(); - viewConstantsBuffer.Dispose(); + constantsBuffer.Dispose(); indirectBuffer.Dispose(); indexBuffer.Dispose(); vertexBuffer.Dispose(); } } -/// -/// Vertex structure with position and color data. -/// [StructLayout(LayoutKind.Sequential)] file struct Vertex(Vector3 position, Vector4 color) { @@ -287,11 +282,8 @@ file struct Vertex(Vector3 position, Vector4 color) public Vector4 Color = color; } -/// -/// Per-instance transformation and color data. -/// [StructLayout(LayoutKind.Explicit, Size = 80)] -file struct InstanceData +file struct Instance { [FieldOffset(0)] public Matrix4x4 Model; @@ -300,11 +292,8 @@ file struct InstanceData public Vector4 Color; } -/// -/// View and projection matrices. -/// [StructLayout(LayoutKind.Explicit, Size = 128)] -file struct ViewConstants +file struct Constants { [FieldOffset(0)] public Matrix4x4 View; @@ -323,8 +312,6 @@ using ZenithTutorials; using ZenithTutorials.Renderers; App.Run(); - -App.Cleanup(); ``` Run the application: @@ -380,8 +367,8 @@ indirectBuffer = App.Context.CreateBuffer(new() ```csharp instanceBuffer = App.Context.CreateBuffer(new() { - SizeInBytes = (uint)(sizeof(InstanceData) * InstanceCount), - StrideInBytes = (uint)sizeof(InstanceData), + SizeInBytes = (uint)(sizeof(Instance) * InstanceCount), + StrideInBytes = (uint)sizeof(Instance), Flags = BufferUsageFlags.ShaderResource | BufferUsageFlags.MapWrite }); ``` @@ -391,14 +378,14 @@ Instance data is stored in a `StructuredBuffer` accessed by the vertex shader us ### Shader Instance Access ```slang -StructuredBuffer instances; +StructuredBuffer instances; PSInput VSMain(VSInput input) { - InstanceData instance = instances[input.InstanceID]; + Instance instance = instances[input.InstanceID]; float4 worldPos = mul(float4(input.Position, 1.0), instance.Model); - // ... + ... } ``` From a4b2558046c0c34b0c50d42f5744e5d69ec31f2c Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Sat, 4 Apr 2026 15:33:35 +0800 Subject: [PATCH 18/24] Add _gitUrlPattern to docfx.json for source code links Added the _gitUrlPattern property to docfx.json, enabling generated documentation to include direct links to specific lines in the GitHub repository. This improves traceability and makes it easier for users to navigate from documentation to the relevant source code. --- documents/docfx.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documents/docfx.json b/documents/docfx.json index dc96a069..28f27224 100644 --- a/documents/docfx.json +++ b/documents/docfx.json @@ -49,7 +49,8 @@ "_gitContribute": { "repo": "https://github.com/qian-o/Zenith.NET", "branch": "master" - } + }, + "_gitUrlPattern": "https://github.com/qian-o/Zenith.NET/blob/master/{path}#L{line}" }, "output": "_site", "sitemap": { From 2b231e24c47f181cfdac6c804906a564e0d779a3 Mon Sep 17 00:00:00 2001 From: qian-o <1324771795@qq.com> Date: Sat, 4 Apr 2026 15:42:10 +0800 Subject: [PATCH 19/24] Update demo images for compute, mesh, ray tracing, and cube Replaced PNG files for compute-shader, indirect-drawing, mesh-shading, ray-tracing, and spinning-cube with updated versions. No source code changes included. --- documents/images/compute-shader.png | Bin 209667 -> 212532 bytes documents/images/indirect-drawing.png | Bin 10645 -> 8073 bytes documents/images/mesh-shading.png | Bin 29804 -> 602293 bytes documents/images/ray-tracing.png | Bin 41297 -> 261493 bytes documents/images/spinning-cube.png | Bin 61157 -> 34165 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/documents/images/compute-shader.png b/documents/images/compute-shader.png index e3ae527c88ea2f24f8eaf0e963c184e6101e8452..a75663c9014c2110d63c8952d124ff9ebd7f40b2 100644 GIT binary patch literal 212532 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 literal 209667 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%0De+^_TItZ_`fKYU*&O@C5HJ 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%0A8qJo;;c4iEg&}SeTLkrYOWOJsRHk2NX<|=nNM5 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,bCa9 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