6 Commits

Author SHA1 Message Date
5183e73ca0 Fixe bug in pbr 2026-02-19 19:08:41 +09:00
b41ea60c02 Refactor codebase. Add punctual light support 2026-01-01 21:52:41 +09:00
acaaa2a86e Added handle system and improve resource management. 2025-12-31 23:50:22 +09:00
5c988108ef Fixed genergy conservation problem in specular lobe 2025-12-31 03:12:29 +09:00
84b2504a6f Updated api 2025-12-31 00:43:04 +09:00
f1d3dddb9a Change project structure;
Added new c# binding;
2025-12-30 20:54:05 +09:00
408 changed files with 5461 additions and 362344 deletions

View File

@@ -1,7 +0,0 @@
CompileFlags:
Add:
- -IF:/c/SimpleRayTracer/external
- -IF:/c/SimpleRayTracer/header
- -LF:/c/SimpleRayTracer/lib
- -lcglm
- -lassimp-vc143-mt

365
.gitignore vendored
View File

@@ -1 +1,366 @@
[Bb]uild/ [Bb]uild/
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
.vscode/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd

61
.vscode/tasks.json vendored
View File

@@ -1,61 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "CMake Build (Debug)",
"type": "shell",
"command": "cmake",
"args": [
"--build",
"f:\\c\\SimpleRayTracer\\build",
"--config",
"Debug"
],
"isBackground": false,
"problemMatcher": [],
"group": "build"
},
{
"label": "CMake Build (Debug)",
"type": "shell",
"command": "cmake",
"args": [
"--build",
"f:\\c\\SimpleRayTracer\\build",
"--config",
"Debug"
],
"isBackground": false,
"problemMatcher": [],
"group": "build"
},
{
"label": "CMake Build (Debug)",
"type": "shell",
"command": "cmake",
"args": [
"--build",
"f:\\c\\SimpleRayTracer\\build",
"--config",
"Debug"
],
"isBackground": false,
"problemMatcher": [],
"group": "build"
},
{
"label": "CMake Build (Debug)",
"type": "shell",
"command": "cmake",
"args": [
"--build",
"f:\\c\\SimpleRayTracer\\build",
"--config",
"Debug"
],
"isBackground": false,
"problemMatcher": [],
"group": "build"
}
]
}

View File

@@ -43,7 +43,7 @@ If you have powershell installed, you can run the build.ps1 script in project ro
### Multiple Importance Sampling (MIS) ### Multiple Importance Sampling (MIS)
- Balance heuristic weights BSDF and light sampling PDFs. - Balance heuristic weights BSDF and light sampling PDFs.
- Supports both directional lights and sky light via next-event estimation. Produce noise-free renders with low sample counts. - Supports directional lights, punctual lgiths, and sky light via next-event estimation. Produce noise-free renders with low sample counts.
### HDR Sky Lighting ### HDR Sky Lighting
- Supports importance sampling of environment maps using a hierarchical CDF over solid angles. - Supports importance sampling of environment maps using a hierarchical CDF over solid angles.
@@ -54,7 +54,7 @@ If you have powershell installed, you can run the build.ps1 script in project ro
- Faster convergence and lower variance than pure RNG at 64 spp. - Faster convergence and lower variance than pure RNG at 64 spp.
### Materials (BSDFs) ### Materials (BSDFs)
- Lambertian, and specular supported. - Oren-Naray and Multi-Scattered GGX support
- Designed to align with the OpenPBR material model. - Designed to align with the OpenPBR material model.
### Path Tracing ### Path Tracing
@@ -68,9 +68,9 @@ If you have powershell installed, you can run the build.ps1 script in project ro
- [ ] Standardize light unit - [ ] Standardize light unit
- [ ] General speed improvements - [ ] General speed improvements
- [ ] Add GPU backend with CUDA - [ ] Add GPU backend with CUDA
- [ ] Support for glossy microfacet models (GGX) - [x] Support for glossy microfacet models (GGX)
- [ ] Support for better diffuse models (Oren-Nayar) - [x] Support for better diffuse models (Oren-Nayar)
- [ ] Support for volumetric scattering (homogeneous media) - [ ] Support for volumetric scattering (homogeneous media)
- [ ] Light hierarchy for large emitter sets - [ ] Light hierarchy for large emitter sets
- [ ] Better multithreaded rendering - [x] Better multithreaded rendering
- [ ] GUI frontend with real-time control - [ ] GUI frontend with real-time control

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +0,0 @@
#ifndef MESH_H
#define MESH_H
#include "Material/Material.h"
#include "Geometry/Triangle.h"
#include "Rendering/Scene.h"
#include <stdint.h>
// TODO: Currently transformation does not work because we store every triangle in to the same buffer and when bulding the bvh, we only consider that triangle buffer.
// One solution for this is we can have two levels of bvh, one for scene and one for each mesh. The mesh level bvh will apply the transformation to the triangles.
// The scene level bvh only tells the ray which mesh to check and the mesh level bvh will tell the ray which triangle to check.
// For instancing, we may need another mesh_model_t struct to store the actual bvh and triangle data(like id and size), and each mesh_entity_t will have a transformation matrix and the id to that mesh_model_t.
// This way we can share the same triangle and bvh for multiple instances of the same mesh, and we can also apply different transformations to each instance.
typedef struct
{
mat4s local_to_world;
// New TLAS/BLAS identifiers.
uint32_t model_id;
uint32_t instance_id;
// Legacy fields (kept for now).
uint64_t triangle_id;
uint64_t triangle_count;
uint16_t material_id;
uint16_t material_count;
} mesh_handle_t;
mesh_handle_t mesh_load(const char* filename, scene_t* scene);
#endif // MESH_H

View File

@@ -1,53 +0,0 @@
#ifndef TRIANGLE_H
#define TRIANGLE_H
#include "Common.h"
#include "Geometry/AABB.h"
#include <stdint.h>
typedef struct
{
vec3s position;
vec3s normal;
vec3s tangent;
vec3s color;
vec2s uv;
} vertex_t;
typedef struct
{
vertex_t vertices[3];
vec3s face_normal;
uint8_t material_id;
} triangle_t;
// TODO: Handle material remove, we can use a lookup array.
typedef struct
{
uint64_t count;
uint64_t size;
triangle_t* buffer;
} triangle_collection_t;
bool triangle_collection_init(size_t size, triangle_collection_t* triangles);
void triangle_collection_resize(triangle_collection_t* collection, size_t size);
void triangle_collection_free(triangle_collection_t* collection);
void triangle_create(vertex_t v1, vertex_t v2, vertex_t v3, uint8_t material_id, triangle_collection_t* collection);
inline vec3s triangle_centroid(const triangle_t triangle)
{
return glms_vec3_scale(glms_vec3_add(glms_vec3_add(triangle.vertices[0].position, triangle.vertices[1].position), triangle.vertices[2].position), 1.0f / 3.0f);
}
inline aabb_t compute_bounds_triangle(triangle_t triangle)
{
aabb_t bounds;
bounds.min = glms_vec3_minv(triangle.vertices[0].position, glms_vec3_minv(triangle.vertices[1].position, triangle.vertices[2].position));
bounds.max = glms_vec3_maxv(triangle.vertices[0].position, glms_vec3_maxv(triangle.vertices[1].position, triangle.vertices[2].position));
return bounds;
}
#endif // TRIANGLE_H

View File

@@ -1,162 +0,0 @@
#ifndef LIGHT_H
#define LIGHT_H
#include "Algorithm/BVH.h"
#include "Algorithm/Sobol.h"
#include "Geometry/Triangle.h"
#include "Rendering/Texture.h"
#include "cglm/struct/vec3.h"
struct scene_t;
typedef struct
{
vec3s position;
vec3s normal;
vec3s geometric_normal;
vec3s tangent;
vec2s uv;
vec3s wo;
uint16_t bounce_depth;
const struct scene_t* scene;
const bvh_tree_t* bvh_tree;
const texture_collection_t* textures;
float spread_angle;
} light_shading_context_t;
typedef path_output (*evaluate_bsdf_sky_f) (const void* data, const light_shading_context_t* context, vec3s throughput, uint32_t sample_index);
typedef void (*sky_free_f) (void* data);
typedef struct
{
uint16_t data_size;
void* data;
evaluate_bsdf_sky_f evaluate_bsdf_sky;
sky_free_f free_sky_data;
} sky_light_t;
typedef enum
{
LIGHT_TYPE_POINT,
LIGHT_TYPE_SPOT,
LIGHT_TYPE_AREA,
} punctual_light_type_e;
typedef struct
{
vec3s position;
vec3s color;
float intensity;
punctual_light_type_e type;
} punctual_light_t;
typedef struct
{
vec3s direction;
vec3s color;
float intensity;
float angular_diameter;
} directional_light_t;
typedef struct
{
uint32_t punctual_light_count;
uint32_t directional_light_count;
uint32_t max_punctual_lights;
uint32_t max_directional_lights;
sky_light_t sky_light;
punctual_light_t* punctual_lights;
directional_light_t* directional_lights;
} light_collection_t;
typedef struct
{
uint32_t id;
bool is_punctual;
} light_entity_t;
inline bool light_collection_create(uint32_t max_punctual_lights, uint32_t max_directional_lights, light_collection_t* collection)
{
if (max_punctual_lights == 0 || max_directional_lights == 0)
{
return false;
}
collection->max_punctual_lights = max_punctual_lights;
collection->max_directional_lights = max_directional_lights;
collection->punctual_light_count = 0;
collection->directional_light_count = 0;
collection->punctual_lights = (punctual_light_t*)malloc(max_punctual_lights * sizeof(punctual_light_t));
if (collection->punctual_lights == NULL)
{
return false;
}
collection->directional_lights = (directional_light_t*)malloc(max_directional_lights * sizeof(directional_light_t));
if (collection->directional_lights == NULL)
{
free(collection->punctual_lights);
return false;
}
return true;
}
inline light_entity_t light_create_punctual_light(light_collection_t* collection)
{
if (collection->punctual_light_count >= collection->max_punctual_lights)
{
return (light_entity_t){0};
}
punctual_light_t light = {0};
collection->punctual_lights[collection->punctual_light_count] = light;
light_entity_t entity = {.id = collection->punctual_light_count, .is_punctual = true};
collection->punctual_light_count++;
return entity;
}
inline light_entity_t light_create_directional_light(light_collection_t* collection)
{
if (collection->directional_light_count >= collection->max_directional_lights)
{
return (light_entity_t){0};
}
directional_light_t light = {0};
collection->directional_lights[collection->directional_light_count] = light;
light_entity_t entity = {.id = collection->directional_light_count, .is_punctual = false};
collection->directional_light_count++;
return entity;
}
inline void light_collection_free(light_collection_t* collection)
{
free(collection->punctual_lights);
free(collection->directional_lights);
if (collection->sky_light.free_sky_data != NULL)
{
collection->sky_light.free_sky_data(collection->sky_light.data);
}
free(collection->sky_light.data);
collection->max_punctual_lights = 0;
collection->max_directional_lights = 0;
collection->punctual_light_count = 0;
collection->directional_light_count = 0;
collection->punctual_lights = NULL;
collection->directional_lights = NULL;
collection->sky_light.data = NULL;
}
#endif // LIGHT_H

View File

@@ -1,6 +0,0 @@
#ifndef SHADING_CONTEXT_H
#define SHADING_CONTEXT_H
#endif // SHADING_CONTEXT_H

View File

@@ -1,81 +0,0 @@
#ifndef SCENE_H
#define SCENE_H
#include "Algorithm/BVH.h"
#include "Algorithm/TLAS.h"
#include "Camera.h"
#include "Lighting/Light.h"
#include "Material/Material.h"
#include "Geometry/Triangle.h"
#include "Rendering/RenderTarget.h"
#include "Rendering/Texture.h"
#include "cglm/struct/mat3.h"
#include "cglm/struct/mat4.h"
#include <stdbool.h>
typedef struct scene_t
{
bool active;
triangle_collection_t triangles; // local-space triangles
bvh_tree_t blas; // BVH over local-space triangles
aabb_t local_bounds;
} mesh_model_t;
typedef struct
{
bool active;
uint32_t model_id;
mat4s local_to_world;
mat4s world_to_local;
mat3s normal_matrix; // transpose(inverse(mat3(local_to_world)))
aabb_t world_bounds;
} mesh_instance_t;
typedef struct
{
uint32_t count;
uint32_t capacity;
mesh_model_t* buffer;
} mesh_model_collection_t;
typedef struct
{
uint32_t count;
uint32_t capacity;
mesh_instance_t* buffer;
} mesh_instance_collection_t;
typedef struct
{
camera_t camera;
// Legacy: single big triangle array (kept for now for procedural geometry).
bvh_tree_t bvh_tree;
triangle_collection_t triangles;
// New: TLAS/BLAS mesh system.
mesh_model_collection_t mesh_models;
mesh_instance_collection_t mesh_instances;
tlas_tree_t tlas;
bool tlas_dirty;
material_collection_t materials;
texture_collection_t textures;
light_collection_t lights;
} scene_t;
bool scene_init(scene_t* scene, uint64_t triangle_count, uint16_t texture_count, uint8_t material_count, uint32_t punctual_light_count);
bool scene_build_bvh(scene_t* scene);
void scene_free(scene_t* scene);
// Mesh model/instance API (simple first, flexible later).
uint32_t scene_add_mesh_model(scene_t* scene, uint64_t triangle_reserve);
uint32_t scene_add_mesh_instance(scene_t* scene, uint32_t model_id, mat4s local_to_world);
void scene_remove_mesh_instance(scene_t* scene, uint32_t instance_id);
void scene_set_mesh_instance_transform(scene_t* scene, uint32_t instance_id, mat4s local_to_world);
#endif // SCENE_H

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Application
x:Class="SimpleRayTracer.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SimpleRayTracer">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.UI.Xaml.Shapes;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace SimpleRayTracer
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : Application
{
private Window? _window;
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
InitializeComponent();
}
/// <summary>
/// Invoked when the application is launched.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
_window = new MainWindow();
_window.Activate();
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,147 @@
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
namespace SimpleRayTracer.Interop;
internal sealed partial class InteropSmokeTest : IDisposable
{
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 is not null || _job is not null)
throw new InvalidOperationException("Already started");
_scene = SrtScene.Create(triangleCount: 0, textureCount: 1, materialCount: 8, punctualLightCount: 1);
// 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);
// Basic sky so we don't render into black/undefined environment.
_scene.SetSky(SrtSky.Constant(new Vector3(1, 1, 1), intensity: 1.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);
_scene.LoadMeshFromFile("F:/c/SimpleRayTracer/native/assets/sponza.fbx");
_scene.Commit();
_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(() =>
{
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: {ex}");
}
});
}
public unsafe void CopyLatestToBitmap()
{
if (_job is null || _bitmap is null)
return;
// Ensure we have enough capacity
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;
if ((uint)_bitmap.PixelWidth != w || (uint)_bitmap.PixelHeight != h)
{
// Bitmap size mismatch (shouldn't happen in this smoke test).
return;
}
var pixelBuffer = _bitmap.PixelBuffer;
var expectedBytes = checked(w * h * 4u);
if (pixelBuffer.Length < expectedBytes)
{
// 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);
}
_bitmap.Invalidate();
}
public bool IsDone
{
get
{
if (_job is null)
return true;
return _job.IsDone;
}
}
public void Dispose()
{
_job?.Dispose();
_job = null;
_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

@@ -0,0 +1,278 @@
using System;
using System.Runtime.InteropServices;
namespace SimpleRayTracer.Interop;
internal static unsafe class SrtNative
{
private const string _DLL = "SimpleRayTracerNative";
internal enum Result : int
{
Ok = 0,
Error = 1,
InvalidArgument = 2,
OutOfRange = 3,
OutOfCapacity = 4,
NotFound = 5,
}
[StructLayout(LayoutKind.Sequential)]
internal struct Vec2
{
public float x, y;
public static Vec2 Of(float x, float y) => new() { x = x, y = y };
}
[StructLayout(LayoutKind.Sequential)]
internal struct Vec3
{
public float x, y, z;
public static Vec3 Of(float x, float y, float z) => new() { x = x, y = y, z = z };
}
[StructLayout(LayoutKind.Sequential)]
internal struct Quat
{
public float x, y, z, w;
public static Quat Identity() => new() { w = 1f };
}
[StructLayout(LayoutKind.Sequential)]
internal struct Mat4
{
// Column-major float[16]
public fixed float m[16];
public static Mat4 Identity()
{
var r = new Mat4();
r.m[0] = 1;
r.m[5] = 1;
r.m[10] = 1;
r.m[15] = 1;
return r;
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct CameraParams
{
public Vec3 position;
public Quat rotation;
public float focal_length;
public float size_x;
public float aspect_ratio;
}
[StructLayout(LayoutKind.Sequential)]
internal struct DirectionalLight
{
public Vec3 direction;
public Vec3 color;
public float intensity;
public float angular_diameter;
}
[StructLayout(LayoutKind.Sequential)]
internal struct TextureHandle
{
public uint id;
public uint generation;
public static TextureHandle Invalid() => new() { id = 0xFFFFFFFF, generation = 0 };
}
[StructLayout(LayoutKind.Sequential)]
internal struct MaterialHandle
{
public byte id;
}
[StructLayout(LayoutKind.Sequential)]
internal struct StandardLitProperties
{
public Vec3 albedo;
public float diffuse_roughness;
public float roughness;
public float metallic;
public TextureHandle albedo_texture;
public TextureHandle normal_texture;
public TextureHandle roughness_texture;
public TextureHandle metallic_texture;
}
internal enum TextureWrapMode : uint
{
Repeat = 0,
Clamp = 1,
}
internal enum TextureFilterMode : uint
{
Nearest = 0,
Linear = 1,
}
internal enum TextureStride : uint
{
UInt8 = 1,
UInt16 = 2,
Float32 = 4,
}
[StructLayout(LayoutKind.Sequential)]
internal struct RenderingConfig
{
public uint width;
public uint height;
public uint sample_count;
public byte max_depth;
public uint bucket_size;
}
[StructLayout(LayoutKind.Sequential)]
internal struct Vertex
{
public Vec3 position;
public Vec3 normal;
public Vec3 tangent;
public Vec3 color;
public Vec2 uv;
}
[StructLayout(LayoutKind.Sequential)]
internal struct Triangle
{
public Vertex v0;
public Vertex v1;
public Vertex v2;
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);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern void srt_scene_destroy(IntPtr scene);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_commit(IntPtr scene);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_camera_set(IntPtr scene, CameraParams* @params);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_add_directional_light(IntPtr scene, DirectionalLight* light, out uint id);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_create_standard_lit_material(IntPtr scene, StandardLitProperties* props, Vec3 emission, out MaterialHandle mat);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_texture_load(IntPtr scene, [MarshalAs(UnmanagedType.LPUTF8Str)] string filename, byte srgb, byte mipmap, uint stride, out TextureHandle texture);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_texture_set_sampler(IntPtr scene, TextureHandle texture, uint wrapMode, uint filterMode);
[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);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_set_sky_hdr(IntPtr scene, TextureHandle hdri, float intensity);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_add_mesh_model(IntPtr scene, ulong triangle_reserve, out uint model_id);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_scene_mesh_model_set_triangles(IntPtr scene, uint model_id, Triangle* triangles, uint triangle_count);
[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,
RenderingConfig* config,
uint aov_flags,
uint rendering_mode,
uint seed,
out IntPtr job);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern void srt_render_job_destroy(IntPtr job);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_render_job_start(IntPtr job);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern void srt_render_job_request_stop(IntPtr job);
// C bool is 1 byte; use byte to avoid marshaling mismatches.
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_render_job_is_done(IntPtr job, out byte done);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_render_job_get_aov_desc(IntPtr job, uint aov_index, out uint width, out uint height, out uint strideBytes);
[DllImport(_DLL, CallingConvention = CallingConvention.Cdecl)]
internal static extern Result srt_render_job_copy_aov_bgra8(IntPtr job, uint aov_index, byte* dst, uint dstStrideBytes);
internal const uint SRT_AOV_BEAUTY = 1u << 0;
internal const uint SRT_RENDER_TILE_BASED = 1;
internal const uint SRT_AOV_BEAUTY_INDEX = 0;
}

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

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="SimpleRayTracer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SimpleRayTracer"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="SimpleRayTracer">
<Window.SystemBackdrop>
<MicaBackdrop />
</Window.SystemBackdrop>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Spacing="12" Margin="12">
<Button x:Name="RunSmokeTestButton" Content="Run Interop Smoke Test" Click="RunSmokeTestButton_Click" />
<TextBlock x:Name="StatusText" VerticalAlignment="Center" />
</StackPanel>
<ScrollViewer Grid.Row="1" Margin="12">
<Image x:Name="RenderImage" Stretch="Uniform" />
</ScrollViewer>
</Grid>
</Window>

View File

@@ -0,0 +1,62 @@
using System;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using SimpleRayTracer.Interop;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace SimpleRayTracer
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : Window
{
private InteropSmokeTest? _smoke;
private DispatcherQueueTimer? _timer;
public MainWindow()
{
InitializeComponent();
}
private void RunSmokeTestButton_Click(object sender, RoutedEventArgs e)
{
RunSmokeTestButton.IsEnabled = false;
StatusText.Text = "Starting...";
try
{
_timer?.Stop();
_timer = null;
_smoke?.Dispose();
_smoke = new InteropSmokeTest();
_smoke.StartAsync(width: 640, height: 360);
RenderImage.Source = _smoke.Bitmap;
StatusText.Text = "Rendering...";
_timer = DispatcherQueue.CreateTimer();
_timer.Interval = TimeSpan.FromMilliseconds(33);
_timer.Tick += (_, _) =>
{
_smoke?.CopyLatestToBitmap();
if (_smoke?.IsDone == true)
{
StatusText.Text = "Done";
_timer?.Stop();
}
};
_timer.Start();
}
catch (Exception ex)
{
StatusText.Text = ex.Message;
RunSmokeTestButton.IsEnabled = true;
}
}
}
}

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity
Name="f3ab7e56-7fa5-4a20-a3e7-94cab61bcb0d"
Publisher="CN=Misaki"
Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="f3ab7e56-7fa5-4a20-a3e7-94cab61bcb0d" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>SimpleRayTracer</DisplayName>
<PublisherDisplayName>Misaki</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="SimpleRayTracer"
Description="SimpleRayTracer"
BackgroundColor="transparent"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>

View File

@@ -0,0 +1,10 @@
{
"profiles": {
"SimpleRayTracer (Package)": {
"commandName": "MsixPackage"
},
"SimpleRayTracer (Unpackaged)": {
"commandName": "Project"
}
}
}

View File

@@ -0,0 +1,77 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>SimpleRayTracer</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Platforms>x86;x64;ARM64</Platforms>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
<PublishProfile>win-$(Platform).pubxml</PublishProfile>
<UseWinUI>true</UseWinUI>
<WinUISDKReferences>false</WinUISDKReferences>
<EnableMsixTooling>true</EnableMsixTooling>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
<PackageReference Include="WinUIEx" Version="2.9.0" />
</ItemGroup>
<!--
Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
Tools extension to be activated for this project even if the Windows App SDK Nuget
package has not yet been restored.
-->
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
<ProjectCapability Include="Msix" />
</ItemGroup>
<!--
Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution
Explorer "Package and Publish" context menu entry to be enabled for this project even if
the Windows App SDK Nuget package has not yet been restored.
-->
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
<HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
</PropertyGroup>
<!-- Publish Properties -->
<PropertyGroup>
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
<PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed>
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Content Include="..\..\build\bin\$(Configuration)\SimpleRayTracerNative.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="..\..\build\bin\$(Configuration)\cglm.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="..\..\build\bin\$(Configuration)\assimp-vc143-mt.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,13 @@
<Solution>
<Configurations>
<Platform Name="ARM64" />
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Project Path="SimpleRayTracer.csproj">
<Platform Solution="*|ARM64" Project="ARM64" />
<Platform Solution="*|x64" Project="x64" />
<Platform Solution="*|x86" Project="x86" />
<Deploy />
</Project>
</Solution>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="SimpleRayTracer.app"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- The ID below informs the system that this application is compatible with OS features first introduced in Windows 10.
It is necessary to support features in unpackaged applications, for example the custom titlebar implementation.
For more info see https://docs.microsoft.com/windows/apps/windows-app-sdk/use-windows-app-sdk-run-time#declare-os-compatibility-in-your-application-manifest -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
</assembly>

7
native/.clangd Normal file
View File

@@ -0,0 +1,7 @@
CompileFlags:
Add:
- -IF:/c/SimpleRayTracer/native/external
- -IF:/c/SimpleRayTracer/native/header
- -LF:/c/SimpleRayTracer/native/lib
- -lcglm
- -lassimp-vc143-mt

View File

View File

@@ -1,7 +1,7 @@
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 23)
set(PROJECT_NAME SimpleRayTracer) set(PROJECT_NAME SimpleRayTracer)
project(${PROJECT_NAME} LANGUAGES C) project(${PROJECT_NAME} LANGUAGES C)
@@ -16,12 +16,25 @@ set(LIBRARY_PATH "${CMAKE_SOURCE_DIR}/lib")
# Recursively find all .c files in the Sources directory # Recursively find all .c files in the Sources directory
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS "${SOURCE_DIR}/*.c") file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS "${SOURCE_DIR}/*.c")
add_executable(${PROJECT_NAME} WIN32 ${SOURCES}) # App sources (exclude interop exports; those live in the Native DLL)
set(APP_SOURCES ${SOURCES})
list(FILTER APP_SOURCES EXCLUDE REGEX ".*/Interop/SrtInterop\\.c$")
add_executable(${PROJECT_NAME} WIN32 ${APP_SOURCES})
# Native DLL for C# interop (keeps existing exe target intact)
set(NATIVE_SOURCES ${SOURCES})
# Exclude the app entrypoint from the DLL
list(FILTER NATIVE_SOURCES EXCLUDE REGEX ".*/[Mm]ain\\.c$")
add_library(${PROJECT_NAME}Native SHARED ${NATIVE_SOURCES})
# Include directories # Include directories
target_include_directories(${PROJECT_NAME} PRIVATE ${EXTERNAL_DIR}) target_include_directories(${PROJECT_NAME} PRIVATE ${EXTERNAL_DIR})
target_include_directories(${PROJECT_NAME} PRIVATE ${HEADER_DIR}) target_include_directories(${PROJECT_NAME} PRIVATE ${HEADER_DIR})
target_include_directories(${PROJECT_NAME}Native PRIVATE ${EXTERNAL_DIR})
target_include_directories(${PROJECT_NAME}Native PRIVATE ${HEADER_DIR})
# Define imported target for cglm # Define imported target for cglm
add_library(cglm_shared SHARED IMPORTED) add_library(cglm_shared SHARED IMPORTED)
set_target_properties(cglm_shared PROPERTIES set_target_properties(cglm_shared PROPERTIES
@@ -43,17 +56,29 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
assimp_shared assimp_shared
) )
target_link_libraries(${PROJECT_NAME}Native PRIVATE
cglm_shared
assimp_shared
)
# target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBRARY_PATH}/cglm.lib) # target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBRARY_PATH}/cglm.lib)
# target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBRARY_PATH}/assimp-vc143-mt.lib) # target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBRARY_PATH}/assimp-vc143-mt.lib)
if(OpenMP_C_FOUND) if(OpenMP_C_FOUND)
target_link_libraries(${PROJECT_NAME} PRIVATE OpenMP::OpenMP_C) target_link_libraries(${PROJECT_NAME} PRIVATE OpenMP::OpenMP_C)
target_link_libraries(${PROJECT_NAME}Native PRIVATE OpenMP::OpenMP_C)
endif() endif()
set_target_properties(${PROJECT_NAME} PROPERTIES set_target_properties(${PROJECT_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
) )
set_target_properties(${PROJECT_NAME}Native PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)
target_compile_definitions(${PROJECT_NAME}Native PRIVATE SRT_INTEROP_BUILD=1)
if(WIN32) if(WIN32)
add_custom_command( add_custom_command(
TARGET ${PROJECT_NAME} POST_BUILD TARGET ${PROJECT_NAME} POST_BUILD
@@ -63,6 +88,15 @@ if(WIN32)
VERBATIM VERBATIM
COMMAND_EXPAND_LISTS COMMAND_EXPAND_LISTS
) )
add_custom_command(
TARGET ${PROJECT_NAME}Native POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_RUNTIME_DLLS:${PROJECT_NAME}Native>
$<TARGET_FILE_DIR:${PROJECT_NAME}Native>
VERBATIM
COMMAND_EXPAND_LISTS
)
endif() endif()
add_custom_command( add_custom_command(

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 194 KiB

View File

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 194 KiB

View File

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

View File

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 134 KiB

View File

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

View File

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 170 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

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