features/modernize #1

Merged
Misaki merged 5 commits from features/modernize into main 2026-02-22 03:05:52 +00:00
9 changed files with 646 additions and 194 deletions
Showing only changes of commit 84b2504a6f - Show all commits

View File

@@ -1,8 +1,7 @@
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.IO;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
@@ -10,168 +9,119 @@ namespace SimpleRayTracer.Interop;
internal sealed partial class InteropSmokeTest : IDisposable
{
private IntPtr _scene;
private IntPtr _job;
private SrtScene? _scene;
private SrtRenderJob? _job;
private WriteableBitmap? _bitmap;
private byte[]? _scratchBgra;
public WriteableBitmap Bitmap => _bitmap ?? throw new InvalidOperationException("Not started");
private static Quaternion FromEulerDegrees(Vector3 degrees)
{
var radians = degrees * (MathF.PI / 180f);
var cy = MathF.Cos(radians.Z * 0.5f);
var sy = MathF.Sin(radians.Z * 0.5f);
var cp = MathF.Cos(radians.Y * 0.5f);
var sp = MathF.Sin(radians.Y * 0.5f);
var cr = MathF.Cos(radians.X * 0.5f);
var sr = MathF.Sin(radians.X * 0.5f);
return new Quaternion
{
W = cr * cp * cy + sr * sp * sy,
X = sr * cp * cy - cr * sp * sy,
Y = cr * sp * cy + sr * cp * sy,
Z = cr * cp * sy - sr * sp * cy,
};
}
public void StartAsync(uint width = 640, uint height = 360)
{
if (_scene != IntPtr.Zero || _job != IntPtr.Zero)
if (_scene is not null || _job is not null)
throw new InvalidOperationException("Already started");
_scene = SrtNative.srt_scene_create(
triangle_count: 0,
texture_count: 1,
material_count: 8,
punctual_light_count: 1);
_scene = SrtScene.Create(triangleCount: 0, textureCount: 1, materialCount: 8, punctualLightCount: 1);
if (_scene == IntPtr.Zero)
throw new InvalidOperationException("srt_scene_create failed (native DLL not found or init failed)");
// Camera similar to native defaults: at (0,0,5) looking towards -Z.
_scene.SetCamera(position: new Vector3(-7.5f, 2.5f, 0.0f), rotation: FromEulerDegrees(new Vector3(10, -90, 0)), focalLength: 0.025f, sizeX: 0.036f, aspectRatio: width / (float)height);
unsafe
{
// Camera similar to native defaults: at (0,0,5) looking towards -Z.
var cam = new SrtNative.CameraParams
{
position = new Vector3(0, 0, 5),
rotation = Quaternion.Identity,
focal_length = 0.025f,
size_x = 0.036f,
aspect_ratio = width / (float)height,
};
ThrowIfNotOk(SrtNative.srt_scene_camera_set(_scene, &cam));
// Basic sky so we don't render into black/undefined environment.
_scene.SetSky(SrtSky.Constant(new Vector3(1, 1, 1), intensity: 1.0f));
// Basic sky so we don't render into black/undefined environment.
// (This also avoids confusion while sky HDR bindings are still being wired into the app UI.)
ThrowIfNotOk(SrtNative.srt_scene_set_sky_constant(_scene, SrtNative.Vec3.Of(1, 1, 1), intensity: 0.0f));
// Directional light
var lightDir = Vector3.Normalize(new Vector3(0.6f, 1.0f, 0.25f));
_scene.AddDirectionalLight(direction: lightDir, color: new Vector3(1.0f, 0.93f, 0.87f), intensity: 2.0f, angularDiameter: 0.53f);
// Directional light
var lightDir = Vector3.Normalize(new Vector3(0.6f, 1.0f, 0.25f));
var sun = new SrtNative.DirectionalLight
{
direction = new Vector3(lightDir.X, lightDir.Y, lightDir.Z),
color = new Vector3(1.0f, 0.93f, 0.87f),
intensity = 2.0f,
angular_diameter = 0.53f,
};
_ = SrtNative.srt_scene_add_directional_light(_scene, &sun, out _);
_scene.LoadMeshFromFile("F:/c/SimpleRayTracer/native/assets/sponza.fbx");
_scene.Commit();
// StandardLit material (id 0 in fresh collection)
var props = new SrtNative.StandardLitProperties
{
albedo = new Vector3(0.8f, 0.2f, 0.2f),
diffuse_roughness = 0.0f,
roughness = 0.6f,
metallic = 0.0f,
// Match native defaults: "no texture" uses INVALID_TEXTURE_ID (0xFFFF).
albedo_texture = SrtNative.TextureHandle.Invalid(),
normal_texture = SrtNative.TextureHandle.Invalid(),
roughness_texture = SrtNative.TextureHandle.Invalid(),
metallic_texture = SrtNative.TextureHandle.Invalid(),
};
ThrowIfNotOk(SrtNative.srt_scene_create_standard_lit_material(_scene, &props, emission: SrtNative.Vec3.Of(0, 0, 0), out var mat));
// A single triangle in front of the camera.
ThrowIfNotOk(SrtNative.srt_scene_add_mesh_model(_scene, triangle_reserve: 1, out var modelId));
var tri = new SrtNative.Triangle
{
v0 = new SrtNative.Vertex
{
position = new Vector3(-1.0f, -1.0f, 0.0f),
normal = new Vector3(0, 0, 1),
tangent = new Vector3(1, 0, 0),
color = new Vector3(1, 1, 1),
uv = new Vector2 { X = 0, Y = 0 },
},
v1 = new SrtNative.Vertex
{
position = new Vector3(1.0f, -1.0f, 0.0f),
normal = new Vector3(0, 0, 1),
tangent = new Vector3(1, 0, 0),
color = new Vector3(1, 1, 1),
uv = new Vector2 { X = 1, Y = 0 },
},
v2 = new SrtNative.Vertex
{
position = new Vector3(0.0f, 1.0f, 0.0f),
normal = new Vector3(0, 0, 1),
tangent = new Vector3(1, 0, 0),
color = new Vector3(1, 1, 1),
uv = new Vector2 { X = 0.5f, Y = 1 },
},
material_id = mat.id,
};
ThrowIfNotOk(SrtNative.srt_scene_mesh_model_set_triangles(_scene, modelId, &tri, 1));
var identity = SrtNative.Mat4.Identity();
ThrowIfNotOk(SrtNative.srt_scene_add_mesh_instance(_scene, modelId, &identity, out _));
ThrowIfNotOk(SrtNative.srt_scene_commit(_scene));
var cfg = new SrtNative.RenderingConfig
{
width = width,
height = height,
sample_count = 64,
max_depth = 4,
bucket_size = 64,
};
ThrowIfNotOk(SrtNative.srt_render_job_create(
_scene,
&cfg,
aov_flags: SrtNative.SRT_AOV_BEAUTY,
rendering_mode: SrtNative.SRT_RENDER_TILE_BASED,
seed: 1,
out _job));
}
_job = _scene.CreateRenderJob(width: width, height: height, sampleCount: 64, maxDepth: 4, bucketSize: 64, seed: 1);
_bitmap = new WriteableBitmap((int)width, (int)height);
// Run renderer on background thread.
_ = Task.Run(() =>
{
var r = SrtNative.srt_render_job_start(_job);
if (r != SrtNative.Result.Ok)
try
{
_job.StartBlocking();
}
catch (Exception ex)
{
// No UI exception throwing from background thread; this is a smoke test.
System.Diagnostics.Debug.WriteLine($"Native render failed: {r}");
System.Diagnostics.Debug.WriteLine($"Native render failed: {ex}");
}
});
}
public unsafe void CopyLatestToBitmap()
{
if (_job == IntPtr.Zero || _bitmap is null)
if (_job is null || _bitmap is null)
return;
var pixelBuffer = _bitmap.PixelBuffer;
using var stream = pixelBuffer.AsStream();
// Ensure we have enough capacity
var res = SrtNative.srt_render_job_get_aov_desc(_job, SrtNative.SRT_AOV_BEAUTY_INDEX, out var w, out var h, out _);
var res = SrtNative.srt_render_job_get_aov_desc(_job.Handle, SrtNative.SRT_AOV_BEAUTY_INDEX, out var w, out var h, out _);
if (res != SrtNative.Result.Ok)
return;
var expectedSize = checked((int)(w * h * 4));
if (stream.Length != expectedSize)
if ((uint)_bitmap.PixelWidth != w || (uint)_bitmap.PixelHeight != h)
{
stream.SetLength(expectedSize);
// Bitmap size mismatch (shouldn't happen in this smoke test).
return;
}
var temp = new byte[expectedSize];
fixed (byte* dst = temp)
var pixelBuffer = _bitmap.PixelBuffer;
var expectedBytes = checked(w * h * 4u);
if (pixelBuffer.Length < expectedBytes)
{
_ = SrtNative.srt_render_job_copy_aov_bgra8(_job, SrtNative.SRT_AOV_BEAUTY_INDEX, dst, w * 4);
// Unexpected: WriteableBitmap pixel buffer is smaller than required.
return;
}
// Fast path: try raw pointer write (true zero-copy into PixelBuffer).
if (WinRtBufferByteAccess.TryGetPointer(pixelBuffer, out var dst) && dst != null)
{
_job.CopyBeautyBgra8(dst, w * 4);
}
else
{
// Fallback: stream write. Still avoids per-frame allocations by reusing a scratch buffer.
int n = checked((int)expectedBytes);
_scratchBgra ??= new byte[n];
if (_scratchBgra.Length != n)
{
_scratchBgra = new byte[n];
}
fixed (byte* tmp = _scratchBgra)
{
_job.CopyBeautyBgra8(tmp, w * 4);
}
using var stream = pixelBuffer.AsStream();
stream.Position = 0;
stream.Write(_scratchBgra, 0, _scratchBgra.Length);
}
stream.Position = 0;
stream.Write(temp, 0, temp.Length);
_bitmap.Invalidate();
}
@@ -179,33 +129,19 @@ internal sealed partial class InteropSmokeTest : IDisposable
{
get
{
if (_job == IntPtr.Zero)
if (_job is null)
return true;
var r = SrtNative.srt_render_job_is_done(_job, out var done);
return r == SrtNative.Result.Ok && done != 0;
return _job.IsDone;
}
}
private static void ThrowIfNotOk(SrtNative.Result r, [CallerArgumentExpression(nameof(r))] string? call = null)
{
if (r != SrtNative.Result.Ok)
throw new InvalidOperationException($"Native call failed ({call}): {r}");
}
public void Dispose()
{
if (_job != IntPtr.Zero)
{
SrtNative.srt_render_job_request_stop(_job);
SrtNative.srt_render_job_destroy(_job);
_job = IntPtr.Zero;
}
_job?.Dispose();
_job = null;
if (_scene != IntPtr.Zero)
{
SrtNative.srt_scene_destroy(_scene);
_scene = IntPtr.Zero;
}
_scene?.Dispose();
_scene = null;
}
}

View File

@@ -0,0 +1,295 @@
using System;
using System.Numerics;
using System.IO;
namespace SimpleRayTracer.Interop;
internal sealed class SrtScene : IDisposable
{
internal IntPtr Handle { get; private set; }
public static SrtScene Create(ulong triangleCount, ushort textureCount, byte materialCount, uint punctualLightCount)
{
var h = SrtNative.srt_scene_create(triangleCount, textureCount, materialCount, punctualLightCount);
if (h == IntPtr.Zero)
throw new InvalidOperationException("srt_scene_create failed (native DLL not found or init failed)");
return new SrtScene(h);
}
private SrtScene(IntPtr handle) => Handle = handle;
public void SetCamera(Vector3 position, Quaternion rotation, float focalLength, float sizeX, float aspectRatio)
{
unsafe
{
var cam = new SrtNative.CameraParams
{
position = SrtNative.Vec3.Of(position.X, position.Y, position.Z),
rotation = new SrtNative.Quat { x = rotation.X, y = rotation.Y, z = rotation.Z, w = rotation.W },
focal_length = focalLength,
size_x = sizeX,
aspect_ratio = aspectRatio,
};
ThrowIfNotOk(SrtNative.srt_scene_camera_set(Handle, &cam));
}
}
public void SetSky(SrtSky sky)
{
if (sky is null) throw new ArgumentNullException(nameof(sky));
sky.Apply(this);
}
public uint AddDirectionalLight(Vector3 direction, Vector3 color, float intensity, float angularDiameter)
{
unsafe
{
var l = new SrtNative.DirectionalLight
{
direction = SrtNative.Vec3.Of(direction.X, direction.Y, direction.Z),
color = SrtNative.Vec3.Of(color.X, color.Y, color.Z),
intensity = intensity,
angular_diameter = angularDiameter,
};
ThrowIfNotOk(SrtNative.srt_scene_add_directional_light(Handle, &l, out var id));
return id;
}
}
public SrtNative.MaterialHandle CreateStandardLitMaterial(Vector3 albedo, float roughness, float diffuseRoughness, float metallic,
SrtNative.TextureHandle albedoTexture, SrtNative.TextureHandle normalTexture, SrtNative.TextureHandle roughnessTexture, SrtNative.TextureHandle metallicTexture,
Vector3 emission)
{
unsafe
{
var props = new SrtNative.StandardLitProperties
{
albedo = SrtNative.Vec3.Of(albedo.X, albedo.Y, albedo.Z),
roughness = roughness,
diffuse_roughness = diffuseRoughness,
metallic = metallic,
albedo_texture = albedoTexture,
normal_texture = normalTexture,
roughness_texture = roughnessTexture,
metallic_texture = metallicTexture,
};
ThrowIfNotOk(SrtNative.srt_scene_create_standard_lit_material(
Handle,
&props,
SrtNative.Vec3.Of(emission.X, emission.Y, emission.Z),
out var mat));
return mat;
}
}
public uint AddMeshModel(ulong triangleReserve)
{
ThrowIfNotOk(SrtNative.srt_scene_add_mesh_model(Handle, triangleReserve, out var id));
return id;
}
public void SetMeshTriangles(uint modelId, ReadOnlySpan<SrtNative.Triangle> triangles)
{
unsafe
{
fixed (SrtNative.Triangle* p = triangles)
{
ThrowIfNotOk(SrtNative.srt_scene_mesh_model_set_triangles(Handle, modelId, p, (uint)triangles.Length));
}
}
}
public uint AddMeshInstance(uint modelId, in SrtNative.Mat4 localToWorld)
{
unsafe
{
fixed (SrtNative.Mat4* p = &localToWorld)
{
ThrowIfNotOk(SrtNative.srt_scene_add_mesh_instance(Handle, modelId, p, out var instanceId));
return instanceId;
}
}
}
public void Commit() => ThrowIfNotOk(SrtNative.srt_scene_commit(Handle));
public SrtNative.TextureHandle LoadTextureFromFile(string path, bool srgb = true, bool mipmap = true, SrtNative.TextureStride stride = SrtNative.TextureStride.UInt8)
{
if (path is null) throw new ArgumentNullException(nameof(path));
path = ResolveAssetPath(path);
ThrowIfNotOk(SrtNative.srt_scene_texture_load(
Handle,
path,
srgb: (byte)(srgb ? 1 : 0),
mipmap: (byte)(mipmap ? 1 : 0),
stride: (uint)stride,
out var tex));
return tex;
}
public SrtNative.MeshHandle LoadMeshFromFile(string path)
{
if (path is null) throw new ArgumentNullException(nameof(path));
path = ResolveAssetPath(path);
ThrowIfNotOk(SrtNative.srt_scene_load_mesh(Handle, path, out var mesh));
return mesh;
}
private static string ResolveAssetPath(string path)
{
if (Path.IsPathRooted(path))
return path;
// Prefer app base directory (WinUI packaged/unpackaged), then fall back to CWD.
var baseDir = AppContext.BaseDirectory;
var candidate = Path.GetFullPath(Path.Combine(baseDir, path));
if (File.Exists(candidate))
return candidate;
candidate = Path.GetFullPath(path);
return candidate;
}
public SrtRenderJob CreateRenderJob(uint width, uint height, uint sampleCount, byte maxDepth, uint bucketSize, uint seed,
uint aovFlags = SrtNative.SRT_AOV_BEAUTY, uint renderingMode = SrtNative.SRT_RENDER_TILE_BASED)
{
unsafe
{
var cfg = new SrtNative.RenderingConfig
{
width = width,
height = height,
sample_count = sampleCount,
max_depth = maxDepth,
bucket_size = bucketSize,
};
ThrowIfNotOk(SrtNative.srt_render_job_create(Handle, &cfg, aovFlags, renderingMode, seed, out var job));
return new SrtRenderJob(job);
}
}
public void Dispose()
{
if (Handle != IntPtr.Zero)
{
SrtNative.srt_scene_destroy(Handle);
Handle = IntPtr.Zero;
}
}
internal static void ThrowIfNotOk(SrtNative.Result r)
{
if (r != SrtNative.Result.Ok)
throw new InvalidOperationException($"Native call failed: {r}");
}
}
internal sealed class SrtRenderJob : IDisposable
{
public IntPtr Handle { get; private set; }
internal SrtRenderJob(IntPtr handle) => Handle = handle;
public void StartBlocking() => SrtScene.ThrowIfNotOk(SrtNative.srt_render_job_start(Handle));
public bool IsDone
{
get
{
var r = SrtNative.srt_render_job_is_done(Handle, out var done);
return r == SrtNative.Result.Ok && done != 0;
}
}
public unsafe void CopyBeautyBgra8(byte* dst, uint dstStrideBytes)
{
_ = SrtNative.srt_render_job_copy_aov_bgra8(Handle, SrtNative.SRT_AOV_BEAUTY_INDEX, dst, dstStrideBytes);
}
public void Dispose()
{
if (Handle != IntPtr.Zero)
{
SrtNative.srt_render_job_request_stop(Handle);
SrtNative.srt_render_job_destroy(Handle);
Handle = IntPtr.Zero;
}
}
}
internal abstract class SrtSky
{
internal abstract unsafe void Apply(SrtScene scene);
public static SrtSky None() => new NoneSky();
public static SrtSky Constant(Vector3 color, float intensity) => new ConstantSky(color, intensity);
public static SrtSky Hdr(SrtNative.TextureHandle hdri, float intensity) => new HdrSky(hdri, intensity);
private sealed class NoneSky : SrtSky
{
internal override unsafe void Apply(SrtScene scene)
{
var desc = new SrtNative.SkyDesc { kind = (uint)SrtNative.SkyKind.None, data_size = 0, data = null };
SrtScene.ThrowIfNotOk(SrtNative.srt_scene_set_sky(scene.Handle, &desc));
}
}
private sealed class ConstantSky : SrtSky
{
private readonly Vector3 _color;
private readonly float _intensity;
public ConstantSky(Vector3 color, float intensity)
{
_color = color;
_intensity = intensity;
}
internal override unsafe void Apply(SrtScene scene)
{
var cd = new SrtNative.SkyConstantDesc
{
color = SrtNative.Vec3.Of(_color.X, _color.Y, _color.Z),
intensity = _intensity,
};
var desc = new SrtNative.SkyDesc
{
kind = (uint)SrtNative.SkyKind.Constant,
data_size = (uint)sizeof(SrtNative.SkyConstantDesc),
data = &cd,
};
SrtScene.ThrowIfNotOk(SrtNative.srt_scene_set_sky(scene.Handle, &desc));
}
}
private sealed class HdrSky : SrtSky
{
private readonly SrtNative.TextureHandle _hdri;
private readonly float _intensity;
public HdrSky(SrtNative.TextureHandle hdri, float intensity)
{
_hdri = hdri;
_intensity = intensity;
}
internal override unsafe void Apply(SrtScene scene)
{
var hd = new SrtNative.SkyHdrDesc { hdri = _hdri, intensity = _intensity };
var desc = new SrtNative.SkyDesc
{
kind = (uint)SrtNative.SkyKind.Hdr,
data_size = (uint)sizeof(SrtNative.SkyHdrDesc),
data = &hd,
};
SrtScene.ThrowIfNotOk(SrtNative.srt_scene_set_sky(scene.Handle, &desc));
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
namespace SimpleRayTracer.Interop;
@@ -22,6 +21,8 @@ internal static unsafe class SrtNative
internal struct Vec2
{
public float x, y;
public static Vec2 Of(float x, float y) => new() { x = x, y = y };
}
[StructLayout(LayoutKind.Sequential)]
@@ -60,8 +61,8 @@ internal static unsafe class SrtNative
[StructLayout(LayoutKind.Sequential)]
internal struct CameraParams
{
public Vector3 position;
public Quaternion rotation;
public Vec3 position;
public Quat rotation;
public float focal_length;
public float size_x;
@@ -71,8 +72,8 @@ internal static unsafe class SrtNative
[StructLayout(LayoutKind.Sequential)]
internal struct DirectionalLight
{
public Vector3 direction;
public Vector3 color;
public Vec3 direction;
public Vec3 color;
public float intensity;
public float angular_diameter;
}
@@ -94,7 +95,7 @@ internal static unsafe class SrtNative
[StructLayout(LayoutKind.Sequential)]
internal struct StandardLitProperties
{
public Vector3 albedo;
public Vec3 albedo;
public float diffuse_roughness;
public float roughness;
public float metallic;
@@ -137,11 +138,11 @@ internal static unsafe class SrtNative
[StructLayout(LayoutKind.Sequential)]
internal struct Vertex
{
public Vector3 position;
public Vector3 normal;
public Vector3 tangent;
public Vector3 color;
public Vector2 uv;
public Vec3 position;
public Vec3 normal;
public Vec3 tangent;
public Vec3 color;
public Vec2 uv;
}
[StructLayout(LayoutKind.Sequential)]
@@ -153,6 +154,47 @@ internal static unsafe class SrtNative
public uint material_id;
}
[StructLayout(LayoutKind.Sequential)]
internal struct MeshHandle
{
public uint model_id;
public uint instance_id;
public ulong triangle_id;
public ulong triangle_count;
public ushort material_id;
public ushort material_count;
}
internal enum SkyKind : uint
{
None = 0,
Constant = 1,
Hdr = 2,
}
[StructLayout(LayoutKind.Sequential)]
internal struct SkyDesc
{
public uint kind;
public uint data_size;
public void* data;
}
[StructLayout(LayoutKind.Sequential)]
internal struct SkyConstantDesc
{
public Vec3 color;
public float intensity;
}
[StructLayout(LayoutKind.Sequential)]
internal struct SkyHdrDesc
{
public TextureHandle hdri;
public float intensity;
}
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr srt_scene_create(ulong triangle_count, ushort texture_count, byte material_count, uint punctual_light_count);
@@ -180,6 +222,9 @@ internal static unsafe class SrtNative
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_set_sky_none(IntPtr scene);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_set_sky(IntPtr scene, SkyDesc* desc);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_set_sky_constant(IntPtr scene, Vec3 color, float intensity);
@@ -195,6 +240,9 @@ internal static unsafe class SrtNative
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_add_mesh_instance(IntPtr scene, uint model_id, Mat4* local_to_world, out uint instance_id);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_load_mesh(IntPtr scene, [MarshalAs(UnmanagedType.LPUTF8Str)] string filename_utf8, out MeshHandle mesh);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_render_job_create(
IntPtr scene,

View File

@@ -0,0 +1,93 @@
using System;
using System.Runtime.InteropServices;
using Windows.Storage.Streams;
using WinRT;
namespace SimpleRayTracer.Interop;
// Allows zero-copy access to WriteableBitmap.PixelBuffer.
internal static class WinRtBufferByteAccess
{
private static readonly Guid IID_IBufferByteAccess = new("905a0fef-bc53-11df-8c49-001e4fc686da");
public static unsafe bool TryGetPointer(IBuffer buffer, out byte* ptr)
{
ptr = null;
if (buffer is null)
{
return false;
}
var unk = IntPtr.Zero;
var releaseUnk = false;
var acc = IntPtr.Zero;
try
{
if (buffer is not IWinRTObject winrt)
{
return false;
}
unk = winrt.NativeObject.ThisPtr;
if (unk == IntPtr.Zero)
{
return false;
}
Marshal.AddRef(unk);
releaseUnk = true;
var iid = IID_IBufferByteAccess;
var hr = Marshal.QueryInterface(unk, in iid, out acc);
if (hr < 0 || acc == IntPtr.Zero)
{
return false;
}
// vtable: [0]=QI, [1]=AddRef, [2]=Release, [3]=Buffer
var vtbl = *(IntPtr*)acc;
var bufferFnPtr = *((IntPtr*)vtbl + 3);
if (bufferFnPtr == IntPtr.Zero)
{
return false;
}
// IMPORTANT: COM uses stdcall on Windows.
var bufferFn = (delegate* unmanaged[Stdcall]<IntPtr, IntPtr*, int>)bufferFnPtr;
var raw = IntPtr.Zero;
hr = bufferFn(acc, &raw);
if (hr < 0 || raw == IntPtr.Zero)
{
return false;
}
ptr = (byte*)raw;
return true;
}
finally
{
if (acc != IntPtr.Zero)
{
Marshal.Release(acc);
}
if (releaseUnk && unk != IntPtr.Zero)
{
Marshal.Release(unk);
}
}
}
public static unsafe byte* GetPointerOrThrow(IBuffer buffer)
{
if (!TryGetPointer(buffer, out var ptr))
{
throw new NotSupportedException("WriteableBitmap.PixelBuffer does not expose IBufferByteAccess in this configuration.");
}
return ptr;
}
}

View File

@@ -274,6 +274,35 @@ SRT_API srt_result_e srt_scene_texture_set_sampler(srt_scene_t* scene,
uint32_t filter_mode /* srt_texture_filter_mode_e */);
/* Sky */
typedef enum srt_sky_kind_e
{
SRT_SKY_NONE = 0,
SRT_SKY_CONSTANT = 1,
SRT_SKY_HDR = 2,
} srt_sky_kind_e;
typedef struct srt_sky_desc_t
{
uint32_t kind; /* srt_sky_kind_e */
uint32_t data_size; /* bytes at `data` */
const void* data; /* points to a kind-specific POD struct */
} srt_sky_desc_t;
typedef struct srt_sky_constant_desc_t
{
srt_vec3_t color;
float intensity;
} srt_sky_constant_desc_t;
typedef struct srt_sky_hdr_desc_t
{
srt_texture_handle_t hdri;
float intensity;
} srt_sky_hdr_desc_t;
/* Generic, extensible sky setter (preferred). */
SRT_API srt_result_e srt_scene_set_sky(srt_scene_t* scene, const srt_sky_desc_t* desc);
SRT_API srt_result_e srt_scene_set_sky_none(srt_scene_t* scene);
SRT_API srt_result_e srt_scene_set_sky_constant(srt_scene_t* scene, srt_vec3_t color, float intensity);
SRT_API srt_result_e srt_scene_set_sky_hdr(srt_scene_t* scene, srt_texture_handle_t hdri, float intensity);

View File

@@ -65,10 +65,9 @@ static inline float next_float_down(float value)
vec3s offset_ray_origin(vec3s point, vec3s normal, vec3s w)
{
float c = glms_vec3_max(glms_vec3_abs(point)) * gamma(15);
vec3s error = (vec3s){c, c, c};
// float g = gamma(10);
// vec3s error = {fabsf(point.x) * g, fabsf(point.y) * g, fabsf(point.z) * g};
float g = gamma(3);
vec3s error = {fabsf(point.x) * g + 1e-5f, fabsf(point.y) * g + 1e-5f, fabsf(point.z) * g + 1e-5f}; // TODO: More accurate error estimation
float d = glms_vec3_dot(glms_vec3_abs(normal), error);
vec3s offset = glms_vec3_scale(normal, d);
@@ -530,6 +529,7 @@ hit_result_t ray_intersect_scene_closest(const ray_t* ray, const scene_t* scene)
hit_result_t ray_intersect_scene_any(const ray_t* ray, const scene_t* scene)
{
hit_result_t result = {0};
result.distance = FLT_MAX;
if (scene == NULL)
@@ -642,5 +642,6 @@ hit_result_t ray_intersect_scene_any(const ray_t* ray, const scene_t* scene)
result.model_id = UINT32_MAX;
result.instance_id = UINT32_MAX;
}
return result;
}

View File

@@ -665,52 +665,99 @@ SRT_API srt_result_e srt_scene_texture_set_sampler(srt_scene_t* scene,
/* ---------------- Sky ---------------- */
SRT_API srt_result_e srt_scene_set_sky_none(srt_scene_t* scene)
SRT_API srt_result_e srt_scene_set_sky(srt_scene_t* scene, const srt_sky_desc_t* desc)
{
if (scene == NULL)
if (scene == NULL || desc == NULL)
{
return SRT_INVALID_ARGUMENT;
}
free_current_sky(&scene->scene.lights);
return SRT_OK;
switch ((srt_sky_kind_e)desc->kind)
{
case SRT_SKY_NONE:
{
free_current_sky(&scene->scene.lights);
return SRT_OK;
}
case SRT_SKY_CONSTANT:
{
if (desc->data == NULL || desc->data_size != sizeof(srt_sky_constant_desc_t))
{
return SRT_INVALID_ARGUMENT;
}
const srt_sky_constant_desc_t* d = (const srt_sky_constant_desc_t*)desc->data;
free_current_sky(&scene->scene.lights);
constant_sky_data_t data = {
.color = srt_to_vec3(d->color),
.intensity = d->intensity,
};
scene->scene.lights.sky_light = sky_create_constant_sky(&data);
return SRT_OK;
}
case SRT_SKY_HDR:
{
if (desc->data == NULL || desc->data_size != sizeof(srt_sky_hdr_desc_t))
{
return SRT_INVALID_ARGUMENT;
}
const srt_sky_hdr_desc_t* d = (const srt_sky_hdr_desc_t*)desc->data;
texture_handle_t h = {.id = d->hdri.id};
if (get_texture(&scene->scene.textures, h) == NULL)
{
return SRT_NOT_FOUND;
}
free_current_sky(&scene->scene.lights);
scene->scene.lights.sky_light = sky_create_hdr_sky(&scene->scene.textures, h, d->intensity);
return SRT_OK;
}
default:
return SRT_INVALID_ARGUMENT;
}
}
SRT_API srt_result_e srt_scene_set_sky_none(srt_scene_t* scene)
{
const srt_sky_desc_t d = {
.kind = SRT_SKY_NONE,
.data_size = 0,
.data = NULL,
};
return srt_scene_set_sky(scene, &d);
}
SRT_API srt_result_e srt_scene_set_sky_constant(srt_scene_t* scene, srt_vec3_t color, float intensity)
{
if (scene == NULL)
{
return SRT_INVALID_ARGUMENT;
}
free_current_sky(&scene->scene.lights);
constant_sky_data_t data = {
.color = srt_to_vec3(color),
const srt_sky_constant_desc_t cd = {
.color = color,
.intensity = intensity,
};
scene->scene.lights.sky_light = sky_create_constant_sky(&data);
return SRT_OK;
const srt_sky_desc_t d = {
.kind = SRT_SKY_CONSTANT,
.data_size = (uint32_t)sizeof(cd),
.data = &cd,
};
return srt_scene_set_sky(scene, &d);
}
SRT_API srt_result_e srt_scene_set_sky_hdr(srt_scene_t* scene, srt_texture_handle_t hdri, float intensity)
{
if (scene == NULL)
{
return SRT_INVALID_ARGUMENT;
}
texture_handle_t h = {.id = hdri.id};
if (get_texture(&scene->scene.textures, h) == NULL)
{
return SRT_NOT_FOUND;
}
free_current_sky(&scene->scene.lights);
scene->scene.lights.sky_light = sky_create_hdr_sky(&scene->scene.textures, h, intensity);
return SRT_OK;
const srt_sky_hdr_desc_t hd = {
.hdri = hdri,
.intensity = intensity,
};
const srt_sky_desc_t d = {
.kind = SRT_SKY_HDR,
.data_size = (uint32_t)sizeof(hd),
.data = &hd,
};
return srt_scene_set_sky(scene, &d);
}
/* ---------------- Mesh loading (Assimp) ---------------- */

View File

@@ -382,16 +382,19 @@ path_output evaluate_bsdf_hdr_sky(const void* data, const light_shading_context_
return output;
}
ray_t shadow_ray = ray_create(offset_ray_origin(context->position, context->normal, context->wo), wi, 0.0f, 0.0f);
hit_result_t shadow_hit = {0};
vec3s origin = offset_ray_origin(context->position, context->normal, context->wo);
ray_t shadow_ray = ray_create(origin, wi, 0.0f, context->spread_angle);
hit_result_t shadow_hit;
if (context->scene != NULL)
{
shadow_hit = ray_intersect_scene_any(&shadow_ray, context->scene);
}
else
{
shadow_hit = (hit_result_t){0};
ray_intersect_bvh_any(&shadow_ray, context->bvh_tree->nodes, context->bvh_tree->primitive_indices, context->bvh_tree->triangles, 0, &shadow_hit);
}
if (shadow_hit.hit)
{
return output;

View File

@@ -206,7 +206,7 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
rendering_config_t config = {
.width = 1920 / 2,
.height = 1080 / 2,
.sample_count = 16 * 4,
.sample_count = 16 * 1,
.max_depth = 4,
.bucket_size = 64,
};