10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/MaterialTest/Imports.cs:
--------------------------------------------------------------------------------
1 | global using System;
2 | global using System.Collections.Concurrent;
3 | global using System.Collections.Generic;
4 | global using System.Diagnostics;
5 | global using System.IO;
6 | global using System.Linq;
7 | global using System.Numerics;
8 | global using System.Text.Json;
9 | global using System.Text.Json.Serialization;
10 | global using System.Threading;
11 | global using System.Threading.Tasks;
12 |
13 | global using TinyEmbree;
14 | global using SimpleImageIO;
15 |
16 | global using SeeSharp;
17 | global using SeeSharp.Cameras;
18 | global using SeeSharp.Common;
19 | global using SeeSharp.Experiments;
20 | global using SeeSharp.Geometry;
21 | global using SeeSharp.Images;
22 | global using SeeSharp.Integrators;
23 | global using SeeSharp.Integrators.Bidir;
24 | global using SeeSharp.Integrators.Common;
25 | global using SeeSharp.Integrators.Util;
26 | global using SeeSharp.Sampling;
27 | global using SeeSharp.Shading;
28 | global using SeeSharp.Shading.Background;
29 | global using SeeSharp.Shading.Emitters;
30 | global using SeeSharp.Shading.Materials;
31 |
32 | global using SeeSharp.Blazor;
--------------------------------------------------------------------------------
/MaterialTest/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
3 | @Body
4 |
--------------------------------------------------------------------------------
/MaterialTest/MaterialTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | enable
7 | preview
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/MaterialTest/Pages/Experiment.razor:
--------------------------------------------------------------------------------
1 | @using SeeSharp.Experiments
2 | @using SeeSharp
3 | @using SeeSharp.Blazor
4 |
5 | @inject IJSRuntime JS
6 |
7 | @page "/Experiment"
8 |
9 |
22 |
23 | An error has occurred. This application may no longer respond until reloaded.
24 |
25 |
26 | An unhandled exception has occurred. See browser dev tools for details.
27 |
28 | Reload
29 | 🗙
30 |
7 |
8 | @code {
9 | [Parameter]
10 | public string Title { get; set; } = "Settings";
11 |
12 | [Parameter]
13 | public RenderFragment ChildContent { get; set; }
14 | }
--------------------------------------------------------------------------------
/SeeSharp.Blazor/SettingsGroup.razor.css:
--------------------------------------------------------------------------------
1 | .render-setting-container {
2 | display: flex;
3 | flex-direction: column;
4 | flex-wrap: wrap;
5 | justify-content: space-between;
6 | height: max-content;
7 |
8 | background-color: #e6f1f4;
9 | padding: 0.5em;
10 | border-style: solid;
11 | border-width: 1pt;
12 | border-color: darkgrey;
13 | }
14 |
15 | .render-setting-title {
16 | margin-top: 0;
17 | margin-bottom: 6pt;
18 | border-bottom: 1pt solid darkgray;
19 | font-weight: 600;
20 | }
--------------------------------------------------------------------------------
/SeeSharp.Blazor/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using Microsoft.AspNetCore.Components.Web
2 |
3 | @using SeeSharp
--------------------------------------------------------------------------------
/SeeSharp.Examples/Imports.cs:
--------------------------------------------------------------------------------
1 | global using System;
2 | global using System.Collections.Concurrent;
3 | global using System.Collections.Generic;
4 | global using System.Diagnostics;
5 | global using System.IO;
6 | global using System.Linq;
7 | global using System.Numerics;
8 | global using System.Text.Json;
9 | global using System.Text.Json.Serialization;
10 | global using System.Threading;
11 | global using System.Threading.Tasks;
12 |
13 | global using TinyEmbree;
14 | global using SimpleImageIO;
15 |
16 | global using SeeSharp;
17 | global using SeeSharp.Cameras;
18 | global using SeeSharp.Common;
19 | global using SeeSharp.Experiments;
20 | global using SeeSharp.Geometry;
21 | global using SeeSharp.Images;
22 | global using SeeSharp.Integrators;
23 | global using SeeSharp.Integrators.Bidir;
24 | global using SeeSharp.Integrators.Common;
25 | global using SeeSharp.Integrators.Util;
26 | global using SeeSharp.Sampling;
27 | global using SeeSharp.Shading;
28 | global using SeeSharp.Shading.Background;
29 | global using SeeSharp.Shading.Emitters;
30 | global using SeeSharp.Shading.Materials;
31 |
--------------------------------------------------------------------------------
/SeeSharp.Examples/MakeFigure.py:
--------------------------------------------------------------------------------
1 | import figuregen
2 | from figuregen.util.image import Cropbox
3 | from figuregen.util.templates import FullSizeWithCrops
4 | import simpleimageio as sio
5 | import sys, os
6 |
7 | def make_figure(dirname, method_names):
8 | """
9 | Creates a simple overview figure using the FullSizeWithCrops template.
10 | Assumes the given directory contains a reference image and subdirectories for each method:
11 |
12 | - Reference.exr
13 | - method_names[0].exr
14 | - method_names[1].exr
15 | """
16 | names = ["Reference"]
17 | names.extend(method_names)
18 | return FullSizeWithCrops(
19 | reference_image=sio.read(os.path.join(dirname, "Reference.exr")),
20 | method_images=[
21 | sio.read(os.path.join(dirname, f"{name}.exr"))
22 | for name in method_names
23 | ],
24 | method_names=names,
25 | crops=[
26 | Cropbox(top=345, left=25, width=64, height=48, scale=4),
27 | Cropbox(top=155, left=200, width=64, height=48, scale=4),
28 | ]
29 | ).figure
30 |
31 | if __name__ == "__main__":
32 | result_dir = sys.argv[1]
33 |
34 | method_names = []
35 | for i in range(2, len(sys.argv)):
36 | method_names.append(sys.argv[i])
37 |
38 | # Find all scenes by enumerating the result directory
39 | rows = []
40 | for path in os.listdir(result_dir):
41 | if not os.path.isdir(os.path.join(result_dir, path)):
42 | continue
43 | try:
44 | rows.extend(make_figure(os.path.join(result_dir, path), method_names))
45 | except:
46 | print(f"skipping scene with invalid data: {path}")
47 |
48 | figuregen.figure(rows, 18, os.path.join(result_dir, "Overview.pdf"))
--------------------------------------------------------------------------------
/SeeSharp.Examples/MisCompensation.dib:
--------------------------------------------------------------------------------
1 | #!meta
2 |
3 | {"kernelInfo":{"defaultKernelName":null,"items":[{"name":"csharp","languageName":"C#","aliases":["c#","cs"]},{"name":"fsharp","languageName":"F#","aliases":["f#","fs"]},{"name":"pwsh","languageName":"PowerShell","aliases":["powershell"]},{"name":"javascript","languageName":"JavaScript","aliases":["js"]},{"name":"html","languageName":"HTML"},{"name":"sql","languageName":"SQL"},{"name":"kql","languageName":"KQL"},{"name":"mermaid","languageName":"Mermaid"},{"name":"httpRequest","languageName":"http"},{"name":"value"}]}}
4 |
5 | #!csharp
6 |
7 | #r "nuget: TinyEmbree"
8 | #r "nuget: SimpleImageIO"
9 | #r "../SeeSharp/bin/Debug/net7.0/SeeSharp.dll"
10 |
11 | using SeeSharp;
12 | using SeeSharp.Integrators;
13 | using SimpleImageIO;
14 | using SimpleImageIO.FlipBook;
15 |
16 | // Avoids slowing down the Jupyter Kernel with console spam
17 | SeeSharp.Common.ProgressBar.Silent = true;
18 |
19 | HTML(FlipBook.MakeHeader()).Display();
20 |
21 | #!csharp
22 |
23 | var scene = Scene.LoadFromFile("../Data/Scenes/simplebackground.json");
24 | scene.FrameBuffer = new(512, 512, "");
25 | scene.Prepare();
26 |
27 | new PathTracer() {
28 | TotalSpp = 10
29 | }.Render(scene);
30 | var plain = scene.FrameBuffer.Image;
31 |
32 | #!csharp
33 |
34 | scene.FrameBuffer = new(512, 512, "");
35 | (scene.Background as SeeSharp.Shading.Background.EnvironmentMap).BuildSamplingGrid(true);
36 | scene.Prepare();
37 |
38 | new PathTracer() {
39 | TotalSpp = 10
40 | }.Render(scene);
41 |
42 | HTML(FlipBook.Make(("plain", plain), ("comp", scene.FrameBuffer.Image)))
43 |
--------------------------------------------------------------------------------
/SeeSharp.Examples/PathVsVcm.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Experiments;
2 | using SeeSharp.Integrators;
3 | using SeeSharp.Integrators.Bidir;
4 | using System.Collections.Generic;
5 |
6 | namespace SeeSharp.Examples;
7 |
8 | ///
9 | /// Renders a scene with a path tracer and with VCM.
10 | ///
11 | class PathVsVcm : Experiment {
12 | public override List MakeMethods() => [
13 | new("PathTracer", new PathTracer() { TotalSpp = 4 }),
14 | new("Vcm", new VertexConnectionAndMerging() { NumIterations = 2 })
15 | ];
16 | }
--------------------------------------------------------------------------------
/SeeSharp.Examples/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using SeeSharp.Experiments;
3 | using SeeSharp.Images;
4 | using SeeSharp.Examples;
5 |
6 | // Register the directory as a scene file provider.
7 | // Asides from the geometry, it is also used as a reference image cache.
8 | SceneRegistry.AddSource("Data/Scenes");
9 |
10 | // Configure a benchmark to compare path tracing and VCM on the CornellBox
11 | // at 512x512 resolution. Display images in tev during rendering (localhost, default port)
12 | Benchmark benchmark = new(new PathVsVcm(), [
13 | SceneRegistry.LoadScene("CornellBox", maxDepth: 5),
14 | // SceneRegistry.LoadScene("CornellBox", maxDepth: 2).WithName("CornellBoxDirectIllum")
15 | ], "Results/PathVsVcm", 512, 512, FrameBuffer.Flags.SendToTev);
16 |
17 | // Render the images
18 | benchmark.Run();
19 |
20 | // Optional, but usually a good idea: assemble the rendering results in an overview
21 | // figure using a Python script.
22 | Process.Start("python", "./SeeSharp.Examples/MakeFigure.py Results/PathVsVcm PathTracer Vcm")
23 | .WaitForExit();
24 |
25 | // For our README file, we further convert the pdf to png with ImageMagick
26 | Process.Start("magick", "-density 300 ./Results/PathVsVcm/Overview.pdf ExampleFigure.png")
27 | .WaitForExit();
28 |
--------------------------------------------------------------------------------
/SeeSharp.Examples/SeeSharp.Examples.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Exe
9 | net9.0
10 | false
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/SeeSharp.IntegrationTests/BidirZeroLightPaths.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Geometry;
2 | using SeeSharp.Integrators;
3 | using SeeSharp.Integrators.Bidir;
4 | using SeeSharp.Shading.Emitters;
5 | using SimpleImageIO;
6 | using System.Diagnostics;
7 | using System.Numerics;
8 |
9 | namespace SeeSharp.IntegrationTests {
10 | class Dummy : VertexConnectionAndMerging {
11 | protected override void OnStartIteration(uint iteration) {
12 | NumLightPaths = 0;
13 | }
14 |
15 | protected override void OnNextEventSample(RgbColor weight, float misWeight, CameraPath cameraPath, float pdfNextEvent, float pdfHit, in BidirPathPdfs pathPdfs, Emitter emitter, Vector3 lightToSurface, SurfacePoint lightPoint) {
16 | Debug.Assert(float.IsFinite(misWeight));
17 | }
18 | }
19 |
20 | static class BidirZeroLightPaths {
21 | public static void Run() {
22 | var scene = Scene.LoadFromFile("Data/Scenes/CornellBox/CornellBox.json");
23 | scene.FrameBuffer = new Images.FrameBuffer(512, 512, "test.exr",
24 | Images.FrameBuffer.Flags.SendToTev);
25 | scene.Prepare();
26 |
27 | var integrator = new Dummy() {
28 | NumIterations = 4,
29 | MaxDepth = 5
30 | };
31 | integrator.Render(scene);
32 |
33 | scene.FrameBuffer = new Images.FrameBuffer(512, 512, "test.exr",
34 | Images.FrameBuffer.Flags.SendToTev);
35 | integrator.Render(scene);
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/SeeSharp.IntegrationTests/ConsoleUtils.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Common;
2 | using System;
3 | using System.Threading;
4 |
5 | namespace SeeSharp.IntegrationTests;
6 |
7 | static class ConsoleUtils {
8 | public static void TestProgressBar() {
9 | SeeSharp.Common.ProgressBar bar = new(10);
10 | bar.Start(100);
11 | var timer = System.Diagnostics.Stopwatch.StartNew();
12 | for (int i = 0; i < 100; ++i) {
13 | Thread.Sleep(10);
14 | bar.ReportDone(1);
15 |
16 | if (i == 2)
17 | Console.WriteLine("Hi there!");
18 |
19 | if (i == 4)
20 | Console.Write("gimme your attention!");
21 | }
22 | Console.WriteLine($"actual time: {timer.ElapsedMilliseconds}");
23 | }
24 |
25 | public static void TestLogger() {
26 | Logger.Log("This is an info thing");
27 | Logger.Log("And now a WARNING", Verbosity.Warning);
28 | Logger.Log("And now a error message!!!!", Verbosity.Error);
29 |
30 | Logger.Verbosity = Verbosity.Warning;
31 | Logger.Log("This is an info thing");
32 | Logger.Log("And now a WARNING", Verbosity.Warning);
33 | Logger.Log("And now a error message!!!!", Verbosity.Error);
34 |
35 | Logger.Verbosity = Verbosity.Error;
36 | Logger.Log("This is an info thing");
37 | Logger.Log("And now a WARNING", Verbosity.Warning);
38 | Logger.Log("And now a error message!!!!", Verbosity.Error);
39 | }
40 | }
--------------------------------------------------------------------------------
/SeeSharp.IntegrationTests/OutlierCacheTest.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Experiments;
2 | using SeeSharp.Images;
3 | using SeeSharp.Integrators;
4 | using SeeSharp.Integrators.Bidir;
5 | using SeeSharp.Integrators.Util;
6 |
7 | namespace SeeSharp.IntegrationTests;
8 |
9 | static class OutlierCacheTest {
10 | public static void RenderPT() {
11 | SceneRegistry.AddSourceRelativeToScript("../Data/Scenes");
12 | using var scene = SceneRegistry.LoadScene("GlassCubes").MakeScene();
13 |
14 | scene.FrameBuffer = new FrameBuffer(640, 480, "test.exr", FrameBuffer.Flags.SendToTev);
15 | scene.Prepare();
16 |
17 | var integrator = new PathTracer() {
18 | TotalSpp = 4,
19 | MaxDepth = 10,
20 | BaseSeed = 1234,
21 | };
22 | integrator.Render(scene);
23 |
24 | Pixel pixel = new(628, 428);
25 |
26 | // Get the strongest path sample in this pixel
27 | var q = scene.FrameBuffer.OutlierCache.GetPixelOutlier(pixel);
28 | float best = 0;
29 | int iteration = -1;
30 | foreach (var i in q.UnorderedItems) {
31 | if (i.Priority > best) {
32 | best = i.Priority;
33 | iteration = i.Element.Iteration;
34 | }
35 | }
36 |
37 | var (graph, _) = integrator.ReplayPixel(scene, pixel, iteration);
38 |
39 | scene.FrameBuffer = new(640, 480, "path.exr", FrameBuffer.Flags.SendToTev);
40 | PathGraphRenderer graphVis = new() {};
41 | graphVis.Render(scene, graph);
42 |
43 | // TODO UI:
44 | // - click on path to show data
45 | // - modify path coloration parameters
46 | }
47 |
48 | public static void RenderVCM() {
49 | SceneRegistry.AddSourceRelativeToScript("../Data/Scenes");
50 | using var scene = SceneRegistry.LoadScene("GlassCubes").MakeScene();
51 |
52 | scene.FrameBuffer = new FrameBuffer(640, 480, "testVCM.exr", FrameBuffer.Flags.SendToTev);
53 | scene.Prepare();
54 |
55 | var integrator = new CameraStoringVCM() {
56 | NumIterations = 10,
57 | MaxDepth = 10,
58 | };
59 | integrator.Render(scene);
60 | scene.FrameBuffer.WriteToFile();
61 |
62 | Pixel pixel = new(628, 428);
63 |
64 | // Get the strongest path sample in this pixel
65 | var q = scene.FrameBuffer.OutlierCache.GetPixelOutlier(pixel);
66 | float best = 0;
67 | int iteration = -1;
68 | foreach (var i in q.UnorderedItems) {
69 | if (i.Priority > best) {
70 | best = i.Priority;
71 | iteration = i.Element.Iteration;
72 | }
73 | }
74 |
75 | var (graph, _) = integrator.ReplayPixel(scene, pixel, iteration);
76 |
77 | scene.FrameBuffer = new(640, 480, "pathVCM.exr", FrameBuffer.Flags.SendToTev);
78 | PathGraphRenderer graphVis = new() {};
79 | graphVis.Render(scene, graph);
80 |
81 | System.IO.File.WriteAllText("VCMpaths.ply", graph.ConvertToPLY());
82 | }
83 | }
--------------------------------------------------------------------------------
/SeeSharp.IntegrationTests/Program.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Common;
2 | using SeeSharp.Experiments;
3 | using SeeSharp.Integrators;
4 |
5 | namespace SeeSharp.IntegrationTests;
6 |
7 | class Program {
8 | public static void PathTracerTimeBudget() {
9 | var scene = SeeSharp.Scene.LoadFromFile("Data/Scenes/CornellBox/CornellBox.json");
10 | scene.FrameBuffer = new SeeSharp.Images.FrameBuffer(512, 512, "test.exr",
11 | SeeSharp.Images.FrameBuffer.Flags.SendToTev);
12 | scene.Prepare();
13 |
14 | var integrator = new PathTracer() {
15 | TotalSpp = 498989,
16 | MaximumRenderTimeMs = 4500,
17 | MaxDepth = 5
18 | };
19 | integrator.Render(scene);
20 | scene.FrameBuffer.WriteToFile();
21 | }
22 |
23 | static void BlenderAutoImport() {
24 | Logger.Verbosity = Verbosity.Debug;
25 | SceneRegistry.AddSourceRelativeToScript("../Data/Scenes");
26 | SceneRegistry.LoadScene("ExportTest");
27 | }
28 |
29 | static void Main(string[] args) {
30 | // BidirPathLogger_HomeOffice.Run();
31 | // BidirPathLogger_IndirectRoom.Run();
32 |
33 | // ConsoleUtils.TestProgressBar();
34 | // ConsoleUtils.TestLogger();
35 |
36 | // LightProbeTest.WhiteImage();
37 | // LightProbeTest.CornellProbe();
38 |
39 | // BidirZeroLightPaths.Run();
40 | // PathTracerTimeBudget();
41 |
42 | // BlenderAutoImport();
43 | // OutlierCacheTest.RenderPT();
44 | OutlierCacheTest.RenderVCM();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/SeeSharp.IntegrationTests/SeeSharp.IntegrationTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/SeeSharp.PreviewRender/Program.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using SeeSharp.Common;
3 | using SeeSharp.Integrators;
4 | using SeeSharp.Integrators.Bidir;
5 |
6 | namespace SeeSharp.PreviewRender;
7 |
8 | class Program {
9 | ///
10 | /// SeeSharp preview renderer
11 | ///
12 | /// Path to the .json scene file to render
13 | /// Number of samples per pixel to render
14 | /// Maximum path length (number of edges)
15 | /// Width of the rendered image in pixels
16 | /// Height of the rendered image in pixels
17 | /// Name of the output file
18 | /// Set to false to write a multi-layer file with AOVs
19 | /// One of: PT, VCM
20 | /// Whether to run Open Image Denoise on the flattened output image
21 | /// If true, the image is displayed and continuously updated in the tev viewer.
22 | static int Main(
23 | FileInfo scene,
24 | int samples = 8,
25 | int maxdepth = 5,
26 | int resx = 512,
27 | int resy = 512,
28 | string output = "Render.exr",
29 | bool flatten = true,
30 | string algo = "PT",
31 | bool denoise = true,
32 | bool interactive = false
33 | ) {
34 | if (scene == null) {
35 | Logger.Error("Please provide a scene filename via --scene [file.json]");
36 | return -1;
37 | }
38 |
39 | using var sc = Scene.LoadFromFile(scene.FullName);
40 | var flags = interactive ? Images.FrameBuffer.Flags.SendToTev : Images.FrameBuffer.Flags.Recommended;
41 | sc.FrameBuffer = new(resx, resy, output, flags);
42 | sc.Prepare();
43 |
44 | if (algo == "PT") {
45 | new PathTracer() {
46 | MaxDepth = maxdepth,
47 | TotalSpp = samples,
48 | }.Render(sc);
49 | } else if (algo == "VCM") {
50 | new VertexConnectionAndMerging() {
51 | MaxDepth = maxdepth,
52 | NumIterations = samples,
53 | }.Render(sc);
54 | } else {
55 | Logger.Error($"Unknown rendering algorithm: {algo}. Use PT or VCM");
56 | return -1;
57 | }
58 |
59 | if (flatten && denoise)
60 | sc.FrameBuffer.GetLayer("denoised").Image.WriteToFile(output);
61 | else if (flatten)
62 | sc.FrameBuffer.Image.WriteToFile(output);
63 | else
64 | sc.FrameBuffer.WriteToFile();
65 |
66 | Logger.Log($"Done after {sc.FrameBuffer.MetaData["RenderTime"]}ms");
67 |
68 | return 0;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/SeeSharp.PreviewRender/SeeSharp.PreviewRender.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Exe
14 | net9.0
15 | false
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/README.md:
--------------------------------------------------------------------------------
1 | # SeeSharp experiment template
2 |
3 | Base project to develop ray tracing experiments with SeeSharp. Shows how to auto-import Blender scenes (see [Scenes/ExampleScene](Scenes/ExampleScene)), how to configure an experiment (`MyExperiment.cs`), how to setup a benchmark (`Program.cs`), and how to assemble the rendered images in a .pdf (`MakeFigure.py`).
--------------------------------------------------------------------------------
/SeeSharp.Templates/SeeSharp.Templates.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SeeSharp.Templates
5 | 1.0.3
6 | SeeSharp Experiment Template
7 | Pascal Grittmann
8 | Basic setup for a raytracing experiment with SeeSharp
9 | dotnet-new;templates;SeeSharp
10 | https://github.com/pgrit/SeeSharp
11 |
12 | Template
13 | net9.0
14 | true
15 | false
16 | content
17 | $(NoWarn);NU5128
18 | true
19 | README.md
20 |
21 |
22 |
23 | false
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Blazor.Template/.template.config/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/template",
3 | "author": "Pascal Grittmann",
4 | "classifications": [ "Web/Blazor/SeeSharp" ],
5 | "identity": "SeeSharp.Blazor.Template",
6 | "name": "SeeSharp Blazor Experiment",
7 | "shortName": "seesharp-blazor",
8 | "sourceName":"SeeSharp.Blazor.Template",
9 | "tags": {
10 | "language": "C#",
11 | "type": "project"
12 | },
13 | "sources": [
14 | {
15 | "modifiers": [
16 | {
17 | "exclude": [
18 | "Results/**",
19 | "Scenes/*/*.blend1"
20 | ],
21 | "copyOnly": [
22 | "Scenes/**"
23 | ]
24 | }
25 | ]
26 | }
27 | ]
28 | }
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Blazor.Template/App.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Not found
8 |
9 |
Sorry, there's nothing at this address.
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Imports.cs:
--------------------------------------------------------------------------------
1 | global using System;
2 | global using System.Collections.Concurrent;
3 | global using System.Collections.Generic;
4 | global using System.Diagnostics;
5 | global using System.IO;
6 | global using System.Linq;
7 | global using System.Numerics;
8 | global using System.Text.Json;
9 | global using System.Text.Json.Serialization;
10 | global using System.Threading;
11 | global using System.Threading.Tasks;
12 |
13 | global using TinyEmbree;
14 | global using SimpleImageIO;
15 |
16 | global using SeeSharp;
17 | global using SeeSharp.Cameras;
18 | global using SeeSharp.Common;
19 | global using SeeSharp.Experiments;
20 | global using SeeSharp.Geometry;
21 | global using SeeSharp.Images;
22 | global using SeeSharp.Integrators;
23 | global using SeeSharp.Integrators.Bidir;
24 | global using SeeSharp.Integrators.Common;
25 | global using SeeSharp.Integrators.Util;
26 | global using SeeSharp.Sampling;
27 | global using SeeSharp.Shading;
28 | global using SeeSharp.Shading.Background;
29 | global using SeeSharp.Shading.Emitters;
30 | global using SeeSharp.Shading.Materials;
31 |
32 | global using SeeSharp.Blazor;
33 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Blazor.Template/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
3 | @Body
4 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor:
--------------------------------------------------------------------------------
1 | @using SeeSharp.Experiments
2 | @using SeeSharp
3 | @using SeeSharp.Blazor
4 |
5 | @inject IJSRuntime JS
6 |
7 | @page "/Experiment"
8 |
9 |
22 |
23 | An error has occurred. This application may no longer respond until reloaded.
24 |
25 |
26 | An unhandled exception has occurred. See browser dev tools for details.
27 |
28 | Reload
29 | 🗙
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Components;
2 | using Microsoft.AspNetCore.Components.Web;
3 |
4 | var builder = WebApplication.CreateBuilder(args);
5 | builder.Services.AddRazorPages();
6 | builder.Services.AddServerSideBlazor();
7 |
8 | var app = builder.Build();
9 |
10 | if (!app.Environment.IsDevelopment())
11 | {
12 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
13 | app.UseHsts();
14 | }
15 |
16 | app.UseHttpsRedirection();
17 |
18 | app.UseStaticFiles();
19 |
20 | app.UseRouting();
21 |
22 | app.MapBlazorHub();
23 | app.MapFallbackToPage("/_Host");
24 |
25 | app.Run();
26 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Blazor.Template/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "iisExpress": {
4 | "applicationUrl": "http://localhost:18831",
5 | "sslPort": 44326
6 | }
7 | },
8 | "profiles": {
9 | "http": {
10 | "commandName": "Project",
11 | "dotnetRunMessages": true,
12 | "launchBrowser": false,
13 | "applicationUrl": "http://localhost:5229",
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "https": {
19 | "commandName": "Project",
20 | "dotnetRunMessages": true,
21 | "launchBrowser": false,
22 | "applicationUrl": "https://localhost:7055;http://localhost:5229",
23 | "environmentVariables": {
24 | "ASPNETCORE_ENVIRONMENT": "Development"
25 | }
26 | },
27 | "IIS Express": {
28 | "commandName": "IISExpress",
29 | "launchBrowser": false,
30 | "environmentVariables": {
31 | "ASPNETCORE_ENVIRONMENT": "Development"
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Blazor.Template/SeeSharp.Blazor.Template.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Blazor.Template/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using Microsoft.AspNetCore.Components.Routing
2 | @using Microsoft.AspNetCore.Components.Web
3 | @using Microsoft.JSInterop
4 | @using SeeSharp.Blazor.Template
5 |
6 | @using SeeSharp.Blazor
7 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Blazor.Template/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "DetailedErrors": true,
3 | "Logging": {
4 | "LogLevel": {
5 | "Default": "Information",
6 | "Microsoft.AspNetCore": "Warning"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Blazor.Template/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*"
9 | }
10 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/.template.config/template.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/template",
3 | "author": "Pascal Grittmann",
4 | "classifications": [ "Console/SeeSharp" ],
5 | "identity": "SeeSharp.Template",
6 | "name": "SeeSharp Experiment",
7 | "shortName": "seesharp",
8 | "sourceName":"SeeSharp.Template",
9 | "tags": {
10 | "language": "C#",
11 | "type": "project"
12 | },
13 | "sources": [
14 | {
15 | "modifiers": [
16 | {
17 | "exclude": [
18 | "Results/**",
19 | "Scenes/*/*.blend1"
20 | ],
21 | "copyOnly": [
22 | "Scenes/**"
23 | ]
24 | }
25 | ]
26 | }
27 | ]
28 | }
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Imports.cs:
--------------------------------------------------------------------------------
1 | global using System;
2 | global using System.Collections.Concurrent;
3 | global using System.Collections.Generic;
4 | global using System.Diagnostics;
5 | global using System.IO;
6 | global using System.Linq;
7 | global using System.Numerics;
8 | global using System.Text.Json;
9 | global using System.Text.Json.Serialization;
10 | global using System.Threading;
11 | global using System.Threading.Tasks;
12 |
13 | global using TinyEmbree;
14 | global using SimpleImageIO;
15 |
16 | global using SeeSharp;
17 | global using SeeSharp.Cameras;
18 | global using SeeSharp.Common;
19 | global using SeeSharp.Experiments;
20 | global using SeeSharp.Geometry;
21 | global using SeeSharp.Images;
22 | global using SeeSharp.Integrators;
23 | global using SeeSharp.Integrators.Bidir;
24 | global using SeeSharp.Integrators.Common;
25 | global using SeeSharp.Integrators.Util;
26 | global using SeeSharp.Sampling;
27 | global using SeeSharp.Shading;
28 | global using SeeSharp.Shading.Background;
29 | global using SeeSharp.Shading.Emitters;
30 | global using SeeSharp.Shading.Materials;
31 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/MyExperiment.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | namespace SeeSharp.Template;
4 |
5 | class MyExperiment : Experiment {
6 | public override List MakeMethods() => [
7 | new("Path tracing", new PathTracer() { TotalSpp = 4 }),
8 | new("VCM", new VertexConnectionAndMerging() { NumIterations = 2 })
9 | ];
10 |
11 | public override void OnDone(string workingDirectory, IEnumerable sceneNames, IEnumerable sceneExposures) {
12 | // Run Python script to generate overview figure
13 | try {
14 | var scenes = sceneNames.Zip(sceneExposures).Select(kv => $"{kv.First};{kv.Second}");
15 | string args = $"\"{workingDirectory}\" \"{string.Join(',', scenes)}\" \"{string.Join(',', MethodNames)}\"";
16 | RunPythonScript("MakeFigure.py", args);
17 | } catch(Exception) {
18 | Logger.Warning("Running figuregen script with Python failed");
19 | }
20 | }
21 |
22 | static void RunPythonScript(string scriptName, string arguments, [CallerFilePath] string callerPath = null) {
23 | string scriptPath = Path.Join(Path.GetDirectoryName(callerPath), scriptName);
24 | Logger.Log($"python \"{scriptPath}\" {arguments}");
25 | Process.Start("python", $"{scriptPath} {arguments}").WaitForExit();
26 | }
27 | }
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Program.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Template;
2 |
3 | // Additional scene directories can be added here, or globally via the environment variable "SEESHARP_SCENE_DIRS"
4 | SceneRegistry.AddSourceRelativeToScript("./Scenes");
5 |
6 | new Benchmark(
7 | new MyExperiment(), // the experiment to run
8 | [ // list of all scenes to render
9 | SceneRegistry.LoadScene("ExampleScene", maxDepth: 100),
10 | ],
11 | "Results", // name of the output directory
12 | 1280, 768 // image resolution
13 | ).Run(skipReference: false);
14 |
15 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/ExampleScene.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/ExampleScene.blend
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/ExampleScene.blend.import:
--------------------------------------------------------------------------------
1 | 133569604326161530
2 | 55D161AD93643964FB1B6451DB9269CE
3 |
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/Floor.0.ply:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/Floor.0.ply
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/FrontWall.0.ply:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/FrontWall.0.ply
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/LampCord.0.ply:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/LampCord.0.ply
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/LampShade.0.ply:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/LampShade.0.ply
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/LeftWall.0.ply:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/LeftWall.0.ply
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/LightBulb.0.ply:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/LightBulb.0.ply
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/RightWall.0.ply:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/RightWall.0.ply
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/Ring.0.ply:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/Ring.0.ply
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/Roof.0.ply:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/Roof.0.ply
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/Suzanne.0.ply:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/Meshes/Suzanne.0.ply
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/References/Config.json:
--------------------------------------------------------------------------------
1 | {
2 | "Name": "SeeSharp.Integrators.Bidir.VertexConnectionAndMerging",
3 | "Settings": {
4 | "MaximumRadius": 0,
5 | "AverageCameraPathLength": 0,
6 | "AverageLightPathLength": 0,
7 | "AveragePhotonsPerQuery": 0,
8 | "DisableCorrelAwareMIS": false,
9 | "MergePrimary": false,
10 | "EnableMerging": true,
11 | "MaxNumPhotons": 8,
12 | "NumConnections": 1,
13 | "NumShadowRays": 1,
14 | "EnableLightTracer": true,
15 | "EnableHitting": true,
16 | "RenderTechniquePyramid": false,
17 | "NumIterations": 32,
18 | "MaximumRenderTimeMs": null,
19 | "NumLightPaths": null,
20 | "BaseSeedCamera": 201523476,
21 | "BaseSeedLight": 331415294,
22 | "EnableDenoiser": true,
23 | "MaxDepth": 100,
24 | "MinDepth": 1
25 | }
26 | }
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/References/MaxDepth100-Width1280-Height768.exr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/References/MaxDepth100-Width1280-Height768.exr
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/Scenes/ExampleScene/References/MaxDepth100-Width1280-Height768.json:
--------------------------------------------------------------------------------
1 | {
2 | "NumIterations": 32,
3 | "RenderTime": 70240,
4 | "FrameBufferTime": 813,
5 | "PathTracerTime": 37208,
6 | "LightTracerTime": 32915,
7 | "ShadingStats": {
8 | "NumMaterialEval": 350451267,
9 | "NumMaterialSample": 150246650,
10 | "NumMaterialPdf": 342831096
11 | },
12 | "RayTracerStats": {
13 | "NumShadowRays": 200949734,
14 | "NumRays": 205236503,
15 | "NumOccluded": 122124712,
16 | "NumRayHits": 163261642
17 | },
18 | "AverageCameraPathLength": 2.7713094,
19 | "AverageLightPathLength": 4.178686,
20 | "AveragePhotonsPerQuery": 2.725642,
21 | "MergeAccelBuildTime": 13201,
22 | "NaNWarnings": [],
23 | "RenderStartTime": "07/4/2024 16:46:38",
24 | "RenderWriteTime": "07/4/2024 16:47:50"
25 | }
--------------------------------------------------------------------------------
/SeeSharp.Templates/content/SeeSharp.Template/SeeSharp.Template.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/Camera/LightProbe_Weights.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Cameras;
2 | using System.Numerics;
3 | using TinyEmbree;
4 | using Xunit;
5 |
6 | namespace SeeSharp.Tests.Core.Camera {
7 | public class LightProbe_Weights {
8 | LightProbeCamera MakeCamera() {
9 | // A simple plane with the z axis as its normal
10 | Raytracer raytracer = new();
11 | raytracer.AddMesh(new(new Vector3[] {
12 | new(-1, -1, 0),
13 | new( 1, -1, 0),
14 | new( 1, 1, 0),
15 | new(-1, 1, 0)
16 | }, new[] { 0, 1, 2, 0, 2, 3 }));
17 | raytracer.CommitScene();
18 |
19 | // Find the center point via ray tracing so we get a good error offset
20 | Hit hit = raytracer.Trace(new Ray {
21 | Origin = Vector3.UnitZ,
22 | Direction = -Vector3.UnitZ,
23 | MinDistance = 0
24 | });
25 |
26 | var camera = new LightProbeCamera(hit.Position, hit.Normal, hit.ErrorOffset, Vector3.UnitY);
27 | camera.UpdateResolution(512, 256);
28 |
29 | return camera;
30 | }
31 |
32 | [Fact]
33 | public void Position_ShouldBeZero() {
34 | var camera = MakeCamera();
35 |
36 | Assert.Equal(0.0f, camera.Position.X, 2);
37 | Assert.Equal(0.0f, camera.Position.Y, 2);
38 | Assert.Equal(0.0f, camera.Position.Z, 2);
39 | }
40 |
41 | [Fact]
42 | public void Direction_ShouldBeDown() {
43 | var camera = MakeCamera();
44 |
45 | RNG rng = new();
46 | var sample = camera.GenerateRay(new(200, 255), ref rng);
47 |
48 | Assert.Equal(0.0f, sample.Ray.Direction.X, 1);
49 | Assert.Equal(-1.0f, sample.Ray.Direction.Y, 2);
50 | Assert.Equal(0.0f, sample.Ray.Direction.Z, 1);
51 | }
52 |
53 | [Fact]
54 | public void Direction_ShouldBeUp() {
55 | var camera = MakeCamera();
56 |
57 | RNG rng = new();
58 | var sample = camera.GenerateRay(new(200, 1), ref rng);
59 |
60 | Assert.Equal(0.0f, sample.Ray.Direction.X, 1);
61 | Assert.Equal(1.0f, sample.Ray.Direction.Y, 2);
62 | Assert.Equal(0.0f, sample.Ray.Direction.Z, 1);
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/Camera/Perspective_Splatting.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Images;
2 | using System;
3 | using System.Numerics;
4 | using Xunit;
5 |
6 | namespace SeeSharp.Tests.Core.Camera {
7 | public class Perspective_Splatting {
8 | Cameras.Camera MakeTestCamera() {
9 | var camTransform = Matrix4x4.CreateLookAt(
10 | cameraPosition: new Vector3(0, 0, 0),
11 | cameraTarget: new Vector3(0, 0, 10),
12 | cameraUpVector: new Vector3(0, 1, 0));
13 | float fov = 90;
14 |
15 | var cam = new Cameras.PerspectiveCamera(camTransform, fov);
16 | cam.UpdateResolution(3, 3);
17 | return cam;
18 | }
19 |
20 | [Fact]
21 | public void Center_CorrectPixel() {
22 | var cam = MakeTestCamera();
23 | RNG rng = new();
24 | var raster = cam.SampleResponse(new() { Position = new Vector3(0, 0, 3.5f) }, rng.NextFloat2D()).Pixel;
25 |
26 | Assert.Equal(1, raster.Col);
27 | Assert.Equal(1, raster.Row);
28 | }
29 |
30 | [Fact]
31 | public void TopLeft_CorrectPixel() {
32 | var cam = MakeTestCamera();
33 |
34 | var c = MathF.Cos(MathF.PI / 4.0f);
35 | var len = MathF.Sqrt(c * c * 3);
36 | var xyz = c / len;
37 |
38 | RNG rng = new();
39 | var raster = cam.SampleResponse(new() { Position = new Vector3(xyz, xyz, xyz) }, rng.NextFloat2D()).Pixel;
40 |
41 | Assert.Equal(0, raster.Col);
42 | Assert.Equal(0, raster.Row);
43 | }
44 |
45 | [Fact]
46 | public void Center_Behind_ShouldBeNull() {
47 | var cam = MakeTestCamera();
48 | RNG rng = new();
49 | var result = cam.SampleResponse(new() { Position = new Vector3(0, 0, -3.5f) }, rng.NextFloat2D());
50 | Assert.False(result.IsValid);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/FrameBuffer.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Nodes;
3 |
4 | namespace SeeSharp.Tests.Core;
5 |
6 | public class FrameBufferTests {
7 | [Fact]
8 | public void NaNShouldBeLogged() {
9 | FrameBuffer frameBuffer = new(512, 512, "nantest.exr");
10 | frameBuffer.StartIteration();
11 | frameBuffer.EndIteration();
12 | frameBuffer.StartIteration();
13 | frameBuffer.Splat(42, 69, RgbColor.White);
14 |
15 | frameBuffer.Splat(69, 42, new RgbColor(float.NaN, 1, 2));
16 | frameBuffer.Splat(13, 200, new RgbColor(0, 1, float.NegativeInfinity));
17 |
18 | frameBuffer.EndIteration();
19 |
20 | var warnings = frameBuffer.NaNWarnings;
21 |
22 | Assert.Equal(2, warnings[0].Iteration);
23 | Assert.Equal(69, warnings[0].Pixel.Col);
24 | Assert.Equal(42, warnings[0].Pixel.Row);
25 |
26 | Assert.Equal(2, warnings[1].Iteration);
27 | Assert.Equal(13, warnings[1].Pixel.Col);
28 | Assert.Equal(200, warnings[1].Pixel.Row);
29 | }
30 |
31 | [Fact]
32 | public void NaNShouldBeLoggedInJson() {
33 | FrameBuffer frameBuffer = new(512, 512, "nantest.exr");
34 | frameBuffer.StartIteration();
35 | frameBuffer.EndIteration();
36 | frameBuffer.StartIteration();
37 | frameBuffer.Splat(42, 69, RgbColor.White);
38 |
39 | frameBuffer.Splat(69, 42, new RgbColor(float.NaN, 1, 2));
40 | frameBuffer.Splat(13, 200, new RgbColor(0, 1, float.NegativeInfinity));
41 |
42 | frameBuffer.EndIteration();
43 |
44 | frameBuffer.WriteToFile();
45 |
46 | var json = JsonNode.Parse(File.ReadAllText("nantest.json"));
47 | var warnings = json["NaNWarnings"].Deserialize();
48 |
49 | Assert.Equal(2, warnings[0].Iteration);
50 | Assert.Equal(69, warnings[0].Pixel.Col);
51 | Assert.Equal(42, warnings[0].Pixel.Row);
52 |
53 | Assert.Equal(2, warnings[1].Iteration);
54 | Assert.Equal(13, warnings[1].Pixel.Col);
55 | Assert.Equal(200, warnings[1].Pixel.Row);
56 | }
57 | }
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/Geometry/Mesh_Attributes.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Geometry;
2 | using System;
3 | using System.Numerics;
4 | using Xunit;
5 |
6 | namespace SeeSharp.Tests.Core.Geometry {
7 | public class Mesh_Attributes {
8 | [Fact]
9 | public void ShadingNormals_ShouldBeSet() {
10 | var vertices = new Vector3[] {
11 | new Vector3(-1, 0, -1),
12 | new Vector3( 1, 0, -1),
13 | new Vector3( 1, 0, 1),
14 |
15 | new Vector3(-1, 0, -1),
16 | new Vector3( 1, 0, -1),
17 | new Vector3(-1, 0, 1)
18 | };
19 |
20 | var indices = new int[] {
21 | 0, 1, 2,
22 | 3, 4, 5
23 | };
24 |
25 | var normals = new Vector3[] {
26 | new Vector3(1, 0, 0),
27 | new Vector3(1, 0, 0),
28 | new Vector3(1, 0, 0),
29 |
30 | new Vector3(1, 0, -1),
31 | new Vector3(1, 0, -1),
32 | new Vector3(1, 0, -1),
33 | };
34 |
35 | Mesh mesh = new Mesh(vertices, indices, shadingNormals: normals);
36 |
37 | var n1 = mesh.ComputeShadingNormal(0, new Vector2(0.5f, 0.5f));
38 | var n2 = mesh.ComputeShadingNormal(1, new Vector2(0.5f, 0.5f));
39 |
40 | Assert.Equal(1, n1.X, 3);
41 | Assert.Equal(0, n1.Y, 3);
42 | Assert.Equal(0, n1.Z, 3);
43 |
44 | Assert.Equal(1.0f / MathF.Sqrt(2), n2.X, 3);
45 | Assert.Equal(0, n2.Y, 3);
46 | Assert.Equal(-1.0f / MathF.Sqrt(2), n2.Z, 3);
47 | }
48 |
49 | [Fact]
50 | public void ShadingNormals_ShouldBeInitialized_CCW() {
51 | var vertices = new Vector3[] {
52 | new Vector3(-1, 0, -1),
53 | new Vector3( 1, 0, -1),
54 | new Vector3( 1, 0, 1),
55 | new Vector3(-1, 0, 1)
56 | };
57 |
58 | var indices = new int[] {
59 | 0, 1, 2,
60 | 0, 2, 3
61 | };
62 |
63 | Mesh mesh = new Mesh(vertices, indices);
64 |
65 | var n1 = mesh.ComputeShadingNormal(0, new Vector2(0.5f, 0.25f));
66 | var n2 = mesh.ComputeShadingNormal(0, new Vector2(0.25f, 0.5f));
67 |
68 | Assert.Equal(0, n1.X, 3);
69 | Assert.Equal(-1, n1.Y, 3);
70 | Assert.Equal(0, n1.Z, 3);
71 |
72 | Assert.Equal(0, n2.X, 3);
73 | Assert.Equal(-1, n2.Y, 3);
74 | Assert.Equal(0, n2.Z, 3);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/Geometry/Mesh_SampleInverse.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Geometry;
2 | using System.Numerics;
3 | using Xunit;
4 |
5 | namespace SeeSharp.Tests.Core.Geometry {
6 | public class Mesh_SampleInverse {
7 | [Fact]
8 | public void SingleTriangle() {
9 | Mesh tri = new(new Vector3[] {
10 | new(4, -1, 4),
11 | new(1, 3, 6),
12 | new(-7, 0, 3)
13 | }, new[] {
14 | 0, 1, 2
15 | });
16 |
17 | var s = tri.Sample(new(0.1f, 0.8f));
18 | var p = tri.SampleInverse(s.Point);
19 |
20 | Assert.Equal(0.1f, p.X, 4);
21 | Assert.Equal(0.8f, p.Y, 4);
22 | }
23 |
24 | [Fact]
25 | public void TwoTriangles() {
26 | Mesh tri = new(new Vector3[] {
27 | new(4, -1, 4),
28 | new(1, 3, 6),
29 | new(-7, 0, 3),
30 |
31 | new(0, 0, 0),
32 | new(1, 0, 0),
33 | new(1, 1, 1),
34 | }, new[] {
35 | 0, 1, 2,
36 | 3, 4, 5
37 | });
38 |
39 | var s = tri.Sample(new(0.1f, 0.8f));
40 | var p = tri.SampleInverse(s.Point);
41 |
42 | Assert.Equal(0.1f, p.X, 2);
43 | Assert.Equal(0.8f, p.Y, 2);
44 |
45 | var s2 = tri.Sample(new(0.75f, 0.8f));
46 | var p2 = tri.SampleInverse(s2.Point);
47 |
48 | Assert.Equal(0.75f, p2.X, 4);
49 | Assert.Equal(0.8f, p2.Y, 4);
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/Imports.cs:
--------------------------------------------------------------------------------
1 | global using System;
2 | global using System.IO;
3 | global using System.Collections.Generic;
4 | global using System.Threading.Tasks;
5 | global using System.Diagnostics;
6 | global using System.Numerics;
7 |
8 | global using TinyEmbree;
9 | global using SimpleImageIO;
10 |
11 | global using SeeSharp.Geometry;
12 | global using SeeSharp.Integrators.Common;
13 | global using SeeSharp.Sampling;
14 | global using SeeSharp.Shading.Emitters;
15 | global using SeeSharp.Common;
16 | global using SeeSharp.Integrators.Util;
17 | global using SeeSharp.Integrators.Bidir;
18 | global using SeeSharp.Cameras;
19 | global using SeeSharp.Shading.Background;
20 | global using SeeSharp.Shading.Materials;
21 | global using SeeSharp.Images;
22 | global using SeeSharp.Shading;
23 | global using SeeSharp.Integrators;
24 |
25 | global using Xunit;
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/Sampling/PiecewiseConstant_SampleInverse.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Sampling;
2 | using Xunit;
3 |
4 | namespace SeeSharp.Tests.Core.Sampling {
5 | public class PiecewiseConstant_SampleInverse {
6 | [Fact]
7 | public void TwoElements_EqualWeights() {
8 | PiecewiseConstantPDF dist = new(new[] { 1.0f, 1.0f });
9 |
10 | var (idx, r) = dist.Sample(0.25f);
11 | float p = dist.SampleInverse(idx, r);
12 | Assert.Equal(0.25f, p, 3);
13 | }
14 |
15 | [Fact]
16 | public void TwoElements_UnevenWeights() {
17 | PiecewiseConstantPDF dist = new(new[] { 1.0f, 3.0f });
18 |
19 | var (idx, r) = dist.Sample(0.25f);
20 | float p = dist.SampleInverse(idx, r);
21 | Assert.Equal(0.25f, p, 3);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/Sampling/SampleWarp_Disc.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Sampling;
2 | using Xunit;
3 |
4 | namespace SeeSharp.Tests.Core.Sampling {
5 | public class SampleWarp_Disc {
6 | [Fact]
7 | public void ConcentricDisc_Inverse() {
8 | var sample = SampleWarp.ToConcentricDisc(new(0.315f, -0.3154f));
9 | var prim = SampleWarp.FromConcentricDisc(sample);
10 | Assert.Equal(0.315f, prim.X, 3);
11 | Assert.Equal(-0.3154f, prim.Y, 3);
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/Sampling/SampleWarp_Sphere.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Sampling;
2 | using Xunit;
3 |
4 | namespace SeeSharp.Tests.Core.Sampling {
5 | public class SampleWarp_Sphere {
6 | [Fact]
7 | public void CosHemisphere_Inverse() {
8 | var sample = SampleWarp.ToCosHemisphere(new(0.43f, 0.793f));
9 | var prim = SampleWarp.FromCosHemisphere(sample.Direction);
10 |
11 | Assert.Equal(0.43f, prim.X, 3);
12 | Assert.Equal(0.793f, prim.Y, 3);
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/Sampling/SampleWarp_Spherical.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Sampling;
2 | using System.Numerics;
3 | using Xunit;
4 |
5 | namespace SeeSharp.Tests.Core.Sampling {
6 | public class SampleWarp_Spherical {
7 | [Fact]
8 | public void SphericalInverse() {
9 | // Y axis
10 | var spherical = SampleWarp.CartesianToSpherical(Vector3.UnitY);
11 | var dir = SampleWarp.SphericalToCartesian(spherical);
12 | Assert.Equal(0, dir.X, 4);
13 | Assert.Equal(1, dir.Y, 4);
14 | Assert.Equal(0, dir.Z, 4);
15 |
16 | spherical = SampleWarp.CartesianToSpherical(-Vector3.UnitY);
17 | dir = SampleWarp.SphericalToCartesian(spherical);
18 | Assert.Equal(0, dir.X, 4);
19 | Assert.Equal(-1, dir.Y, 4);
20 | Assert.Equal(0, dir.Z, 4);
21 |
22 | // x axis
23 | spherical = SampleWarp.CartesianToSpherical(Vector3.UnitX);
24 | dir = SampleWarp.SphericalToCartesian(spherical);
25 | Assert.Equal(1, dir.X, 4);
26 | Assert.Equal(0, dir.Y, 4);
27 | Assert.Equal(0, dir.Z, 4);
28 |
29 | spherical = SampleWarp.CartesianToSpherical(-Vector3.UnitX);
30 | dir = SampleWarp.SphericalToCartesian(spherical);
31 | Assert.Equal(-1, dir.X, 4);
32 | Assert.Equal(0, dir.Y, 4);
33 | Assert.Equal(0, dir.Z, 4);
34 |
35 | // z axis
36 | spherical = SampleWarp.CartesianToSpherical(Vector3.UnitZ);
37 | dir = SampleWarp.SphericalToCartesian(spherical);
38 | Assert.Equal(0, dir.X, 4);
39 | Assert.Equal(0, dir.Y, 4);
40 | Assert.Equal(1, dir.Z, 4);
41 |
42 | spherical = SampleWarp.CartesianToSpherical(-Vector3.UnitZ);
43 | dir = SampleWarp.SphericalToCartesian(spherical);
44 | Assert.Equal(0, dir.X, 4);
45 | Assert.Equal(0, dir.Y, 4);
46 | Assert.Equal(-1, dir.Z, 4);
47 | }
48 |
49 | [Fact]
50 | public void FromSphere_ShouldBeInverse() {
51 | var primary = new Vector2(0.41f, 0.123f);
52 | var dir = SampleWarp.ToUniformSphere(primary).Direction;
53 | var p2 = SampleWarp.FromUniformSphere(dir);
54 |
55 | Assert.Equal(primary.X, p2.X, 4);
56 | Assert.Equal(primary.Y, p2.Y, 4);
57 |
58 | primary = new Vector2(0.91f, 0.00123f);
59 | dir = SampleWarp.ToUniformSphere(primary).Direction;
60 | p2 = SampleWarp.FromUniformSphere(dir);
61 |
62 | Assert.Equal(primary.X, p2.X, 4);
63 | Assert.Equal(primary.Y, p2.Y, 4);
64 |
65 | primary = new Vector2(0.091f, 0.00123f);
66 | dir = SampleWarp.ToUniformSphere(primary).Direction;
67 | p2 = SampleWarp.FromUniformSphere(dir);
68 |
69 | Assert.Equal(primary.X, p2.X, 4);
70 | Assert.Equal(primary.Y, p2.Y, 4);
71 |
72 | primary = new Vector2(0.91f, 0.823f);
73 | dir = SampleWarp.ToUniformSphere(primary).Direction;
74 | p2 = SampleWarp.FromUniformSphere(dir);
75 |
76 | Assert.Equal(primary.X, p2.X, 4);
77 | Assert.Equal(primary.Y, p2.Y, 4);
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/Sampling/SampleWarp_Triangle.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Sampling;
2 | using Xunit;
3 |
4 | namespace SeeSharp.Tests.Core.Sampling {
5 | public class SampleWarp_Triangle {
6 | [Fact]
7 | public void Inverse_ShouldBeZero() {
8 | var bary = SampleWarp.ToUniformTriangle(new(0.1f, 0.1f));
9 | var prim = SampleWarp.FromUniformTriangle(bary);
10 | Assert.Equal(0.1f, prim.X, 4);
11 | Assert.Equal(0.1f, prim.Y, 4);
12 | }
13 |
14 | [Fact]
15 | public void Inverse_ShouldBeOne() {
16 | var bary = SampleWarp.ToUniformTriangle(new(0.1f, 0.9f));
17 | var prim = SampleWarp.FromUniformTriangle(bary);
18 | Assert.Equal(0.1f, prim.X, 4);
19 | Assert.Equal(0.9f, prim.Y, 4);
20 | }
21 |
22 | [Fact]
23 | public void Inverse_ShouldBeQuarter() {
24 | var bary = SampleWarp.ToUniformTriangle(new(0.25f, 0.25f));
25 | var prim = SampleWarp.FromUniformTriangle(bary);
26 | Assert.Equal(0.25f, prim.X, 4);
27 | Assert.Equal(0.25f, prim.Y, 4);
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/Sampling/TrowbridgeReitz_CorrectValues.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Tests.Core.Sampling;
2 |
3 | public class TrowbridgeReitz_CorrectValues {
4 | [Fact]
5 | public void NDF_Orthogonal() {
6 | float ax = 0.5f;
7 | float ay = 0.3f;
8 | var dist = new TrowbridgeReitzDistribution() { AlphaX = ax, AlphaY = ay };
9 | var ndf = dist.NormalDistribution(new System.Numerics.Vector3(0, 0, 1));
10 |
11 | float expectedNdf = 1 / (MathF.PI * ax * ay);
12 |
13 | Assert.Equal(expectedNdf, ndf, 4);
14 | }
15 |
16 | [Fact]
17 | public void NDF_GrazingAngle() {
18 | float ax = 0.5f;
19 | float ay = 0.3f;
20 | var dist = new TrowbridgeReitzDistribution() { AlphaX = ax, AlphaY = ay };
21 | var ndf = dist.NormalDistribution(new System.Numerics.Vector3(1, 0, 0));
22 |
23 | float expectedNdf = 0;
24 |
25 | Assert.Equal(expectedNdf, ndf, 4);
26 | }
27 |
28 | [Fact]
29 | public void SampleCornerCases_ShouldBeFinite() {
30 | float ax = 0.5f;
31 | float ay = 0.3f;
32 | var dist = new TrowbridgeReitzDistribution() { AlphaX = ax, AlphaY = ay };
33 |
34 | var dir1 = dist.Sample(Vector3.UnitZ, new(0, 1));
35 | var dir2 = dist.Sample(new(MathF.Sqrt(2), 0, MathF.Sqrt(2)), new(1, 0));
36 | var dir3 = dist.Sample(Vector3.UnitZ, new(1, 0));
37 |
38 | Assert.True(float.IsFinite(dir1.X));
39 | Assert.True(float.IsFinite(dir1.Y));
40 | Assert.True(float.IsFinite(dir1.Z));
41 |
42 | Assert.True(float.IsFinite(dir2.X));
43 | Assert.True(float.IsFinite(dir2.Y));
44 | Assert.True(float.IsFinite(dir2.Z));
45 |
46 | Assert.True(float.IsFinite(dir3.X));
47 | Assert.True(float.IsFinite(dir3.Y));
48 | Assert.True(float.IsFinite(dir3.Z));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/Shading/Emitter_Glossy.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 |
3 | namespace SeeSharp.Tests.Core.Shading;
4 |
5 | public class Emitter_Glossy {
6 | [Fact]
7 | public void EmittedRays_SampleInverse() {
8 | var mesh = new Mesh(
9 | new Vector3[] {
10 | new Vector3(-1, 10, -1),
11 | new Vector3( 1, 10, -1),
12 | new Vector3( 1, 10, 1),
13 | new Vector3(-1, 10, 1)
14 | }, new int[] {
15 | 0, 1, 2,
16 | 0, 2, 3
17 | },
18 | shadingNormals: new Vector3[] {
19 | new Vector3(0, 1, 0),
20 | new Vector3(0, 1, 0),
21 | new Vector3(0, 1, 0),
22 | new Vector3(0, 1, 0)
23 | }
24 | );
25 | var emitter = GlossyEmitter.MakeFromMesh(mesh, RgbColor.White, 50);
26 |
27 | var sample = emitter.First().SampleRay(new Vector2(0.3f, 0.8f), new Vector2(0.56f, 0.03f));
28 |
29 | var (posP, dirP) = emitter.First().SampleRayInverse(sample.Point, sample.Direction);
30 |
31 | Assert.Equal(0.3f, posP.X, 3);
32 | Assert.Equal(0.8f, posP.Y, 3);
33 | Assert.Equal(0.56f, dirP.X, 3);
34 | Assert.Equal(0.03f, dirP.Y, 3);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/SeeSharp.Tests/Core/Shading/ShadingSpace_Setup_Transform.cs:
--------------------------------------------------------------------------------
1 | using static SeeSharp.Shading.ShadingSpace;
2 |
3 | namespace SeeSharp.Tests.Core.Shading;
4 |
5 | public class ShadingSpace_Setup_Transform {
6 | [Fact]
7 | public void WorldToShade_ShouldBeZAxis() {
8 | var normal = Vector3.Normalize(new Vector3(1, 1, 0));
9 | var worldDir = Vector3.Normalize(new Vector3(-1, -1, 0));
10 | var shadeDir = WorldToShading(normal, worldDir);
11 |
12 | Assert.Equal(0.0f, shadeDir.X);
13 | Assert.Equal(0.0f, shadeDir.Y);
14 | Assert.Equal(-1.0f, shadeDir.Z, 4);
15 | }
16 |
17 | [Fact]
18 | public void WorldToShade_ShouldBeXY() {
19 | var normal = new Vector3(1, 1, 0);
20 | var worldDir = new Vector3(0, 0, 1);
21 | var shadeDir = WorldToShading(normal, worldDir);
22 |
23 | Assert.Equal(0.0f, shadeDir.Z);
24 | }
25 |
26 | [Fact]
27 | public void WorldToShade_AndBack_ShouldBeOriginalNormalized() {
28 | var normal = Vector3.Normalize(new Vector3(1, 5, 0));
29 | var worldDir = Vector3.Normalize(new Vector3(1, 6, 2));
30 | var shadeDir = WorldToShading(normal, worldDir);
31 | var worldDir2 = ShadingToWorld(normal, shadeDir);
32 |
33 | Assert.Equal(worldDir.X / worldDir.Length(), worldDir2.X, 4);
34 | Assert.Equal(worldDir.Y / worldDir.Length(), worldDir2.Y, 4);
35 | Assert.Equal(worldDir.Z / worldDir.Length(), worldDir2.Z, 4);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/SeeSharp.Tests/SeeSharp.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 | runtime; build; native; contentfiles; analyzers; buildtransitive
13 | all
14 |
15 |
16 | runtime; build; native; contentfiles; analyzers; buildtransitive
17 | all
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/SeeSharp.ToMitsuba/SeeSharp.ToMitsuba.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Exe
10 | net9.0
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/SeeSharp.Validation/Program.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Validation;
2 | using System.Collections.Generic;
3 |
4 | var allTests = new List() {
5 | new Validate_DirectIllumTransmit(),
6 | new Validate_DirectIllum(),
7 | new Validate_SingleBounce(),
8 |
9 | // Needs more samples to correctly validate (PT produces mostly outliers)
10 | // new Validate_SingleBounceGlossy(),
11 |
12 | new Validate_MultiLight(),
13 | new Validate_GlossyLight(),
14 | new Validate_Environment(),
15 | new Validate_CornellBox(),
16 | new Validate_Textures(),
17 | };
18 |
19 | bool useTev = args.Length >= 1 && args[0] == "--tev";
20 |
21 | int benchmarkRuns = 1;
22 | List> allTimings = new();
23 | foreach (var test in allTests) {
24 | var timings = Validator.Benchmark(test, benchmarkRuns, useTev);
25 | allTimings.Add(timings);
26 | }
27 |
28 | System.Console.Write("Average Timings: \n");
29 | foreach (var timings in allTimings) {
30 | foreach (long t in timings) {
31 | System.Console.Write($"{t}ms, ");
32 | }
33 | System.Console.Write("\b \b\b \b\n");
34 | }
35 |
--------------------------------------------------------------------------------
/SeeSharp.Validation/SeeSharp.Validation.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Exe
19 | net9.0
20 | logo.ico
21 | false
22 | Validation
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/SeeSharp.Validation/Validate_CornellBox.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Images;
2 |
3 | namespace SeeSharp.Validation {
4 | class Validate_CornellBox : ValidationSceneFactory {
5 | public override int SamplesPerPixel => 8;
6 |
7 | public override int MaxDepth => 5;
8 |
9 | public override string Name => "CornellBox";
10 |
11 | public override Scene MakeScene() {
12 | var scene = Scene.LoadFromFile("Data/Scenes/CornellBox/CornellBox.json");
13 | scene.FrameBuffer = new FrameBuffer(512, 512, "");
14 | scene.Prepare();
15 | return scene;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/SeeSharp.Validation/Validate_DirectIllum.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp;
2 | using SeeSharp.Cameras;
3 | using SeeSharp.Geometry;
4 | using SeeSharp.Shading;
5 | using SeeSharp.Shading.Emitters;
6 | using SeeSharp.Shading.Materials;
7 | using SeeSharp.Images;
8 | using System.Numerics;
9 | using SimpleImageIO;
10 |
11 | namespace SeeSharp.Validation {
12 | class Validate_DirectIllum : ValidationSceneFactory {
13 | public override int SamplesPerPixel => 10;
14 |
15 | public override int MaxDepth => 2;
16 |
17 | public override string Name => "DirectIllumination";
18 |
19 | public override Scene MakeScene() {
20 | var scene = new Scene();
21 |
22 | // Ground plane
23 | scene.Meshes.Add(new Mesh(new Vector3[] {
24 | new Vector3(-10, -10, -2),
25 | new Vector3( 10, -10, -2),
26 | new Vector3( 10, 10, -2),
27 | new Vector3(-10, 10, -2),
28 | }, new int[] {
29 | 0, 1, 2, 0, 2, 3
30 | }));
31 | scene.Meshes[^1].Material = new GenericMaterial(new GenericMaterial.Parameters {
32 | BaseColor = new TextureRgb(RgbColor.White),
33 | Roughness = new TextureMono(0.5f),
34 | });
35 |
36 | // Emitter
37 | float emitterSize = 0.1f;
38 | //float emitterDepth = -1.9f;
39 | float emitterDepth = -1.0f;
40 | scene.Meshes.Add(new Mesh(new Vector3[] {
41 | new Vector3(-emitterSize, -emitterSize, emitterDepth),
42 | new Vector3( emitterSize, -emitterSize, emitterDepth),
43 | new Vector3( emitterSize, emitterSize, emitterDepth),
44 | new Vector3(-emitterSize, emitterSize, emitterDepth),
45 | }, new int[] {
46 | 0, 1, 2, 0, 2, 3
47 | }, new Vector3[] {
48 | new Vector3(0, 0, -1),
49 | new Vector3(0, 0, -1),
50 | new Vector3(0, 0, -1),
51 | new Vector3(0, 0, -1),
52 | }));
53 | scene.Meshes[^1].Material = new DiffuseMaterial(new DiffuseMaterial.Parameters {
54 | BaseColor = new TextureRgb(RgbColor.Black)
55 | });
56 | scene.Emitters.AddRange(DiffuseEmitter.MakeFromMesh(scene.Meshes[^1], RgbColor.White * 1000));
57 |
58 | scene.Camera = new PerspectiveCamera(Matrix4x4.CreateLookAt(Vector3.Zero, -Vector3.UnitZ, Vector3.UnitY), 40);
59 | scene.FrameBuffer = new FrameBuffer(512, 512, "");
60 |
61 | scene.Prepare();
62 |
63 | return scene;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/SeeSharp.Validation/Validate_DirectTransmit.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Cameras;
2 | using SeeSharp.Geometry;
3 | using SeeSharp.Shading.Emitters;
4 | using SeeSharp.Shading.Materials;
5 | using SeeSharp.Images;
6 | using System.Numerics;
7 | using SimpleImageIO;
8 |
9 | namespace SeeSharp.Validation;
10 |
11 | class Validate_DirectIllumTransmit : ValidationSceneFactory {
12 | public override int SamplesPerPixel => 10;
13 |
14 | public override int MaxDepth => 2;
15 |
16 | public override string Name => "DirectIllumTransmit";
17 |
18 | public override Scene MakeScene() {
19 | var scene = new Scene();
20 |
21 | // Ground plane
22 | scene.Meshes.Add(new Mesh(new Vector3[] {
23 | new Vector3(-10, -10, -2),
24 | new Vector3( 10, -10, -2),
25 | new Vector3( 10, 10, -2),
26 | new Vector3(-10, 10, -2),
27 | }, new int[] {
28 | 0, 1, 2, 0, 2, 3
29 | }));
30 | scene.Meshes[^1].Material = new GenericMaterial(new GenericMaterial.Parameters {
31 | BaseColor = new TextureRgb(RgbColor.White * 0.8f),
32 | Roughness = new TextureMono(1.0f),
33 | Anisotropic = 1.0f,
34 | IndexOfRefraction = 1.45f,
35 | Metallic = 0.0f,
36 | SpecularTransmittance = 1.0f,
37 | SpecularTintStrength = 0.0f
38 | });
39 |
40 | // Emitter
41 | float emitterSize = 0.1f;
42 | float emitterDepth = -4.0f;
43 | scene.Meshes.Add(new Mesh(new Vector3[] {
44 | new Vector3(-emitterSize, -emitterSize, emitterDepth),
45 | new Vector3( emitterSize, -emitterSize, emitterDepth),
46 | new Vector3( emitterSize, emitterSize, emitterDepth),
47 | new Vector3(-emitterSize, emitterSize, emitterDepth),
48 | }, new int[] {
49 | 0, 1, 2, 0, 2, 3
50 | }, new Vector3[] {
51 | new Vector3(0, 0, 1),
52 | new Vector3(0, 0, 1),
53 | new Vector3(0, 0, 1),
54 | new Vector3(0, 0, 1),
55 | }));
56 | scene.Meshes[^1].Material = new DiffuseMaterial(new DiffuseMaterial.Parameters {
57 | BaseColor = new TextureRgb(RgbColor.Black)
58 | });
59 | scene.Emitters.AddRange(DiffuseEmitter.MakeFromMesh(scene.Meshes[^1], RgbColor.White * 1000));
60 |
61 | scene.Camera = new PerspectiveCamera(Matrix4x4.CreateLookAt(Vector3.Zero, -Vector3.UnitZ, Vector3.UnitY), 40);
62 | scene.FrameBuffer = new FrameBuffer(512, 512, "");
63 |
64 | scene.Prepare();
65 |
66 | return scene;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/SeeSharp.Validation/Validate_Environment.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Images;
2 |
3 | namespace SeeSharp.Validation;
4 |
5 | class Validate_Environment : ValidationSceneFactory {
6 | public override int SamplesPerPixel => 10;
7 |
8 | public override int MaxDepth => 5;
9 |
10 | public override string Name => "Environment";
11 |
12 | public override Scene MakeScene() {
13 | var scene = Scene.LoadFromFile("Data/Scenes/simplebackground.json");
14 | scene.FrameBuffer = new FrameBuffer(512, 512, "");
15 | scene.Prepare();
16 | return scene;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/SeeSharp.Validation/Validate_GlossyLight.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp;
2 | using SeeSharp.Cameras;
3 | using SeeSharp.Geometry;
4 | using SeeSharp.Shading;
5 | using SeeSharp.Shading.Emitters;
6 | using SeeSharp.Shading.Materials;
7 | using SeeSharp.Images;
8 | using System.Numerics;
9 | using SimpleImageIO;
10 |
11 | namespace SeeSharp.Validation {
12 | class Validate_GlossyLight : ValidationSceneFactory {
13 | public override int SamplesPerPixel => 10;
14 |
15 | public override int MaxDepth => 2;
16 |
17 | public override string Name => "GlossyLight";
18 |
19 | public override Scene MakeScene() {
20 | var scene = new Scene();
21 |
22 | // Ground plane
23 | scene.Meshes.Add(new Mesh(new Vector3[] {
24 | new Vector3(-10, -10, 0),
25 | new Vector3( 10, -10, 0),
26 | new Vector3( 10, 10, 0),
27 | new Vector3(-10, 10, 0),
28 | }, new int[] {
29 | 0, 1, 2, 0, 2, 3
30 | }));
31 | scene.Meshes[^1].Material = new DiffuseMaterial(new DiffuseMaterial.Parameters {
32 | BaseColor = new TextureRgb(RgbColor.White),
33 | Transmitter = true
34 | });
35 |
36 | // Emitter
37 | float emitterSize = 0.1f;
38 | //float emitterDepth = -1.9f;
39 | float emitterDepth = -1.0f;
40 | scene.Meshes.Add(new Mesh(new Vector3[] {
41 | new Vector3(-emitterSize, -emitterSize, emitterDepth),
42 | new Vector3( emitterSize, -emitterSize, emitterDepth),
43 | new Vector3( emitterSize, emitterSize, emitterDepth),
44 | new Vector3(-emitterSize, emitterSize, emitterDepth),
45 | }, new int[] {
46 | 0, 1, 2, 0, 2, 3
47 | }, new Vector3[] {
48 | new Vector3(0, 0, 1),
49 | new Vector3(0, 0, 1),
50 | new Vector3(0, 0, 1),
51 | new Vector3(0, 0, 1),
52 | }));
53 | scene.Meshes[^1].Material = new DiffuseMaterial(new DiffuseMaterial.Parameters {
54 | BaseColor = new TextureRgb(RgbColor.Black)
55 | });
56 | scene.Emitters.AddRange(GlossyEmitter.MakeFromMesh(scene.Meshes[^1], RgbColor.White * 1000, 200));
57 | //scene.Emitters.Add(new DiffuseEmitter(scene.Meshes[^1], RgbColor.White * 1000));
58 |
59 | var matrix = Matrix4x4.CreateLookAt(Vector3.UnitZ * 2,
60 | -Vector3.UnitZ,
61 | Vector3.UnitY);
62 | scene.Camera = new PerspectiveCamera(matrix, 40);
63 | scene.FrameBuffer = new FrameBuffer(512, 512, "");
64 |
65 | scene.Prepare();
66 |
67 | return scene;
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/SeeSharp.Validation/Validate_MultiLight.cs:
--------------------------------------------------------------------------------
1 | using System.Numerics;
2 | using SeeSharp;
3 | using SeeSharp.Cameras;
4 | using SeeSharp.Geometry;
5 | using SeeSharp.Shading;
6 | using SeeSharp.Shading.Emitters;
7 | using SeeSharp.Shading.Materials;
8 | using SeeSharp.Images;
9 | using SimpleImageIO;
10 |
11 | namespace SeeSharp.Validation {
12 | class Validate_MultiLight : ValidationSceneFactory {
13 | public override int SamplesPerPixel => 10;
14 |
15 | public override int MaxDepth => 2;
16 |
17 | public override string Name => "MultiLight";
18 |
19 | public override Scene MakeScene() {
20 | var scene = new Scene();
21 |
22 | // Ground plane
23 | scene.Meshes.Add(new Mesh(new Vector3[] {
24 | new Vector3(-10, -10, -2),
25 | new Vector3( 10, -10, -2),
26 | new Vector3( 10, 10, -2),
27 | new Vector3(-10, 10, -2),
28 | }, new int[] {
29 | 0, 1, 2, 0, 2, 3
30 | }));
31 | scene.Meshes[^1].Material = new DiffuseMaterial(new DiffuseMaterial.Parameters {
32 | BaseColor = new TextureRgb(RgbColor.White)
33 | });
34 |
35 | // Three emitters
36 | float emitterSize = 0.1f;
37 | float emitterDepth = -1.0f;
38 |
39 | void AddEmitter(float offsetX, float offsetY) {
40 | scene.Meshes.Add(new Mesh(new Vector3[] {
41 | new Vector3(-emitterSize + offsetX, -emitterSize + offsetY, emitterDepth),
42 | new Vector3( emitterSize + offsetX, -emitterSize + offsetY, emitterDepth),
43 | new Vector3( emitterSize + offsetX, emitterSize + offsetY, emitterDepth),
44 | new Vector3(-emitterSize + offsetX, emitterSize + offsetY, emitterDepth),
45 | }, new int[] {
46 | 0, 1, 2, 0, 2, 3
47 | }, new Vector3[] {
48 | new Vector3(0, 0, -1),
49 | new Vector3(0, 0, -1),
50 | new Vector3(0, 0, -1),
51 | new Vector3(0, 0, -1),
52 | }));
53 | scene.Meshes[^1].Material = new DiffuseMaterial(new DiffuseMaterial.Parameters {
54 | BaseColor = new TextureRgb(RgbColor.Black)
55 | });
56 | scene.Emitters.AddRange(DiffuseEmitter.MakeFromMesh(scene.Meshes[^1], RgbColor.White * 1000));
57 | }
58 | AddEmitter(-1, 0);
59 | AddEmitter(1, 0);
60 |
61 | scene.Camera = new PerspectiveCamera(Matrix4x4.CreateLookAt(
62 | Vector3.Zero, -Vector3.UnitZ, Vector3.UnitY), 40);
63 | scene.FrameBuffer = new FrameBuffer(512, 512, "");
64 |
65 | scene.Prepare();
66 |
67 | return scene;
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/SeeSharp.Validation/Validate_Textures.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Images;
2 |
3 | namespace SeeSharp.Validation {
4 | class Validate_Textures : ValidationSceneFactory {
5 | public override int SamplesPerPixel => 8;
6 |
7 | public override int MaxDepth => 5;
8 |
9 | public override string Name => "TextureTest";
10 |
11 | public override Scene MakeScene() {
12 | var scene = Scene.LoadFromFile("Data/Scenes/TextureTest/TextureTest.json");
13 | scene.FrameBuffer = new FrameBuffer(700, 500, "");
14 | scene.Prepare();
15 | return scene;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/SeeSharp.Validation/ValidationSceneFactory.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp;
2 |
3 | namespace SeeSharp.Validation {
4 | abstract class ValidationSceneFactory {
5 | public abstract int SamplesPerPixel { get; }
6 | public abstract int MaxDepth { get; }
7 | public abstract string Name { get; }
8 | public abstract Scene MakeScene();
9 | }
10 | }
--------------------------------------------------------------------------------
/SeeSharp.Validation/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/SeeSharp.Validation/logo.ico
--------------------------------------------------------------------------------
/SeeSharp/Cameras/CameraRaySample.cs:
--------------------------------------------------------------------------------
1 | using SeeSharp.Geometry;
2 | using SimpleImageIO;
3 | using TinyEmbree;
4 |
5 | namespace SeeSharp.Cameras {
6 | ///
7 | /// Stores sample data for a ray generated by a camera
8 | ///
9 | public struct CameraRaySample {
10 | ///
11 | /// The sampled ray
12 | ///
13 | public Ray Ray;
14 |
15 | ///
16 | /// The importance (= pixel contribution) divided by the sampling pdf
17 | ///
18 | public RgbColor Weight;
19 |
20 | ///
21 | /// Surface point on the camera lens, or the closest equivalent, depending on the camera model
22 | ///
23 | public SurfacePoint Point;
24 |
25 | ///
26 | /// Pdf that this ray was sampled with. Units depend on the camera model.
27 | ///
28 | public float PdfRay;
29 |
30 | ///
31 | /// Pdf of sampling the same point via connection in a light tracer.
32 | /// Same unit as .
33 | ///
34 | public float PdfConnect;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/SeeSharp/Cameras/CameraResponseSample.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Cameras;
2 |
3 | ///
4 | /// Stores the relevant data when sampling a connection to the camera
5 | ///
6 | public struct CameraResponseSample {
7 | ///
8 | /// Pixel coordinates
9 | ///
10 | public Pixel Pixel;
11 |
12 | ///
13 | /// Position of the lens point in world space, the sample only contributes if this position
14 | /// is visible from the scene point in question.
15 | ///
16 | public Vector3 Position;
17 |
18 | ///
19 | /// Contribution to the sampled pixel ("importance" divided by the pdf)
20 | ///
21 | public RgbColor Weight;
22 |
23 | ///
24 | /// Probability of sampling this connection
25 | ///
26 | public float PdfConnect;
27 |
28 | ///
29 | /// Probability of instead sampling a ray from the camera into the scene.
30 | /// Unit: surface area at primary hit point times [whatever happens on the specific camera model]
31 | ///
32 | public float PdfEmit;
33 |
34 | ///
35 | /// Checks whether this is a valid sample, i.e., non-zero and sampled with non-zero pdf
36 | ///
37 | public bool IsValid => Weight != RgbColor.Black && PdfConnect != 0 && PdfEmit != 0;
38 |
39 | ///
40 | /// An invalid sample is one where everything is set to zero.
41 | ///
42 | public static CameraResponseSample Invalid => new();
43 | }
44 |
--------------------------------------------------------------------------------
/SeeSharp/Common/Atomic.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | namespace SeeSharp.Common;
4 |
5 | ///
6 | /// Provides utility functions for atomic operations.
7 | ///
8 | public static class Atomic {
9 | ///
10 | /// Adds two floating point values in an atomic fashion, using a compare-and-swap.
11 | /// Thread-safe version of: target += value;
12 | ///
13 | /// Destination
14 | /// Value to add
15 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
16 | public static void AddFloat(ref float target, float value) {
17 | float initialValue, computedValue;
18 | do {
19 | initialValue = target;
20 | computedValue = initialValue + value;
21 | } while (
22 | initialValue != Interlocked.CompareExchange(ref target, computedValue, initialValue)
23 | // If another thread changes target to NaN in the meantime, we will be stuck forever
24 | // since NaN != NaN is always true, and NaN + value is also NaN
25 | && !float.IsNaN(initialValue)
26 | );
27 | }
28 | }
--------------------------------------------------------------------------------
/SeeSharp/Common/ConsoleWatchdog.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 | using System.Text;
3 |
4 | namespace SeeSharp.Common;
5 |
6 | ///
7 | /// A TextWriter that raises an event for each character written to the stream.
8 | /// Can be attached to the Console.Out to monitor output from all parts of the program.
9 | ///
10 | internal class ConsoleWatchdog : TextWriter {
11 | readonly TextWriter output;
12 | public delegate void WriteCharEventHandler(char value);
13 | public event WriteCharEventHandler WriteCharEvent;
14 | public ConsoleWatchdog(TextWriter original) => output = original;
15 | public override Encoding Encoding => output.Encoding;
16 |
17 | [MethodImpl(MethodImplOptions.Synchronized)]
18 | public override void Write(char value) {
19 | output.Write(value);
20 | WriteCharEvent?.Invoke(value);
21 | }
22 | }
--------------------------------------------------------------------------------
/SeeSharp/Common/HtmlReport.cs:
--------------------------------------------------------------------------------
1 | using Markdig;
2 |
3 | namespace SeeSharp.Common;
4 |
5 | ///
6 | /// Utility to generate a static .html page with
7 | ///
8 | public class HtmlReport {
9 | const string style = """
10 |
32 | """;
33 |
34 | const string script = """
35 |
36 | """;
37 |
38 | string htmlBody = "";
39 |
40 | public void AddMarkdown(string text) {
41 | var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
42 | htmlBody += Markdown.ToHtml(text, pipeline);
43 | }
44 |
45 | public void AddTable(IEnumerable> rows) {
46 | htmlBody += HtmlUtil.MakeTable(rows, true);
47 | }
48 |
49 | public void AddFlipBook(FlipBook flip) {
50 | htmlBody += $"""
{flip.Resize(900,800)}
""";
51 | }
52 |
53 | public override string ToString() {
54 | return HtmlUtil.MakeHTML(FlipBook.Header + style + script, htmlBody);
55 | }
56 | }
--------------------------------------------------------------------------------
/SeeSharp/Common/MathUtils.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Common;
2 |
3 | public partial class MathUtils {
4 | public static Vector3 Lerp(Vector3 a, Vector3 b, float t) => t * a + (1 - t) * b;
5 | public static float Lerp(float a, float b, float t) => t * a + (1 - t) * b;
6 |
7 | public static float AngleBetween(Vector3 a, Vector3 b) {
8 | if (Vector3.Dot(a, b) < 0)
9 | return MathF.PI - 2 * MathF.Asin((a + b).Length() / 2);
10 | else
11 | return 2 * MathF.Asin((b - a).Length() / 2);
12 | }
13 | }
--------------------------------------------------------------------------------
/SeeSharp/Common/SanityChecks.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Common;
2 |
3 | ///
4 | /// Provides common sanity checks like checking if a vector is normalized
5 | ///
6 | public static class SanityChecks {
7 | ///
8 | /// Asserts that the given direction is normalized, i.e., has a length of one.
9 | ///
10 | public static void IsNormalized(Vector3 dir, float threshold = 0.001f) {
11 | #if DEBUG
12 | float len = dir.Length();
13 | bool normalized = MathF.Abs(len - 1) < 0.001f;
14 | Debug.Assert(normalized, "Vector is not normalized!");
15 | #endif
16 | }
17 | }
--------------------------------------------------------------------------------
/SeeSharp/Common/TypeFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 |
3 | namespace SeeSharp.Common;
4 |
5 | public class TypeFactory where T : class {
6 | public static T[] All {
7 | get {
8 | // Find all non-abstract classes in the currently loaded assemblies that implement T
9 | var types = AppDomain.CurrentDomain.GetAssemblies()
10 | .SelectMany(assembly => assembly.GetTypes())
11 | .Where(type => type.IsClass && !type.IsAbstract && typeof(T).IsAssignableFrom(type));
12 |
13 | // Instantiate a new (default constructed) object of each implementation
14 | List result = new();
15 | foreach (var type in types) {
16 | result.Add(Activator.CreateInstance(type) as T);
17 | }
18 |
19 | return result.ToArray();
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/SeeSharp/Experiments/BlenderImporter.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 |
3 | namespace SeeSharp.Experiments;
4 |
5 | static class BlenderImporter {
6 | static string _blender;
7 | static string blenderExecutable {
8 | get {
9 | if (_blender != null) return _blender;
10 |
11 | if (IsInPath("blender")) _blender = "blender";
12 | if (IsInPath("blender.exe")) _blender = "blender.exe";
13 |
14 | // Check if it is in any of the default installation directories
15 | if (OperatingSystem.IsWindows()) {
16 | var parentDir = @"C:\Program Files\Blender Foundation\";
17 | if (Directory.Exists(parentDir)) {
18 | double bestVersion = 0;
19 | foreach (var dir in Directory.EnumerateDirectories(parentDir)) {
20 | string candidate = Path.Join(dir, "blender.exe");
21 | if (File.Exists(candidate)) {
22 | if (double.TryParse(Regex.Match(candidate, @"(\d+)\.(\d+)").Value, out var version)) {
23 | if (version > bestVersion) {
24 | bestVersion = version;
25 | _blender = candidate;
26 | }
27 | }
28 | }
29 | }
30 | }
31 | }
32 | // TODO add default directories for Linux / Mac
33 |
34 | if (_blender != null)
35 | Logger.Log("Using Blender from: " + _blender);
36 |
37 | return _blender;
38 | }
39 | }
40 |
41 | static bool IsInPath(string exe) {
42 | if (File.Exists(exe))
43 | return true;
44 |
45 | var paths = Environment.GetEnvironmentVariable("PATH").Split(Path.PathSeparator);
46 | foreach (var path in paths) {
47 | if (File.Exists(Path.Combine(path, exe)))
48 | return true;
49 | }
50 |
51 | return false;
52 | }
53 |
54 | public static bool Import(string blendFile, string jsonFile) {
55 | string python =
56 | $"""
57 | import bpy
58 | bpy.ops.wm.open_mainfile(filepath='{blendFile.Replace('\\', '/')}')
59 | bpy.ops.export.to_seesharp(filepath='{jsonFile.Replace('\\', '/')}')
60 | """;
61 |
62 | if (blenderExecutable == null)
63 | return false;
64 |
65 | var p = Process.Start(blenderExecutable, new string[] {
66 | "--background",
67 | "--python-expr",
68 | python
69 | });
70 | p.WaitForExit();
71 | return p.ExitCode == 0;
72 | }
73 | }
--------------------------------------------------------------------------------
/SeeSharp/Experiments/SceneConfig.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Experiments;
2 |
3 | ///
4 | /// Describes a scene configuration when running experiments
5 | ///
6 | public abstract class SceneConfig {
7 | ///
8 | /// The name of the scene, used for the directory structure
9 | ///
10 | public abstract string Name { get; }
11 |
12 | ///
13 | /// Maximum path length used when rendering the scene. DI only = 2
14 | ///
15 | public abstract int MaxDepth { get; }
16 |
17 | ///
18 | /// Minimum path length used when rendering the scene. No directly visible lights = 2
19 | ///
20 | public abstract int MinDepth { get; }
21 |
22 | ///
23 | /// Generates (or retrieves) the scene ready for rendering
24 | ///
25 | /// The generated scene
26 | public abstract Scene MakeScene();
27 |
28 | ///
29 | /// Renders a reference image, or retrieves a cached one
30 | ///
31 | /// Width of the image
32 | /// Height of the image
33 | /// The reference image
34 | public abstract RgbImage GetReferenceImage(int width, int height);
35 | }
--------------------------------------------------------------------------------
/SeeSharp/Geometry/BoundingBox.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Geometry;
2 |
3 | ///
4 | /// Represents an axis aligned bounding box
5 | ///
6 | public readonly struct BoundingBox {
7 | ///
8 | /// Minimum and maximum values along all axes of the points within the box
9 | ///
10 | public readonly Vector3 Min, Max;
11 |
12 | ///
13 | /// Creates a new bounding box that spans the given region
14 | ///
15 | public BoundingBox(Vector3 min, Vector3 max) {
16 | Min = min;
17 | Max = max;
18 | }
19 |
20 | ///
21 | /// An empty box that contains nothing (max is smaller than min)
22 | ///
23 | public static BoundingBox Empty => new(
24 | min: Vector3.One * float.MaxValue,
25 | max: -Vector3.One * float.MaxValue
26 | );
27 |
28 | ///
29 | /// A box that spans the entire (representable) space
30 | ///
31 | public static BoundingBox Full => new(
32 | min: -Vector3.One * float.MaxValue,
33 | max: Vector3.One * float.MaxValue
34 | );
35 |
36 | ///
37 | /// Computes a new box with updated minimum and maximum so the given point is within the bounds.
38 | ///
39 | /// Point that should be within the box
40 | /// A new box with the updated bounds
41 | public BoundingBox GrowToContain(Vector3 point) => new(
42 | min: Vector3.Min(Min, point),
43 | max: Vector3.Max(Max, point)
44 | );
45 |
46 | ///
47 | /// Computes a new box with updated minimum and maximum so the given box is entirely within the bounds.
48 | ///
49 | /// Other box that should be inside
50 | /// A new box with the updated bounds
51 | public BoundingBox GrowToContain(BoundingBox box) => new(
52 | min: Vector3.Min(Min, box.Min),
53 | max: Vector3.Max(Max, box.Max)
54 | );
55 |
56 | ///
57 | /// Checks if a point is inside the bounding box
58 | ///
59 | public bool IsInside(Vector3 point)
60 | => point.X >= Min.X && point.Y >= Min.Y && point.Z >= Min.Z &&
61 | point.X <= Max.X && point.Y <= Max.Y && point.Z <= Max.Z;
62 |
63 | ///
64 | /// Computes the diagonal vector of the box
65 | ///
66 | public Vector3 Diagonal => Max - Min;
67 |
68 | ///
69 | /// Center point of the box
70 | ///
71 | public Vector3 Center => (Max + Min) / 2;
72 |
73 | ///
74 | /// An empty box is one where Max < Min along every axis
75 | ///
76 | public bool IsEmpty => Min.X >= Max.X && Min.Y >= Max.Y && Min.Z >= Max.Z;
77 |
78 | ///
79 | /// Surface area of the box
80 | ///
81 | public float SurfaceArea => 2 * (Diagonal.X * (Diagonal.Y + Diagonal.Z) + Diagonal.Y * Diagonal.Z);
82 |
83 | public float Volume => Diagonal.X * Diagonal.Y * Diagonal.Z;
84 | }
85 |
--------------------------------------------------------------------------------
/SeeSharp/Geometry/SurfacePoint.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Geometry;
2 |
3 | ///
4 | /// Represents a point on the surface of a mesh in the scene. Wrapper around with
5 | /// additional SeeSharp specific material information.
6 | ///
7 | public struct SurfacePoint {
8 | ///
9 | /// Position in world space
10 | ///
11 | public Vector3 Position { get => hit.Position; set => hit.Position = value; }
12 |
13 | ///
14 | /// Face normal at the point (i.e., actual geometric normal, not the shading normal)
15 | ///
16 | public Vector3 Normal { get => hit.Normal; set => hit.Normal = value; }
17 |
18 | ///
19 | /// Barycentric coordinates within the primitive
20 | ///
21 | public Vector2 BarycentricCoords { get => hit.BarycentricCoords; set => hit.BarycentricCoords = value; }
22 |
23 | ///
24 | /// The mesh on which this point lies
25 | ///
26 | public Mesh Mesh { get => hit.Mesh as Mesh; set => hit.Mesh = value; }
27 |
28 | ///
29 | /// Index of the primitive within the mesh
30 | ///
31 | public uint PrimId { get => hit.PrimId; set => hit.PrimId = value; }
32 |
33 | ///
34 | /// Offset that should be used to avoid self-intersection during ray tracing
35 | ///
36 | public float ErrorOffset { get => hit.ErrorOffset; set => hit.ErrorOffset = value; }
37 |
38 | ///
39 | /// Distance from a previous point if this is a ray intersection
40 | ///
41 | public float Distance { get => hit.Distance; set => hit.Distance = value; }
42 |
43 | ///
44 | /// Checks if the point is valid
45 | ///
46 | public static implicit operator bool(SurfacePoint point) => point.hit;
47 |
48 | ///
49 | /// Implicit cast to a TinyEmbree hit object for convenience
50 | ///
51 | public static implicit operator Hit(SurfacePoint point) => point.hit;
52 |
53 | ///
54 | /// Implicit cast from a TinyEmbree hit object for convenience
55 | ///
56 | ///
57 | public static implicit operator SurfacePoint(Hit hit) {
58 | return new SurfacePoint { hit = hit };
59 | }
60 |
61 | ///
62 | /// Computes the shading normal on the fly, can be expensive
63 | ///
64 | public Vector3 ShadingNormal => hit.ShadingNormal;
65 |
66 | ///
67 | /// Computes / looks up the texture coordinates on-the-fly
68 | ///
69 | public Vector2 TextureCoordinates => hit.TextureCoordinates;
70 |
71 | ///
72 | /// The material of the intersected mesh
73 | ///
74 | public Material Material => Mesh.Material;
75 |
76 | Hit hit;
77 | }
78 |
--------------------------------------------------------------------------------
/SeeSharp/Geometry/SurfaceSample.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Geometry;
2 |
3 | ///
4 | /// A point on a surface that was sampled randomly
5 | ///
6 | public struct SurfaceSample {
7 | ///
8 | /// The sampled point
9 | ///
10 | public SurfacePoint Point;
11 |
12 | ///
13 | /// Probability density at this point, per surface area
14 | ///
15 | public float Pdf;
16 | }
17 |
--------------------------------------------------------------------------------
/SeeSharp/IO/IMeshLoader.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.IO;
2 |
3 | ///
4 | /// All classes implementing this interface are automatically detected via reflections and used to
5 | /// load mesh files with the corresponding type.
6 | ///
7 | public interface IMeshLoader {
8 | ///
9 | /// The type of the mesh file, e.g., "obj" or "ply"
10 | ///
11 | string Type { get; }
12 |
13 | ///
14 | /// Loads the mesh / meshes described by a json entry and any associated emitters.
15 | ///
16 | /// Set of materials specified in the scene description
17 | /// The mesh description in the .json file
18 | /// Full path to the directory containing the .json file
19 | /// All emissive materials in the scene
20 | /// Thrown if the file is corrupted
21 | (IEnumerable, IEnumerable) LoadMesh(Dictionary namedMaterials,
22 | Dictionary emissiveMaterials, JsonElement jsonElement, string dirname);
23 |
24 | struct EmissionParameters {
25 | public RgbColor Radiance;
26 | public bool IsGlossy;
27 | public float Exponent;
28 | }
29 | }
--------------------------------------------------------------------------------
/SeeSharp/IO/MeshLoadException.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.IO;
2 |
3 | ///
4 | /// Represents errors that occur during loading of external meshes.
5 | ///
6 | public class MeshLoadException : Exception {
7 | ///
8 | /// Path to the file which was attempted to be loaded.
9 | ///
10 | public string Path { get; }
11 |
12 | ///
13 | /// Initializes a new instance of the MeshLoadException class with a specified error message and file path.
14 | ///
15 | /// The error message string.
16 | /// A path to the file which was attempted to be loaded.
17 | public MeshLoadException(string message, string path) : base(message + $" ({path})") {
18 | Path = path;
19 | }
20 |
21 | ///
22 | /// Initializes a new instance of the MeshLoadException class with a specified error message, a file path
23 | /// and a reference to the inner exception that is the cause of this exception.
24 | ///
25 | /// The error message string.
26 | /// A path to the file which was attempted to be loaded.
27 | /// The exception that is the cause of the current exception,
28 | /// or a null reference if no inner exception is specified.
29 | public MeshLoadException(string message, string path, Exception inner) : base(message + $" ({path})", inner) {
30 | Path = path;
31 | }
32 | }
--------------------------------------------------------------------------------
/SeeSharp/IO/MixReader.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 |
3 | namespace SeeSharp.IO;
4 |
5 | ///
6 | /// Simple class allowing to mix ascii text and binary data reading
7 | ///
8 | internal class MixReader : BinaryReader {
9 | public MixReader(string path, Encoding encoding) : base(new FileStream(path, FileMode.Open, FileAccess.Read), encoding) {
10 | Path = path;
11 | }
12 |
13 | public string ReadLineAsString(ref bool eos) {
14 | StringBuilder stringBuffer = new(1024);
15 |
16 | eos = false;
17 | try {
18 | while (true) {
19 | char ch = base.ReadChar();
20 | if (ch == '\r') { // Windows style
21 | ch = base.ReadChar();
22 | if (ch == '\n') {
23 | break;
24 | } else {
25 | stringBuffer.Append(ch);
26 | }
27 | } else if (ch == '\n') { // Unix style
28 | break;
29 | } else {
30 | stringBuffer.Append(ch);
31 | }
32 | }
33 | } catch (EndOfStreamException) {
34 | eos = true;
35 | }
36 |
37 | if (stringBuffer.Length == 0)
38 | return "";
39 | else
40 | return stringBuffer.ToString();
41 | }
42 |
43 | public string Path { get; }
44 | }
45 |
--------------------------------------------------------------------------------
/SeeSharp/IO/PlyLoader.cs:
--------------------------------------------------------------------------------
1 | using static SeeSharp.IO.IMeshLoader;
2 |
3 | namespace SeeSharp.IO;
4 |
5 | ///
6 | /// Loads a mesh from a binary or ASCII .ply file
7 | ///
8 | public class PlyLoader : IMeshLoader {
9 | public string Type => "ply";
10 |
11 | public (IEnumerable, IEnumerable) LoadMesh(Dictionary namedMaterials,
12 | Dictionary emissiveMaterials,
13 | JsonElement jsonElement, string dirname) {
14 | // The path is relative to this .json, we need to make it absolute / relative to the CWD
15 | string relpath = jsonElement.GetProperty("relativePath").GetString();
16 | string filename = Path.Join(dirname, relpath);
17 |
18 | // In contrast to obj and fbx, ply files only have one material assigned
19 | string materialName = jsonElement.GetProperty("material").GetString();
20 | var material = namedMaterials[materialName];
21 |
22 | // Load the mesh and add it to the scene.
23 | PlyFile plyFile = new();
24 | if (!plyFile.ParseFile(filename))
25 | return (null, null);
26 |
27 | var mesh = plyFile.ToMesh();
28 | mesh.Material = material;
29 |
30 | IEnumerable emitters = null;
31 | if (emissiveMaterials != null && emissiveMaterials.TryGetValue(materialName, out var emission)) {
32 | emitters = emission.IsGlossy
33 | ? GlossyEmitter.MakeFromMesh(mesh, emission.Radiance, emission.Exponent)
34 | : DiffuseEmitter.MakeFromMesh(mesh, emission.Radiance);
35 | } else if (jsonElement.TryGetProperty("emission", out var emissionJson)) {
36 | emitters = DiffuseEmitter.MakeFromMesh(mesh, JsonUtils.GetRgbColor(emissionJson));
37 | }
38 |
39 | return (new[] { mesh }, emitters);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/SeeSharp/IO/TriMeshLoader.cs:
--------------------------------------------------------------------------------
1 | using static SeeSharp.IO.IMeshLoader;
2 |
3 | namespace SeeSharp.IO;
4 |
5 | internal class TriMeshLoader : IMeshLoader {
6 | public string Type => "trimesh";
7 |
8 | public (IEnumerable, IEnumerable) LoadMesh(Dictionary namedMaterials,
9 | Dictionary emissiveMaterials,
10 | JsonElement jsonElement, string dirname) {
11 | string materialName = jsonElement.GetProperty("material").GetString();
12 | var material = namedMaterials[materialName];
13 |
14 | Vector3[] ReadVec3Array(JsonElement json) {
15 | var result = new Vector3[json.GetArrayLength() / 3];
16 | for (int idx = 0; idx < json.GetArrayLength(); idx += 3) {
17 | result[idx / 3].X = json[idx + 0].GetSingle();
18 | result[idx / 3].Y = json[idx + 1].GetSingle();
19 | result[idx / 3].Z = json[idx + 2].GetSingle();
20 | }
21 | return result;
22 | }
23 |
24 | Vector2[] ReadVec2Array(JsonElement json) {
25 | var result = new Vector2[json.GetArrayLength() / 2];
26 | for (int idx = 0; idx < json.GetArrayLength(); idx += 2) {
27 | result[idx / 2].X = json[idx + 0].GetSingle();
28 | result[idx / 2].Y = json[idx + 1].GetSingle();
29 | }
30 | return result;
31 | }
32 |
33 | int[] ReadIntArray(JsonElement json) {
34 | var result = new int[json.GetArrayLength()];
35 | int idx = 0;
36 | foreach (var v in json.EnumerateArray())
37 | result[idx++] = v.GetInt32();
38 | return result;
39 | }
40 |
41 | Vector3[] vertices = ReadVec3Array(jsonElement.GetProperty("vertices"));
42 | int[] indices = ReadIntArray(jsonElement.GetProperty("indices"));
43 |
44 | Vector3[] normals = null;
45 | if (jsonElement.TryGetProperty("normals", out var normalsJson))
46 | normals = ReadVec3Array(jsonElement.GetProperty("normals"));
47 |
48 | Vector2[] uvs = null;
49 | if (jsonElement.TryGetProperty("uv", out var uvJson))
50 | uvs = ReadVec2Array(jsonElement.GetProperty("uv"));
51 |
52 | var mesh = new Mesh(vertices, indices, normals, uvs) { Material = material };
53 |
54 | IEnumerable emitters = null;
55 | if (emissiveMaterials != null && emissiveMaterials.TryGetValue(materialName, out var emission)) {
56 | emitters = emission.IsGlossy
57 | ? GlossyEmitter.MakeFromMesh(mesh, emission.Radiance, emission.Exponent)
58 | : DiffuseEmitter.MakeFromMesh(mesh, emission.Radiance);
59 | } else if (jsonElement.TryGetProperty("emission", out var emissionJson)) {
60 | emitters = DiffuseEmitter.MakeFromMesh(mesh, JsonUtils.GetRgbColor(emissionJson));
61 | }
62 |
63 | return (new[] { mesh }, emitters);
64 | }
65 | }
--------------------------------------------------------------------------------
/SeeSharp/Images/ImageTexture.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Images;
2 |
3 | ///
4 | /// An image texture
5 | ///
6 | public class ImageTexture {
7 | ///
8 | /// How texture coordinates outside the [0,1] range are mapped to pixels in the image
9 | ///
10 | public enum BorderHandling {
11 | ///
12 | /// Repeat the image in all directions, 1.1 is mapped to 0.1
13 | ///
14 | Repeat,
15 |
16 | ///
17 | /// Maps coordinates outside the image to the closest pixel that is within the image
18 | ///
19 | Clamp
20 | }
21 |
22 | ///
23 | /// The border handling mode to be used, defaults to "Repeat"
24 | ///
25 | public BorderHandling Border = BorderHandling.Repeat;
26 |
27 | (int, int) ApplyBorderHandling(int col, int row) {
28 | if (Border == BorderHandling.Repeat) {
29 | row = (row % Image.Height + Image.Height) % Image.Height;
30 | col = (col % Image.Width + Image.Width) % Image.Width;
31 | } else if (Border == BorderHandling.Clamp) {
32 | row = System.Math.Clamp(row, 0, Image.Height - 1);
33 | col = System.Math.Clamp(col, 0, Image.Width - 1);
34 | }
35 |
36 | return (col, row);
37 | }
38 |
39 | /// The (x,y) / (col,row) coordinate of the texel.
40 | public (int, int) ComputeTexel(Vector2 uv) {
41 | int col = (int)(uv.X * Image.Width);
42 | int row = (int)(uv.Y * Image.Height);
43 | return ApplyBorderHandling(col, row);
44 | }
45 |
46 | ///
47 | /// The texture image
48 | ///
49 | public Image Image;
50 | }
--------------------------------------------------------------------------------
/SeeSharp/Images/Layer.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Images;
2 |
3 | ///
4 | /// A layer in the frame buffer, an image to hold AOVs
5 | ///
6 | public abstract class Layer {
7 | bool frozen = false;
8 |
9 | ///
10 | /// The image buffer
11 | ///
12 | public Image Image { get; set; }
13 |
14 | ///
15 | /// Stops normalizing the image data. Useful if an AOV is only written during some initial iterations.
16 | ///
17 | public virtual void Freeze() {
18 | frozen = true;
19 | }
20 |
21 | ///
22 | /// Called once before the first rendering iteration
23 | ///
24 | /// The width of the frame buffer
25 | /// The height of the frame buffer
26 | public abstract void Init(int width, int height);
27 |
28 | ///
29 | /// Called at the beginning of each new rendering iteration. Derived classes should always call
30 | /// this function to achieve proper normalization
31 | ///
32 | /// The 1-based index of the iteration that starts now
33 | public virtual void OnStartIteration(int curIteration) {
34 | if (curIteration > 1 && !frozen)
35 | Image.Scale((curIteration - 1.0f) / curIteration);
36 | this.curIteration = curIteration;
37 | }
38 |
39 | ///
40 | /// Called at the end of each rendering iteration
41 | ///
42 | /// The 1-based index of the iteration that just finished
43 | public virtual void OnEndIteration(int curIteration) { }
44 |
45 | ///
46 | /// The 1-based index of the iteration that is currently being rendered
47 | ///
48 | protected int curIteration;
49 | }
--------------------------------------------------------------------------------
/SeeSharp/Images/MonoLayer.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Images;
2 |
3 | ///
4 | /// Convenience layer for images storing monochromatic values
5 | ///
6 | public class MonoLayer : Layer {
7 | ///
8 | /// Called once before the first rendering iteration
9 | ///
10 | /// The width of the frame buffer
11 | /// The height of the frame buffer
12 | public override void Init(int width, int height) => Image = new MonochromeImage(width, height);
13 |
14 | ///
15 | /// Adds a new sample contribution to the layer
16 | ///
17 | public virtual void Splat(int x, int y, float value)
18 | => (Image as MonochromeImage).AtomicAdd(x, y, value / curIteration);
19 |
20 | ///
21 | /// Adds a new sample contribution to the layer
22 | ///
23 | public virtual void Splat(Pixel pixel, float value) => Splat(pixel.Col, pixel.Row, value);
24 | }
--------------------------------------------------------------------------------
/SeeSharp/Images/Pixel.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Images;
2 |
3 | ///
4 | /// Tracks the integer coordinates of a pixel in an image
5 | ///
6 | /// Horizontal position, 0 is leftmost, Width-1 is rightmost
7 | /// Vertical position, 0 is topmost, Height-1 is bottommost
8 | public record struct Pixel(int Col, int Row) {}
9 |
--------------------------------------------------------------------------------
/SeeSharp/Images/RgbLayer.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Images;
2 |
3 | ///
4 | /// Convenience layer for images storing RGB values
5 | ///
6 | public class RgbLayer : Layer {
7 | ///
8 | /// Called once before the first rendering iteration
9 | ///
10 | /// The width of the frame buffer
11 | /// The height of the frame buffer
12 | public override void Init(int width, int height) => Image = new RgbImage(width, height);
13 |
14 | ///
15 | /// Adds a new sample contribution to the layer
16 | ///
17 | public virtual void Splat(int x, int y, RgbColor value)
18 | => (Image as RgbImage).AtomicAdd((int)x, (int)y, value / curIteration);
19 |
20 | ///
21 | /// Adds a new sample contribution to the layer
22 | ///
23 | public virtual void Splat(Pixel pixel, RgbColor value) => Splat(pixel.Col, pixel.Row, value);
24 | }
--------------------------------------------------------------------------------
/SeeSharp/Images/TextureMono.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Images;
2 |
3 | ///
4 | /// Monochromatic image texture
5 | ///
6 | public class TextureMono : ImageTexture {
7 | ///
8 | /// Creates a single pixel texture set to a constant value
9 | ///
10 | public TextureMono(float color) => constColor = color;
11 |
12 | ///
13 | /// Creates a texture from the given file
14 | ///
15 | /// Path to the monochrome image to load
16 | public TextureMono(string filename) => Image = new MonochromeImage(filename);
17 |
18 | ///
19 | /// Creates a texture from a monochromatic image
20 | ///
21 | public TextureMono(MonochromeImage img) => Image = img;
22 |
23 | // TODO not yet supported in SimpleImageIO
24 | // public TextureMono(string filename) => image = new MonochromeImage(filename);
25 |
26 | ///
27 | /// True if the texture is just a single constant value
28 | ///
29 | public bool IsConstant => Image == null;
30 |
31 | /// The texture value for the given uv-coordinates.
32 | public float Lookup(Vector2 uv) {
33 | if (Image == null)
34 | return constColor;
35 |
36 | (int col, int row) = ComputeTexel(uv);
37 | return (Image as MonochromeImage).GetPixel(col, row);
38 | }
39 |
40 | float constColor;
41 | }
--------------------------------------------------------------------------------
/SeeSharp/Images/TextureRgb.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Images;
2 |
3 | ///
4 | /// An RGB image texture
5 | ///
6 | public class TextureRgb : ImageTexture {
7 | ///
8 | /// Creates a single-pixel image texture with a constant color
9 | ///
10 | public TextureRgb(RgbColor color) => constColor = color;
11 |
12 | ///
13 | /// Creates a texture from an RGB image
14 | ///
15 | /// Full path to the RGB image
16 | public TextureRgb(string filename) => Image = new RgbImage(filename);
17 |
18 | ///
19 | /// Creates a texture from an RGB image
20 | ///
21 | public TextureRgb(RgbImage image) => Image = image;
22 |
23 | ///
24 | /// True if the texture is just a single constant value
25 | ///
26 | public bool IsConstant => Image == null;
27 |
28 | /// Color value at the given uv-coordinates
29 | public RgbColor Lookup(Vector2 uv) {
30 | if (Image == null)
31 | return constColor;
32 |
33 | (int col, int row) = ComputeTexel(uv);
34 | return (Image as RgbImage).GetPixel(col, row);
35 | }
36 |
37 | RgbColor constColor;
38 | }
--------------------------------------------------------------------------------
/SeeSharp/Imports.cs:
--------------------------------------------------------------------------------
1 | global using System;
2 | global using System.IO;
3 | global using System.Collections.Generic;
4 | global using System.Threading;
5 | global using System.Threading.Tasks;
6 | global using System.Diagnostics;
7 | global using System.Numerics;
8 | global using System.Text.Json;
9 | global using System.Text.Json.Serialization;
10 |
11 | global using TinyEmbree;
12 | global using SimpleImageIO;
13 |
14 | global using SeeSharp.Geometry;
15 | global using SeeSharp.Sampling;
16 | global using SeeSharp.Common;
17 | global using SeeSharp.Cameras;
18 | global using SeeSharp.Images;
19 |
20 | global using SeeSharp.Shading;
21 | global using SeeSharp.Shading.Background;
22 | global using SeeSharp.Shading.Materials;
23 | global using SeeSharp.Shading.Emitters;
24 | global using static SeeSharp.Shading.ShadingSpace;
25 |
26 | global using SeeSharp.Integrators;
27 | global using SeeSharp.Integrators.Common;
28 | global using SeeSharp.Integrators.Util;
29 | global using SeeSharp.Integrators.Bidir;
--------------------------------------------------------------------------------
/SeeSharp/Integrators/Bidir/VertexSelector.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Integrators.Bidir;
2 |
3 | ///
4 | /// Helper class to select random vertices from a path vertex cache.
5 | /// Ignores the first vertices of all light paths (the ones on the lights).
6 | ///
7 | /// The light subpath cache
8 | public struct VertexSelector(PathCache cache) {
9 | ///
10 | /// Randomly selects a light subpath vertex
11 | ///
12 | /// RNG to use
13 | /// Index of the path, index of the vertex along the path
14 | public (int, int) Select(ref RNG rng) {
15 | int idx = rng.NextInt(Count);
16 | return (-1, idx);
17 | }
18 |
19 | ///
20 | /// Number of light subpath vertices that can be connected to
21 | ///
22 | public int Count => cache.NumVertices;
23 |
24 | PathCache cache = cache;
25 | }
26 |
--------------------------------------------------------------------------------
/SeeSharp/Integrators/Common/PathBuffer.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Integrators.Common;
2 |
3 | ///
4 | /// A container optimized to be frequently and repeatedly re-used to store small
5 | /// batches of data (e.g., the vertices along the currently traced path).
6 | ///
7 | public class PathBuffer(int expectedLength) {
8 | T[] buffer = new T[expectedLength];
9 | int next = 0;
10 |
11 | public void Add(in T value) {
12 | if (next == buffer.Length) {
13 | T[] bigger = new T[buffer.Length * 2];
14 | buffer.CopyTo(bigger, 0);
15 | buffer = bigger;
16 | }
17 | buffer[next++] = value;
18 | }
19 |
20 | public void Clear() => next = 0;
21 |
22 | public int Count => next;
23 |
24 | public ref T this[int i] => ref buffer[i];
25 |
26 | public ReadOnlySpan AsSpan() => buffer.AsSpan(0, next);
27 | }
--------------------------------------------------------------------------------
/SeeSharp/Integrators/Common/PathVertex.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Integrators.Common;
2 |
3 | ///
4 | /// Stores the info of a single vertex of a cached light path
5 | ///
6 | public struct PathVertex {
7 | ///
8 | /// The surface intersection. If this is the first vertex of a background path, this point is not actually
9 | /// on a surface but somewhere in free space outside the scene.
10 | ///
11 | public SurfacePoint Point;
12 |
13 | ///
14 | /// Surface area pdf to sample this vertex from the previous one, i.e., the actual density this vertex
15 | /// was sampled from
16 | ///
17 | public float PdfFromAncestor;
18 |
19 | /// Surface area pdf to sample the ancestor of the previous vertex.
20 | public float PdfReverseAncestor;
21 |
22 | /// Surface area pdf of next event estimation at the ancestor (if applicable)
23 | public float PdfNextEventAncestor;
24 |
25 | ///
26 | /// Normalized direction from this vertex to its ancestor
27 | ///
28 | public Vector3 DirToAncestor;
29 |
30 | ///
31 | /// cos/d^2, cosine at the ancestor divided by squared distance
32 | ///
33 | public float JacobianToAncestor;
34 |
35 | ///
36 | /// Accumulated Monte Carlo weight of the sub-path up to and including this vertex
37 | ///
38 | public RgbColor Weight;
39 |
40 | ///
41 | /// 0-based index of the path this vertex belongs to
42 | ///
43 | public int PathId;
44 |
45 | ///
46 | /// The number of edges along the path.
47 | ///
48 | public byte Depth;
49 |
50 | ///
51 | /// Maximum roughness of the materials at any of the previous vertices and this one.
52 | ///
53 | public float MaximumRoughness;
54 |
55 | ///
56 | /// True if the path behind this vertex originated from the background rather than an emissive surface
57 | ///
58 | public bool FromBackground;
59 | }
60 |
--------------------------------------------------------------------------------
/SeeSharp/Integrators/DebugVisualizer.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Integrators;
2 |
3 | ///
4 | /// Renders a simple and fast grayscale visualization of a scene
5 | ///
6 | public class DebugVisualizer : Integrator {
7 | ///
8 | /// Base seed used for anti-aliasing
9 | ///
10 | public uint BaseSeed = 0xC030114;
11 |
12 | ///
13 | /// Number of anti-aliasing samples to take in each pixel
14 | ///
15 | public int TotalSpp = 1;
16 |
17 | ///
18 | /// Renders the given scene.
19 | ///
20 | public override void Render(Scene scene) {
21 | for (uint sampleIndex = 0; sampleIndex < TotalSpp; ++sampleIndex) {
22 | scene.FrameBuffer.StartIteration();
23 | Parallel.For(0, scene.FrameBuffer.Height,
24 | row => {
25 | for (uint col = 0; col < scene.FrameBuffer.Width; ++col) {
26 | RenderPixel(scene, (uint)row, col, sampleIndex);
27 | }
28 | }
29 | );
30 | scene.FrameBuffer.EndIteration();
31 | }
32 | }
33 |
34 | ///
35 | /// The shading value at a primary hit point. The default implementation uses "eye light shading",
36 | /// i.e., the cosine between the outgoing direction and the normal.
37 | ///
38 | public virtual RgbColor ComputeColor(SurfacePoint hit, Vector3 from, uint row, uint col) {
39 | float cosine = Math.Abs(Vector3.Dot(hit.Normal, from));
40 | cosine /= hit.Normal.Length();
41 | cosine /= from.Length();
42 | return RgbColor.White * cosine;
43 | }
44 |
45 | public virtual void RenderPixel(Scene scene, uint row, uint col, uint sampleIndex) {
46 | // Seed the random number generator
47 | uint pixelIndex = row * (uint)scene.FrameBuffer.Width + col;
48 | var rng = new RNG(BaseSeed, pixelIndex, sampleIndex);
49 |
50 | // Sample a ray from the camera
51 | var offset = rng.NextFloat2D();
52 | Ray primaryRay = scene.Camera.GenerateRay(new Vector2(col, row) + offset, ref rng).Ray;
53 | var hit = scene.Raytracer.Trace(primaryRay);
54 |
55 | // Shade and splat
56 | RgbColor value = RgbColor.Black;
57 | if (hit) value = ComputeColor(hit, -primaryRay.Direction, row, col);
58 | scene.FrameBuffer.Splat((int)col, (int)row, value);
59 | }
60 | }
--------------------------------------------------------------------------------
/SeeSharp/Integrators/Integrator.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Integrators;
2 |
3 | ///
4 | /// Base class for all rendering algorithms.
5 | ///
6 | public abstract class Integrator {
7 | ///
8 | /// Maximum path length for global illumination algorithms. Default is 100.
9 | ///
10 | public int MaxDepth { get; set; } = 100;
11 |
12 | ///
13 | /// Minimum length (in edges) of a path that can contribute to the image. If set to 2, e.g., directly
14 | /// visible lights are not rendered. Default is 1.
15 | ///
16 | public int MinDepth { get; set; } = 1;
17 |
18 | ///
19 | /// Renders a scene to the frame buffer that is specified by the object.
20 | ///
21 | /// The scene to render
22 | public abstract void Render(Scene scene);
23 |
24 | ///
25 | /// Re-renders a pixel as it was rendered in a specific iteration.
26 | ///
27 | /// The paths that contributed to this pixel as a connected graph
28 | public virtual (PathGraph Graph, RgbColor Estimate) ReplayPixel(Scene scene, Pixel pixel, int iteration)
29 | => throw new NotSupportedException("This integrator does not implement path replay");
30 | }
31 |
--------------------------------------------------------------------------------
/SeeSharp/Integrators/Util/DenoiseBuffers.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Integrators.Util;
2 |
3 | ///
4 | /// Convenience wrapper to add common feature buffers to a frame buffer that are useful for denoising.
5 | ///
6 | public class DenoiseBuffers {
7 | ///
8 | /// Adds the required layers to a frame buffer and tracks there references in this object
9 | ///
10 | public DenoiseBuffers(FrameBuffer frameBuffer) {
11 | frameBuffer.AddLayer("albedo", albedo);
12 | frameBuffer.AddLayer("normal", normal);
13 | frameBuffer.AddLayer("denoised", denoised);
14 | this.frameBuffer = frameBuffer;
15 | }
16 |
17 | ///
18 | /// Logs the features at a primary hit point
19 | ///
20 | public void LogPrimaryHit(Pixel pixel, RgbColor albedo, Vector3 normal) {
21 | this.albedo.Splat(pixel, albedo);
22 | this.normal.Splat(pixel, normal);
23 | }
24 |
25 | ///
26 | /// Runs the denoiser on the current rendered image. The result is stored in the "denoised" layer.
27 | ///
28 | public void Denoise() {
29 | Image.Move(
30 | denoiser.Denoise(frameBuffer.Image, (RgbImage)albedo.Image, (RgbImage)normal.Image),
31 | denoised.Image);
32 | }
33 |
34 | RgbLayer albedo = new();
35 | RgbLayer normal = new();
36 | RgbLayer denoised = new();
37 | FrameBuffer frameBuffer;
38 | Denoiser denoiser = new();
39 | }
--------------------------------------------------------------------------------
/SeeSharp/Integrators/Util/OutlierReplayCache.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Integrators.Util;
2 |
3 | public class OutlierReplayCache {
4 | public struct PathReplayInfo {
5 | public RgbColor Weight;
6 | public int Iteration;
7 | }
8 |
9 | public void Notify(in Pixel pixel, in PathReplayInfo info) {
10 | float w = info.Weight.Average;
11 |
12 | if (!float.IsFinite(w)) {
13 | // NaN / Inf replay info is logged by the FrameBuffer already
14 | return;
15 | }
16 |
17 | var q = pixelHeaps[pixel.Row * width + pixel.Col];
18 | lock (q) {
19 | if (q.Count < nMax) q.Enqueue(info, w);
20 | else q.EnqueueDequeue(info, w);
21 | }
22 | }
23 |
24 | public OutlierReplayCache(int width, int height, int n) {
25 | this.width = width;
26 | nMax = n;
27 |
28 | pixelHeaps = new PriorityQueue[width * height];
29 | for (int i = 0; i < width * height; ++i)
30 | pixelHeaps[i] = new(n + 1);
31 | }
32 |
33 | public PriorityQueue GetPixelOutlier(in Pixel pixel)
34 | => pixelHeaps[pixel.Row * width + pixel.Col];
35 |
36 | PriorityQueue[] pixelHeaps;
37 | int width, nMax;
38 | }
39 |
--------------------------------------------------------------------------------
/SeeSharp/Integrators/Util/RenderTimer.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Integrators.Util;
2 |
3 | ///
4 | /// Utility to track the time spent rendering and the frame buffer IO overhead separately.
5 | /// Can be used to achieve fair equal-time renderings.
6 | ///
7 | public class RenderTimer {
8 | ///
9 | /// The total time spent processing the frame buffer so far, in milliseconds.
10 | ///
11 | public long FrameBufferTime { get; private set; }
12 |
13 | ///
14 | /// The total time spent in the actual rendering code so far, in milliseconds.
15 | ///
16 | public long RenderTime { get; private set; }
17 |
18 | ///
19 | /// The estimated cost of a single iteration, in milliseconds.
20 | ///
21 | public long PerIterationCost { get; private set; }
22 |
23 | ///
24 | /// The total duration of the last iteration (including all overheads) in seconds. This should
25 | /// only be used to update progress bars etc, not for equal time rendering.
26 | ///
27 | public double CurrentIterationSeconds { get; private set; }
28 |
29 | Stopwatch timer = new();
30 | int numIter = 0;
31 |
32 | ///
33 | /// Adds the elapsed time to the frame buffer cost and resets the timer
34 | ///
35 | public void EndFrameBuffer() {
36 | FrameBufferTime += timer.ElapsedMilliseconds;
37 | CurrentIterationSeconds += timer.Elapsed.TotalSeconds;
38 | timer.Restart();
39 | }
40 |
41 | ///
42 | /// Adds the elapsed time to the rendering cost and resets the timer
43 | ///
44 | public void EndRender() {
45 | RenderTime += timer.ElapsedMilliseconds;
46 | CurrentIterationSeconds += timer.Elapsed.TotalSeconds;
47 | timer.Restart();
48 | }
49 |
50 | ///
51 | /// Starts a new timer for the next iteration
52 | ///
53 | public void StartIteration() {
54 | CurrentIterationSeconds = 0;
55 | numIter++;
56 | timer.Restart();
57 | }
58 |
59 | ///
60 | /// Updates statistics at the end of each iteration
61 | ///
62 | public void EndIteration() => PerIterationCost = RenderTime / numIter;
63 | }
64 |
--------------------------------------------------------------------------------
/SeeSharp/Sampling/ISampler.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Sampling;
2 |
3 | public interface ISampler {
4 | float NextFloat();
5 | Vector2 NextFloat2D();
6 | }
7 |
--------------------------------------------------------------------------------
/SeeSharp/Sampling/PiecewiseConstant.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Sampling;
2 |
3 | ///
4 | /// A piece-wise constant PDF / discrete probability to sample from
5 | ///
6 | public class PiecewiseConstantPDF {
7 | ///
8 | /// Initializes the piece-wise constant pdf over the [0, 1] domain, where each piece has the same length.
9 | /// The given weights are normalized and the CDF is computed.
10 | ///
11 | /// The non-normalized weights of each bin
12 | public PiecewiseConstantPDF(params ReadOnlySpan weights) {
13 | // Compute unnormalized cdf
14 | cdf = new List(weights.Length);
15 | float sum = 0;
16 | foreach (float w in weights) {
17 | cdf.Add(sum += w);
18 | }
19 |
20 | // Normalize
21 | float total = cdf[^1];
22 | for (int i = 0; i < cdf.Count && total > 0.0f; ++i) {
23 | cdf[i] /= total;
24 | Debug.Assert(float.IsFinite(cdf[i]));
25 | }
26 |
27 | // Force the last value to one for numerical stability
28 | cdf[^1] = 1.0f;
29 | }
30 |
31 | ///
32 | /// Transforms a primary sample to one distributed according to the
33 | /// piecewise constant density encoded by this object.
34 | ///
35 | /// A primary sample in [0,1]
36 | /// The bin index, and the relative position within the bin.
37 | public (int BinIndex, float RelativePosition) Sample(float primarySample) {
38 | // Find the index of the next greater (or exact) match in the CDF
39 | int idx = cdf.BinarySearch(primarySample);
40 | if (idx < 0) idx = ~idx;
41 | else // Make sure we find the first element, some might have zero probability!
42 | for (; idx > 0 && cdf[idx - 1] == primarySample; --idx) { }
43 |
44 | // Compute the relative position within the constant region
45 | float lo = idx == 0 ? 0 : cdf[idx - 1];
46 | float delta = cdf[idx] - lo;
47 | float relative = (primarySample - lo) / delta;
48 |
49 | return (idx, relative);
50 | }
51 |
52 | ///
53 | /// Performs the inverse of the transform used by .
54 | ///
55 | /// The bin index
56 | /// Position within the bin
57 | /// The primary sample that is mapped to this position
58 | public float SampleInverse(int idx, float relative) {
59 | float lo = idx == 0 ? 0 : cdf[idx - 1];
60 | float delta = cdf[idx] - lo;
61 | return delta * relative + lo;
62 | }
63 |
64 | /// Index of a bin
65 | /// The probability that any sampled point lies within the bin
66 | public float Probability(int idx) {
67 | if (idx > 0)
68 | return cdf[idx] - cdf[idx - 1];
69 | return cdf[idx];
70 | }
71 |
72 | /// Index of a bin in the piecewise distribution
73 | /// The CDF value of the given bin
74 | public float CumulativeProbability(int idx) => cdf[idx];
75 |
76 | List cdf;
77 | }
78 |
--------------------------------------------------------------------------------
/SeeSharp/Sampling/RegularGrid3d.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Numerics;
3 |
4 | namespace SeeSharp.Sampling {
5 | ///
6 | /// A regular grid on the unit cube.
7 | /// Useful for describing 3D pdfs in primary sample space.
8 | ///
9 | public class RegularGrid3d {
10 | public RegularGrid3d(int resx, int resy, int resz) {
11 | this.zRes = resz;
12 | grid = new RegularGrid2d[resz];
13 | for (int i = 0; i < resz; ++i)
14 | grid[i] = new RegularGrid2d(resx, resy);
15 | depthMarginals = new float[resz];
16 | }
17 |
18 | public Vector3 Sample(Vector3 primary) {
19 | var (depthIdx, relDepth) = depthDistribution.Sample(primary.Z);
20 | float z = (depthIdx + relDepth) / zRes;
21 | var pos = grid[depthIdx].Sample(new Vector2(primary.X, primary.Y));
22 | return new Vector3(pos.X, pos.Y, z);
23 | }
24 |
25 | public float Pdf(Vector3 pos) {
26 | int d = Math.Min((int)(pos.Z * zRes), zRes - 1);
27 | float pz = depthDistribution.Probability(d) * zRes;
28 | if (pz == 0) return 0;
29 | float pxy = grid[d].Pdf(new Vector2(pos.X, pos.Y));
30 | return pz * pxy;
31 | }
32 |
33 | public void Splat(float x, float y, float z, float value) {
34 | int d = (int)(z * zRes);
35 | depthMarginals[d] += value;
36 | grid[d].Splat(x, y, value);
37 | }
38 |
39 | public void Normalize() {
40 | depthDistribution = new PiecewiseConstantPDF(depthMarginals);
41 | for (int i = 0; i < zRes; ++i) {
42 | if (depthMarginals[i] > 0) grid[i].Normalize();
43 | }
44 | }
45 |
46 | RegularGrid2d[] grid;
47 | int zRes;
48 | float[] depthMarginals;
49 | PiecewiseConstantPDF depthDistribution;
50 | }
51 | }
--------------------------------------------------------------------------------
/SeeSharp/SeeSharp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net9.0
4 | preview
5 |
6 | SeeSharp
7 | A flexibility-first framework for rapid prototyping of rendering algorithms.
8 | SeeSharp
9 | 2.4.0
10 | (c) Pascal Grittmann
11 | https://github.com/pgrit/SeeSharp
12 |
13 | LICENSE
14 | Pascal Grittmann
15 | true
16 | true
17 | rendering global illumination ray tracing bidirectional renderer
18 |
19 | logo.png
20 |
21 | true
22 | 1591
23 |
24 | true
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/SeeSharp/Shading/Background/Background.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Shading.Background;
2 |
3 | ///
4 | /// Base class for all sorts of sky models, image based lighting, etc.
5 | ///
6 | public abstract class Background {
7 | ///
8 | /// Computes the emitted radiance from a given direction. All backgrounds are invariant with respect to the position.
9 | ///
10 | public abstract RgbColor EmittedRadiance(Vector3 direction);
11 |
12 | public abstract RgbColor ComputeTotalPower();
13 |
14 | public abstract BackgroundSample SampleDirection(Vector2 primary);
15 | public abstract Vector2 SampleDirectionInverse(Vector3 Direction);
16 |
17 | ///
18 | /// Computes the PDF for sampling a given direction from the background
19 | ///
20 | /// Direction from the scene towards the background
21 | /// Solid angle PDF
22 | public abstract float DirectionPdf(Vector3 Direction);
23 | public abstract (Ray, RgbColor, float) SampleRay(Vector2 primaryPos, Vector2 primaryDir);
24 | public abstract (Vector2, Vector2) SampleRayInverse(Vector3 dir, Vector3 pos);
25 |
26 | ///
27 | /// Computes the pdf value for sampling a ray from the background towards the scene.
28 | ///
29 | /// A point along the ray. Could be the start, end, or some other point.
30 | /// Direction of the ray (i.e., from the background to the scene).
31 | ///
32 | public abstract float RayPdf(Vector3 point, Vector3 direction);
33 |
34 | public Vector3 SceneCenter;
35 | public float SceneRadius;
36 | }
--------------------------------------------------------------------------------
/SeeSharp/Shading/Background/BackgroundSample.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Shading.Background;
2 |
3 | public struct BackgroundSample {
4 | public RgbColor Weight;
5 | public Vector3 Direction;
6 | public float Pdf;
7 | }
--------------------------------------------------------------------------------
/SeeSharp/Shading/Emitters/EmitterSample.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Shading.Emitters;
2 |
3 | public struct EmitterSample {
4 | public SurfacePoint Point;
5 | public Vector3 Direction;
6 | public float Pdf;
7 |
8 | // Sample weight for an MC estimate of the total emitted power
9 | public RgbColor Weight;
10 | }
--------------------------------------------------------------------------------
/SeeSharp/Shading/Fresnel.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Shading;
2 |
3 | public static class Fresnel {
4 | public static float Dielectric(float cosThetaI, float etaI, float etaT) {
5 | cosThetaI = Math.Clamp(cosThetaI, -1, 1);
6 | // Potentially swap indices of refraction
7 | bool entering = cosThetaI > 0;
8 | if (!entering) {
9 | (etaT, etaI) = (etaI, etaT);
10 | cosThetaI = Math.Abs(cosThetaI);
11 | }
12 |
13 | // Compute _cosThetaT_ using Snell's law
14 | float sinThetaI = MathF.Sqrt(Math.Max(0, 1 - cosThetaI * cosThetaI));
15 | float sinThetaT = etaI / etaT * sinThetaI;
16 |
17 | // Handle total internal reflection
18 | if (sinThetaT >= 1) return 1;
19 | float cosThetaT = MathF.Sqrt(Math.Max(0, 1 - sinThetaT * sinThetaT));
20 | float Rparl = (etaT * cosThetaI - etaI * cosThetaT) /
21 | (etaT * cosThetaI + etaI * cosThetaT);
22 | float Rperp = (etaI * cosThetaI - etaT * cosThetaT) /
23 | (etaI * cosThetaI + etaT * cosThetaT);
24 | return (Rparl * Rparl + Rperp * Rperp) / 2;
25 | }
26 |
27 | // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
28 | //
29 | // The Schlick Fresnel approximation is:
30 | //
31 | // R = R(0) + (1 - R(0)) (1 - cos theta)^5,
32 | //
33 | // where R(0) is the reflectance at normal indicence.
34 | public static float SchlickWeight(float cosTheta) {
35 | float m = Math.Clamp(1 - cosTheta, 0, 1);
36 | return (m * m) * (m * m) * m;
37 | }
38 |
39 | public static RgbColor SchlickFresnel(RgbColor R0, float cosTheta) {
40 | return RgbColor.Lerp(SchlickWeight(cosTheta), R0, RgbColor.White);
41 | }
42 |
43 | // For a dielectric, R(0) = (eta - 1)^2 / (eta + 1)^2, assuming we're
44 | // coming from air..
45 | public static float SchlickR0FromEta(float eta) {
46 | var ratio = (eta - 1) / (eta + 1);
47 | return ratio * ratio;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/SeeSharp/Shading/Materials/BsdfSample.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Shading.Materials;
2 |
3 | public struct BsdfSample {
4 | public Vector3 Direction;
5 | public float Pdf;
6 | public float PdfReverse;
7 |
8 | ///
9 | /// Sample weight of the reflectance estimate, i.e., the product of
10 | /// BSDF and shading cosine divided by the pdf.
11 | ///
12 | public RgbColor Weight;
13 |
14 | public static BsdfSample Invalid
15 | => new() { Pdf = 0, PdfReverse = 0, Weight = RgbColor.Black };
16 |
17 | public static implicit operator bool(BsdfSample sample)
18 | => sample.Pdf > 0 && sample.PdfReverse > 0;
19 | }
--------------------------------------------------------------------------------
/SeeSharp/Shading/Materials/ShadingContext.cs:
--------------------------------------------------------------------------------
1 | namespace SeeSharp.Shading.Materials;
2 |
3 | public struct ShadingContext {
4 | public SurfacePoint Point;
5 | public bool IsOnLightSubpath;
6 | public Vector3 Normal;
7 | public Vector3 Tangent;
8 | public Vector3 Binormal;
9 |
10 | ///
11 | /// Outgoing direction in shading space
12 | ///
13 | public Vector3 OutDir;
14 |
15 | public Vector3 OutDirWorld;
16 |
17 | public ShadingContext(in SurfacePoint point, in Vector3 outDir, bool isOnLightSubpath) {
18 | Point = point;
19 | IsOnLightSubpath = isOnLightSubpath;
20 | Normal = point.ShadingNormal;
21 | ComputeBasisVectors(Normal, out Tangent, out Binormal);
22 | OutDir = WorldToShading(outDir);
23 | OutDirWorld = outDir;
24 | }
25 |
26 | public Vector3 WorldToShading(in Vector3 dir) => ShadingSpace.WorldToShading(Normal, Tangent, Binormal, dir);
27 | public Vector3 ShadingToWorld(in Vector3 dir) => ShadingSpace.ShadingToWorld(Normal, Tangent, Binormal, dir);
28 | }
29 |
--------------------------------------------------------------------------------
/SeeSharp/Shading/ShadingStats.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 |
3 | namespace SeeSharp.Shading;
4 |
5 | public struct ShadingStats {
6 | public ulong NumMaterialEval { get; set; }
7 | public ulong NumMaterialSample { get; set; }
8 | public ulong NumMaterialPdf { get; set; }
9 | }
10 |
11 | public class ShadingStatCounter {
12 | static ShadingStatCounter currentCounter = new();
13 |
14 | public static void Reset() {
15 | currentCounter = new();
16 | }
17 |
18 | public static ShadingStats Current => new() {
19 | NumMaterialEval = (ulong)currentCounter.numEval.Values.Sum(v => (long)v),
20 | NumMaterialSample = (ulong)currentCounter.numSample.Values.Sum(v => (long)v),
21 | NumMaterialPdf = (ulong)currentCounter.numPdf.Values.Sum(v => (long)v),
22 | };
23 |
24 | readonly ThreadLocal numEval = new(true);
25 | readonly ThreadLocal numSample = new(true);
26 | readonly ThreadLocal numPdf = new(true);
27 |
28 | public static void NotifyEvaluate() => currentCounter.numEval.Value++;
29 |
30 | public static void NotifySample() => currentCounter.numSample.Value++;
31 |
32 | public static void NotifyPdfCompute() => currentCounter.numPdf.Value++;
33 | }
34 |
--------------------------------------------------------------------------------
/SeeSharp/extension.dib:
--------------------------------------------------------------------------------
1 | #!csharp
2 |
3 | using System;
4 | using System.Collections.Concurrent;
5 | using System.Collections.Generic;
6 | using System.Diagnostics;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Numerics;
10 | using System.Text.Json;
11 | using System.Text.Json.Serialization;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | using TinyEmbree;
16 | using SimpleImageIO;
17 |
18 | using SeeSharp;
19 | using SeeSharp.Cameras;
20 | using SeeSharp.Common;
21 | using SeeSharp.Experiments;
22 | using SeeSharp.Geometry;
23 | using SeeSharp.Images;
24 | using SeeSharp.Integrators;
25 | using SeeSharp.Integrators.Bidir;
26 | using SeeSharp.Integrators.Common;
27 | using SeeSharp.Integrators.Util;
28 | using SeeSharp.Sampling;
29 | using SeeSharp.Shading;
30 | using SeeSharp.Shading.Background;
31 | using SeeSharp.Shading.Emitters;
32 | using SeeSharp.Shading.Materials;
33 |
34 | // The polyglot notebook VSCode extension has exceptionally poor performance for rapid console output,
35 | // so we suppress the progress bar updates
36 | ProgressBar.Silent = true;
37 |
38 | // Loads a scene from file (using SceneRegistry) and initializes it for ray tracing with the given render resolution
39 | Scene QuickloadScene(string name, int width, int height) {
40 | var scene = SceneRegistry.LoadScene(name).MakeScene();
41 | scene.FrameBuffer = new(width, height, "");
42 | scene.Prepare();
43 | return scene;
44 | }
45 |
--------------------------------------------------------------------------------
/build_blender.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | dotnet publish ./SeeSharp.PreviewRender -c Release -o ./see_blender/bin
4 | if %errorlevel% neq 0 exit /b %errorlevel%
5 |
6 | zip -r see_blender see_blender
7 | if %errorlevel% neq 0 exit /b %errorlevel%
8 |
9 |
10 | echo Blender plugin built. Open Blender and go to 'Edit - Preferences - Addons - Install...'
11 |
12 | echo Browse to the 'see_blender.zip' file in this directory and install it.
--------------------------------------------------------------------------------
/build_blender.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | dotnet publish ./SeeSharp.PreviewRender -c Release -o ./see_blender/bin
4 | zip -r see_blender see_blender
5 |
6 | echo "Blender plugin built. Open Blender and go to 'Edit - Preferences - Addons - Install...'"
7 | echo "Browse to the 'see_blender.zip' file in this directory and install it."
--------------------------------------------------------------------------------
/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/logo.ico
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/logo.png
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/omnisharp.json:
--------------------------------------------------------------------------------
1 | {
2 | "FormattingOptions": {
3 | "NewLine": "\n",
4 | "UseTabs": false,
5 | "TabSize": 4,
6 | "IndentationSize": 4,
7 | "SpacingAfterMethodDeclarationName": false,
8 | "SpaceWithinMethodDeclarationParenthesis": false,
9 | "SpaceBetweenEmptyMethodDeclarationParentheses": false,
10 | "SpaceAfterMethodCallName": false,
11 | "SpaceWithinMethodCallParentheses": false,
12 | "SpaceBetweenEmptyMethodCallParentheses": false,
13 | "SpaceAfterControlFlowStatementKeyword": true,
14 | "SpaceWithinExpressionParentheses": false,
15 | "SpaceWithinCastParentheses": false,
16 | "SpaceWithinOtherParentheses": false,
17 | "SpaceAfterCast": false,
18 | "SpacesIgnoreAroundVariableDeclaration": false,
19 | "SpaceBeforeOpenSquareBracket": false,
20 | "SpaceBetweenEmptySquareBrackets": false,
21 | "SpaceWithinSquareBrackets": false,
22 | "SpaceAfterColonInBaseTypeDeclaration": true,
23 | "SpaceAfterComma": true,
24 | "SpaceAfterDot": false,
25 | "SpaceAfterSemicolonsInForStatement": true,
26 | "SpaceBeforeColonInBaseTypeDeclaration": true,
27 | "SpaceBeforeComma": false,
28 | "SpaceBeforeDot": false,
29 | "SpaceBeforeSemicolonsInForStatement": false,
30 | "SpacingAroundBinaryOperator": "single",
31 | "IndentBraces": false,
32 | "IndentBlock": true,
33 | "IndentSwitchSection": true,
34 | "IndentSwitchCaseSection": true,
35 | "LabelPositioning": "oneLess",
36 | "WrappingPreserveSingleLine": true,
37 | "WrappingKeepStatementsOnSingleLine": true,
38 | "NewLinesForBracesInTypes": false,
39 | "NewLinesForBracesInMethods": false,
40 | "NewLinesForBracesInProperties": false,
41 | "NewLinesForBracesInAccessors": false,
42 | "NewLinesForBracesInAnonymousMethods": false,
43 | "NewLinesForBracesInControlBlocks": false,
44 | "NewLinesForBracesInAnonymousTypes": false,
45 | "NewLinesForBracesInObjectCollectionArrayInitializers": false,
46 | "NewLinesForBracesInLambdaExpressionBody": false,
47 | "NewLineForElse": false,
48 | "NewLineForCatch": false,
49 | "NewLineForFinally": false,
50 | "NewLineForMembersInObjectInit": true,
51 | "NewLineForMembersInAnonymousTypes": true,
52 | "NewLineForClausesInQuery": true
53 | },
54 | "script": {
55 | "enableScriptNuGetReferences": true,
56 | "defaultTargetFramework": "net7.0"
57 | }
58 | }
--------------------------------------------------------------------------------
/see_blender/__init__.py:
--------------------------------------------------------------------------------
1 | import bpy
2 |
3 | from . import exporter, render_engine, material_ui, material, world
4 |
5 | bl_info = {
6 | "name": "SeeSharp Renderer",
7 | "author": "Pascal Grittmann",
8 | "version": (0, 1),
9 | "blender": (2, 92, 0),
10 | "category": "Render",
11 | }
12 |
13 | def register():
14 | exporter.register()
15 | render_engine.register()
16 | material_ui.register()
17 | material.register()
18 | world.register()
19 |
20 | def unregister():
21 | exporter.unregister()
22 | render_engine.unregister()
23 | material_ui.unregister()
24 | material.unregister()
25 | world.unregister()
--------------------------------------------------------------------------------
/see_blender/world.py:
--------------------------------------------------------------------------------
1 | import bpy
2 | from bl_ui.properties_world import WorldButtonsPanel
3 | from bpy.types import Panel
4 | from bpy.props import *
5 |
6 | class SeeSharpWorld(bpy.types.PropertyGroup):
7 | hdr: PointerProperty(
8 | name="HDR Texture",
9 | description="HDR image to illuminate the scene with",
10 | type=bpy.types.Image)
11 |
12 | @classmethod
13 | def register(cls):
14 | bpy.types.World.seesharp = PointerProperty(
15 | name="SeeSharpWorld",
16 | description="SeeSharp world settings",
17 | type=cls,
18 | )
19 |
20 | @classmethod
21 | def unregister(cls):
22 | del bpy.types.World.seesharp
23 |
24 | class SEESHARP_PT_context_world(WorldButtonsPanel, Panel):
25 | """
26 | UI Panel for world settings (HDR background)
27 | """
28 | COMPAT_ENGINES = {"SEE_SHARP"}
29 | bl_label = "HDR Background"
30 | bl_order = 1
31 |
32 | @classmethod
33 | def poll(cls, context):
34 | return context.world is not None and context.scene.render.engine == "SEE_SHARP"
35 |
36 | def draw(self, context):
37 | self.layout.template_ID(context.world.seesharp, "hdr", open="image.open")
38 |
39 | def register():
40 | bpy.utils.register_class(SeeSharpWorld)
41 | bpy.utils.register_class(SEESHARP_PT_context_world)
42 |
43 | def unregister():
44 | bpy.utils.unregister_class(SeeSharpWorld)
45 | bpy.utils.unregister_class(SEESHARP_PT_context_world)
--------------------------------------------------------------------------------