├── .editorconfig ├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── Data └── Scenes │ ├── CornellBox │ ├── CornellBox.json │ └── References │ │ ├── Config.json │ │ ├── MaxDepth2-Width512-Height512.exr │ │ ├── MaxDepth2-Width512-Height512.json │ │ ├── MaxDepth5-Width512-Height512.exr │ │ └── MaxDepth5-Width512-Height512.json │ ├── ExportTest │ └── ExportTest.blend │ ├── TextureTest │ ├── TextureTest.json │ ├── TextureTest.obj │ └── Textures │ │ └── TestPattern.png │ ├── furnacebox.json │ ├── simplebackground.json │ ├── simpledi.json │ ├── sunsky.exr │ └── uniform.exr ├── ExampleFigure.png ├── Fbx2Ply.dib ├── LICENSE ├── MaterialTest ├── App.razor ├── Imports.cs ├── MainLayout.razor ├── MaterialTest.csproj ├── Pages │ ├── Experiment.razor │ ├── Experiment.razor.cs │ ├── Index.razor │ ├── IntegratorTest.razor │ ├── IntegratorTest.razor.cs │ └── _Host.cshtml ├── Program.cs ├── _Imports.razor ├── appsettings.Development.json ├── appsettings.json └── wwwroot │ └── css │ └── site.css ├── README.md ├── SeeSharp.Benchmark ├── GenericMaterial_Sampling.cs ├── Program.cs ├── RunBench.fsx ├── SeeSharp.Benchmark.csproj └── VectorBench.cs ├── SeeSharp.Blazor ├── AutocompleteInput.razor ├── AutocompleteInput.razor.css ├── BoolSetting.razor ├── FlipViewer.razor ├── FloatSetting.razor ├── HtmlProgressBar.razor ├── IntSetting.razor ├── LogOutput.razor ├── LogOutput.razor.css ├── Readme.md ├── RotationInput.razor ├── SceneSelector.razor ├── SceneSelector.razor.css ├── Scripts.cs ├── Scripts │ └── rotationInput.js ├── SeeSharp.Blazor.csproj ├── SettingBase.razor ├── SettingBase.razor.css ├── SettingsGroup.razor ├── SettingsGroup.razor.css └── _Imports.razor ├── SeeSharp.Examples ├── Imports.cs ├── MakeFigure.py ├── MisCompensation.dib ├── PathVsVcm.cs ├── Program.cs ├── SeeSharp.Examples.csproj └── SphericalSampling.dib ├── SeeSharp.IntegrationTests ├── BidirZeroLightPaths.cs ├── ConsoleUtils.cs ├── LightProbeTest.cs ├── OutlierCacheTest.cs ├── Program.cs └── SeeSharp.IntegrationTests.csproj ├── SeeSharp.PreviewRender ├── Program.cs └── SeeSharp.PreviewRender.csproj ├── SeeSharp.Templates ├── README.md ├── SeeSharp.Templates.csproj └── content │ ├── SeeSharp.Blazor.Template │ ├── .template.config │ │ └── template.json │ ├── App.razor │ ├── Imports.cs │ ├── MainLayout.razor │ ├── Pages │ │ ├── Experiment.razor │ │ ├── Experiment.razor.cs │ │ ├── Index.razor │ │ └── _Host.cshtml │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── SeeSharp.Blazor.Template.csproj │ ├── _Imports.razor │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ │ └── css │ │ └── site.css │ └── SeeSharp.Template │ ├── .template.config │ └── template.json │ ├── Imports.cs │ ├── MakeFigure.py │ ├── MyExperiment.cs │ ├── Program.cs │ ├── Scenes │ └── ExampleScene │ │ ├── ExampleScene.blend │ │ ├── ExampleScene.blend.import │ │ ├── ExampleScene.json │ │ ├── Meshes │ │ ├── Floor.0.ply │ │ ├── FrontWall.0.ply │ │ ├── LampCord.0.ply │ │ ├── LampShade.0.ply │ │ ├── LeftWall.0.ply │ │ ├── LightBulb.0.ply │ │ ├── RightWall.0.ply │ │ ├── Ring.0.ply │ │ ├── Roof.0.ply │ │ └── Suzanne.0.ply │ │ └── References │ │ ├── Config.json │ │ ├── MaxDepth100-Width1280-Height768.exr │ │ └── MaxDepth100-Width1280-Height768.json │ └── SeeSharp.Template.csproj ├── SeeSharp.Tests ├── Core │ ├── Camera │ │ ├── LightProbe_Weights.cs │ │ ├── Perspective_Rays.cs │ │ └── Perspective_Splatting.cs │ ├── FrameBuffer.cs │ ├── Geometry │ │ ├── Mesh_Attributes.cs │ │ ├── Mesh_SampleInverse.cs │ │ ├── Mesh_Sampling.cs │ │ ├── ObjFiles_Import.cs │ │ └── PlyFiles_Import.cs │ ├── Imports.cs │ ├── Sampling │ │ ├── PiecewiseConstant_DistributionTest.cs │ │ ├── PiecewiseConstant_SampleInverse.cs │ │ ├── SampleWarp_Disc.cs │ │ ├── SampleWarp_Sphere.cs │ │ ├── SampleWarp_Spherical.cs │ │ ├── SampleWarp_Triangle.cs │ │ └── TrowbridgeReitz_CorrectValues.cs │ ├── Scene_Assemble.cs │ └── Shading │ │ ├── Background_EnvironmentMap.cs │ │ ├── Emitter_Diffuse.cs │ │ ├── Emitter_Glossy.cs │ │ ├── Material_Diffuse.cs │ │ ├── Material_Generic.cs │ │ ├── Material_SampleTransform.cs │ │ └── ShadingSpace_Setup_Transform.cs └── SeeSharp.Tests.csproj ├── SeeSharp.ToMitsuba ├── Program.cs └── SeeSharp.ToMitsuba.csproj ├── SeeSharp.Validation ├── Program.cs ├── SeeSharp.Validation.csproj ├── Validate_CornellBox.cs ├── Validate_DirectIllum.cs ├── Validate_DirectTransmit.cs ├── Validate_Environment.cs ├── Validate_GlossyLight.cs ├── Validate_MultiLight.cs ├── Validate_SingleBounce.cs ├── Validate_SingleBounceGlossy.cs ├── Validate_Textures.cs ├── ValidationSceneFactory.cs ├── Validator.cs └── logo.ico ├── SeeSharp.sln ├── SeeSharp ├── Cameras │ ├── Camera.cs │ ├── CameraRaySample.cs │ ├── CameraResponseSample.cs │ ├── LightProbeCamera.cs │ └── PerspectiveCamera.cs ├── Common │ ├── Atomic.cs │ ├── ConsoleWatchdog.cs │ ├── HtmlReport.cs │ ├── Logger.cs │ ├── MathUtils.cs │ ├── ProgressBar.cs │ ├── SanityChecks.cs │ └── TypeFactory.cs ├── Experiments │ ├── Benchmark.cs │ ├── BlenderImporter.cs │ ├── Experiment.cs │ ├── SceneConfig.cs │ ├── SceneFromFile.cs │ └── SceneRegistry.cs ├── Geometry │ ├── BoundingBox.cs │ ├── Mesh.cs │ ├── MeshFactory.cs │ ├── SurfacePoint.cs │ ├── SurfaceSample.cs │ └── Triangle.cs ├── IO │ ├── IMeshLoader.cs │ ├── JsonScene.cs │ ├── JsonUtils.cs │ ├── MeshLoadException.cs │ ├── MixReader.cs │ ├── ObjConverter.cs │ ├── ObjMesh.cs │ ├── PlyFile.cs │ ├── PlyLoader.cs │ └── TriMeshLoader.cs ├── Images │ ├── FrameBuffer.cs │ ├── ImageTexture.cs │ ├── Layer.cs │ ├── MonoLayer.cs │ ├── Pixel.cs │ ├── RgbLayer.cs │ ├── TextureMono.cs │ ├── TextureRgb.cs │ └── VarianceLayer.cs ├── Imports.cs ├── Integrators │ ├── Bidir │ │ ├── BidirBase.Camera.cs │ │ ├── BidirBase.Light.cs │ │ ├── BidirBase.cs │ │ ├── BidirPathPdfs.cs │ │ ├── CameraStoringVCM.cs │ │ ├── ClassicBidir.cs │ │ ├── LightPathCache.cs │ │ ├── PhotonMapper.cs │ │ ├── TechPyramid.cs │ │ ├── VertexCacheBidir.cs │ │ ├── VertexConnectionAndMerging.cs │ │ └── VertexSelector.cs │ ├── Common │ │ ├── PathBuffer.cs │ │ ├── PathCache.cs │ │ ├── PathVertex.cs │ │ └── RandomWalk.cs │ ├── DebugVisualizer.cs │ ├── Integrator.cs │ ├── PathTracer.cs │ └── Util │ │ ├── DenoiseBuffers.cs │ │ ├── OutlierReplayCache.cs │ │ ├── PathGraph.cs │ │ ├── PathGraphRenderer.cs │ │ └── RenderTimer.cs ├── Sampling │ ├── ISampler.cs │ ├── PiecewiseConstant.cs │ ├── RNG.cs │ ├── RegularGrid2d.cs │ ├── RegularGrid3d.cs │ └── SampleWarp.cs ├── Scene.cs ├── SeeSharp.csproj ├── Shading │ ├── Background │ │ ├── Background.cs │ │ ├── BackgroundSample.cs │ │ └── EnvironmentMap.cs │ ├── Emitters │ │ ├── DiffuseEmitter.cs │ │ ├── Emitter.cs │ │ ├── EmitterSample.cs │ │ └── GlossyEmitter.cs │ ├── Fresnel.cs │ ├── Materials │ │ ├── BsdfSample.cs │ │ ├── DiffuseMaterial.cs │ │ ├── GenericMaterial.cs │ │ ├── Material.cs │ │ ├── ShadingContext.cs │ │ └── SurfaceShader.cs │ ├── ShadingSpace.cs │ ├── ShadingStats.cs │ └── TrowbridgeReitzDistribution.cs └── extension.dib ├── build_blender.bat ├── build_blender.sh ├── logo.ico ├── logo.png ├── nuget.config ├── omnisharp.json └── see_blender ├── __init__.py ├── exporter.py ├── material.py ├── material_ui.py ├── ply.py ├── render_engine.py └── world.py /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ macos-latest, ubuntu-latest, windows-latest ] 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | 15 | - name: Setup .NET SDK 16 | uses: actions/setup-dotnet@v4 17 | with: 18 | dotnet-version: '9.0.x' 19 | 20 | - name: Build 21 | working-directory: ${{github.workspace}} 22 | run: dotnet build 23 | 24 | - name: Test 25 | working-directory: ${{github.workspace}} 26 | run: dotnet test -l "console;verbosity=normal" --no-build --blame-hang 27 | 28 | validate: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - uses: actions/checkout@v4 32 | 33 | - name: Setup .NET SDK 34 | uses: actions/setup-dotnet@v4 35 | with: 36 | dotnet-version: '9.0.x' 37 | 38 | - name: Run validation 39 | working-directory: ${{github.workspace}} 40 | run: dotnet run -c Release --project SeeSharp.Validation 41 | 42 | pack: 43 | runs-on: ubuntu-latest 44 | needs: [test, validate] 45 | 46 | steps: 47 | - uses: actions/checkout@v4 48 | 49 | - name: Setup .NET SDK 50 | uses: actions/setup-dotnet@v4 51 | with: 52 | dotnet-version: '9.0.x' 53 | 54 | - name: Pack 55 | run: dotnet pack -c Release 56 | 57 | - name: Upload SeeSharp.nupkg 58 | uses: actions/upload-artifact@v4 59 | with: 60 | path: ${{github.workspace}}/SeeSharp/bin/**/*.nupkg 61 | 62 | - name: Publish SeeSharp on version change 63 | uses: alirezanet/publish-nuget@v3.1.0 64 | with: 65 | PROJECT_FILE_PATH: SeeSharp/SeeSharp.csproj 66 | NUGET_KEY: ${{secrets.NUGET_API_KEY}} 67 | TAG_COMMIT: false 68 | 69 | - name: Upload SeeSharp.Templates.nupkg 70 | uses: actions/upload-artifact@v4 71 | with: 72 | path: ${{github.workspace}}/SeeSharp.Templates/bin/**/*.nupkg 73 | name: template 74 | 75 | - name: Publish SeeSharp.Templates on version change 76 | uses: alirezanet/publish-nuget@v3.1.0 77 | with: 78 | PROJECT_FILE_PATH: SeeSharp.Templates/SeeSharp.Templates.csproj 79 | NUGET_KEY: ${{secrets.NUGET_API_KEY}} 80 | TAG_COMMIT: false 81 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Setup .NET SDK 15 | uses: actions/setup-dotnet@v4 16 | with: 17 | dotnet-version: '9.0.x' 18 | 19 | - name: Test 20 | working-directory: ${{github.workspace}} 21 | run: dotnet test 22 | 23 | - name: Publish 24 | run: dotnet publish ${{github.workspace}}/SeeSharp.PreviewRender -c Release -o ${{github.workspace}}/see_blender/bin 25 | 26 | - name: Zip 27 | working-directory: ${{github.workspace}} 28 | run: zip -r see_blender see_blender 29 | 30 | - name: Create Release 31 | id: create_release 32 | uses: actions/create-release@v1 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | with: 36 | tag_name: ${{ github.ref }} 37 | release_name: Release ${{ github.ref }} 38 | draft: false 39 | prerelease: false 40 | 41 | - name: Upload Blender Addon 42 | uses: actions/upload-release-asset@v1 43 | env: 44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 45 | with: 46 | upload_url: ${{ steps.create_release.outputs.upload_url }} 47 | asset_path: ./see_blender.zip 48 | asset_name: see_blender.zip 49 | asset_content_type: application/zip 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User specific editor and IDE configurations 2 | .vs 3 | launchSettings.json 4 | !SeeSharp.Templates/content/SeeSharp.Blazor.Template/Properties/launchSettings.json 5 | .idea 6 | SeeSharp.sln.DotSettings.user 7 | 8 | # Build files generated by Visual Studio 9 | out 10 | bin 11 | obj 12 | /CMakeSettings.json 13 | 14 | # F# caches and other temporary files 15 | *.fsx.lock 16 | .fake 17 | .ionide 18 | 19 | # CMake generated files 20 | build 21 | dist/* 22 | 23 | # Python temporary and build files 24 | __pycache__ 25 | 26 | # .NET core generated files 27 | src/examples/*/bin 28 | src/examples/*/obj 29 | 30 | # intermediate files generated by our utility scripts 31 | output.json 32 | see_blender.zip 33 | 34 | # Blender backup file 35 | *.blend1 36 | 37 | # Doxygen output 38 | docs/* 39 | !docs/doxyfile 40 | 41 | # Generated images of the tests and examples 42 | /SeeSharp.Validation/Results 43 | /SeeSharp.Benchmark/Results 44 | /SeeSharp.Benchmark/*.json 45 | /SeeSharp.Benchmark/*.exr 46 | /SeeSharp.Templates/content/SeeSharp.Template/Results 47 | /SeeSharp.Example/Results 48 | /Results 49 | 50 | # Profiler output 51 | *.nettrace 52 | *.speedscope.json 53 | *ultra*pid*.json.gz -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.rulers": [ 3 | 110 4 | ], 5 | "dotnet.defaultSolution": "SeeSharp.sln" 6 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "/property:GenerateFullPaths=true", 11 | "/consoleloggerparameters:NoSummary" 12 | ], 13 | "problemMatcher": "$msCompile", 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | }, 18 | "presentation": { 19 | "reveal": "silent" 20 | } 21 | }, 22 | { 23 | "label": "publish", 24 | "command": "dotnet", 25 | "type": "process", 26 | "args": [ 27 | "publish", 28 | "${workspaceFolder}/src/SeeSharp/Validation/Validation.csproj", 29 | "/property:GenerateFullPaths=true", 30 | "/consoleloggerparameters:NoSummary" 31 | ], 32 | "problemMatcher": "$msCompile" 33 | }, 34 | { 35 | "label": "watch", 36 | "command": "dotnet", 37 | "type": "process", 38 | "args": [ 39 | "watch", 40 | "run", 41 | "${workspaceFolder}/src/SeeSharp/Validation/Validation.csproj", 42 | "/property:GenerateFullPaths=true", 43 | "/consoleloggerparameters:NoSummary" 44 | ], 45 | "problemMatcher": "$msCompile" 46 | } 47 | ] 48 | } -------------------------------------------------------------------------------- /Data/Scenes/CornellBox/References/Config.json: -------------------------------------------------------------------------------- 1 | { "Name": "PathTracer", "Settings": { 2 | "BaseSeed": 571298512, 3 | "TotalSpp": 512, 4 | "MaxDepth": 5, 5 | "MinDepth": 1, 6 | "NumShadowRays": 1, 7 | "EnableBsdfDI": true, 8 | "RenderTechniquePyramid": false 9 | }} -------------------------------------------------------------------------------- /Data/Scenes/CornellBox/References/MaxDepth2-Width512-Height512.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/Data/Scenes/CornellBox/References/MaxDepth2-Width512-Height512.exr -------------------------------------------------------------------------------- /Data/Scenes/CornellBox/References/MaxDepth2-Width512-Height512.json: -------------------------------------------------------------------------------- 1 | { 2 | "RenderTime": 26768 3 | } -------------------------------------------------------------------------------- /Data/Scenes/CornellBox/References/MaxDepth5-Width512-Height512.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/Data/Scenes/CornellBox/References/MaxDepth5-Width512-Height512.exr -------------------------------------------------------------------------------- /Data/Scenes/CornellBox/References/MaxDepth5-Width512-Height512.json: -------------------------------------------------------------------------------- 1 | { 2 | "RenderTime": 142532 3 | } -------------------------------------------------------------------------------- /Data/Scenes/ExportTest/ExportTest.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/Data/Scenes/ExportTest/ExportTest.blend -------------------------------------------------------------------------------- /Data/Scenes/TextureTest/TextureTest.json: -------------------------------------------------------------------------------- 1 | { 2 | "materials": [ 3 | { 4 | "type": "diffuse", 5 | "baseColor": { 6 | "type": "rgb", 7 | "value": [ 8 | 0.800000011920929, 9 | 0.800000011920929, 10 | 0.800000011920929 11 | ] 12 | }, 13 | "name": "Dots_Stroke" 14 | }, 15 | { 16 | "type": "diffuse", 17 | "baseColor": { 18 | "type": "rgb", 19 | "value": [ 20 | 0, 21 | 0, 22 | 0 23 | ] 24 | }, 25 | "emission": { 26 | "type": "rgb", 27 | "value": [ 28 | 10.0, 29 | 10.0, 30 | 10.0 31 | ] 32 | }, 33 | "name": "Material.001" 34 | }, 35 | { 36 | "type": "diffuse", 37 | "baseColor": { 38 | "type": "image", 39 | "filename": "Textures/TestPattern.png" 40 | }, 41 | "name": "Material.002" 42 | } 43 | ], 44 | "transforms": [ 45 | { 46 | "name": "camera", 47 | "position": [ 48 | 0.05385369062423706, 49 | 5.471238613128662, 50 | -0.059679657220840454 51 | ], 52 | "rotation": [ 53 | -89.45677456474138, 54 | 179.8844821311455, 55 | 0.4280197540739464 56 | ], 57 | "scale": [ 58 | 1.0, 59 | 1.0, 60 | 1.0 61 | ] 62 | } 63 | ], 64 | "cameras": [ 65 | { 66 | "fov": 22.895194130645738, 67 | "transform": "camera", 68 | "name": "default", 69 | "type": "perspective" 70 | } 71 | ], 72 | "objects": [ 73 | { 74 | "name": "scene", 75 | "type": "obj", 76 | "relativePath": "TextureTest.obj" 77 | } 78 | ] 79 | } -------------------------------------------------------------------------------- /Data/Scenes/TextureTest/TextureTest.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.92.0 OBJ File: 'TextureTest.blend' 2 | # www.blender.org 3 | mtllib TextureTest.mtl 4 | o Plane 5 | v 2.082877 0.000000 -1.000000 6 | v 0.082877 0.000000 -1.000000 7 | v 2.082877 0.000000 1.000000 8 | v 0.082877 0.000000 1.000000 9 | vt 0.999900 0.999900 10 | vt 0.000100 0.999900 11 | vt 0.000100 0.000100 12 | vt 0.999900 0.000100 13 | vn 0.0000 1.0000 0.0000 14 | g Plane_Plane_Material.002 15 | usemtl Material.002 16 | s off 17 | f 1/1/1 2/2/1 4/3/1 3/4/1 18 | o Plane.001 19 | v 0.842612 6.370134 0.842612 20 | v -0.842612 6.370134 0.842612 21 | v 0.842612 6.370133 -0.842612 22 | v -0.842612 6.370133 -0.842612 23 | vt 0.000000 0.000000 24 | vt 1.000000 0.000000 25 | vt 1.000000 1.000000 26 | vt 0.000000 1.000000 27 | vn 0.0000 -1.0000 0.0000 28 | g Plane.001_Plane.001_Material.001 29 | usemtl Material.001 30 | s off 31 | f 5/5/2 6/6/2 8/7/2 7/8/2 32 | o Plane.002 33 | v 0.003669 0.000000 -1.000000 34 | v -1.996331 0.000000 -1.000000 35 | v 0.003669 0.000000 1.000000 36 | v -1.996331 0.000000 1.000000 37 | vt 4.000190 4.006390 38 | vt 0.000991 4.006391 39 | vt 0.000990 0.007191 40 | vt 4.000189 0.007191 41 | vn 0.0000 1.0000 0.0000 42 | g Plane.002_Plane.002_Material.002 43 | usemtl Material.002 44 | s off 45 | f 9/9/3 10/10/3 12/11/3 11/12/3 46 | -------------------------------------------------------------------------------- /Data/Scenes/TextureTest/Textures/TestPattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/Data/Scenes/TextureTest/Textures/TestPattern.png -------------------------------------------------------------------------------- /Data/Scenes/simplebackground.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Simple background", 3 | "transforms": [ 4 | { 5 | "name": "camera", 6 | "position": [ 0, 1.0, 0 ], 7 | "rotation": [ -90, 0, 0 ], 8 | "scale": [ 1, 1, 1 ] 9 | } 10 | ], 11 | "cameras": [ 12 | { 13 | "name": "default", 14 | "type": "perspective", 15 | "fov": 19.5, 16 | "transform": "camera" 17 | } 18 | ], 19 | "background": { 20 | "type": "image", 21 | "filename": "sunsky.exr" 22 | }, 23 | "materials": [ 24 | { 25 | "name": "Quad", 26 | "baseColor": { 27 | "type": "rgb", 28 | "value": [1, 1, 1] 29 | } 30 | }, 31 | { 32 | "name": "Light", 33 | "baseColor": { 34 | "type": "rgb", 35 | "value": [ 0, 0, 0 ] 36 | } 37 | } 38 | ], 39 | "objects": [ 40 | { 41 | "name": "quad", 42 | "material": "Quad", 43 | "type": "trimesh", 44 | "indices": [ 45 | 0, 2, 1, 46 | 0, 3, 2 47 | ], 48 | "vertices": [ 49 | -10.0, 0.0, -10.0, 50 | 10.0, 0.0, -10.0, 51 | 10.0, 0.0, 10.0, 52 | -10.0, 0.0, 10.0 53 | ], 54 | "normals": [ 55 | 0, -1, 0, 56 | 0, -1, 0, 57 | 0, -1, 0, 58 | 0, -1, 0 59 | ], 60 | "uv": [ 61 | 0.0, 0.0, 62 | 1.0, 0.0, 63 | 1.0, 1.0, 64 | 0.0, 1.0 65 | ] 66 | } 67 | ] 68 | } -------------------------------------------------------------------------------- /Data/Scenes/simpledi.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Simple direct illumination", 3 | "transforms": [ 4 | { 5 | "name": "camera", 6 | "position": [ 0, 1.0, 0 ], 7 | "rotation": [ -90, 0, 0 ], 8 | "scale": [ 1, 1, 1 ] 9 | } 10 | ], 11 | "cameras": [ 12 | { 13 | "name": "default", 14 | "type": "perspective", 15 | "fov": 19.5, 16 | "transform": "camera" 17 | } 18 | ], 19 | "materials": [ 20 | { 21 | "name": "Quad", 22 | "baseColor": { 23 | "type": "rgb", 24 | "value": [1, 1, 1] 25 | } 26 | }, 27 | { 28 | "name": "Light", 29 | "baseColor": { 30 | "type": "rgb", 31 | "value": [ 0, 0, 0 ] 32 | } 33 | } 34 | ], 35 | "objects": [ 36 | { 37 | "name": "light", 38 | "emission": { 39 | "type": "rgb", 40 | "unit": "radiance", 41 | "value": [20.0, 20.0, 20.0] 42 | }, 43 | "material": "Light", 44 | "type": "trimesh", 45 | "indices": [ 46 | 0, 1, 2, 47 | 0, 2, 3 48 | ], 49 | "vertices": [ 50 | -0.1, 2.0, -0.1, 51 | 0.1, 2.0, -0.1, 52 | 0.1, 2.0, 0.1, 53 | -0.1, 2.0, 0.1 54 | ], 55 | "normals": [ 56 | 0, -1, 0, 57 | 0, -1, 0, 58 | 0, -1, 0, 59 | 0, -1, 0 60 | ], 61 | "uv": [ 62 | 0.0, 0.0, 63 | 1.0, 0.0, 64 | 1.0, 1.0, 65 | 0.0, 1.0 66 | ] 67 | }, 68 | { 69 | "name": "quad", 70 | "material": "Quad", 71 | "type": "trimesh", 72 | "indices": [ 73 | 0, 2, 1, 74 | 0, 3, 2 75 | ], 76 | "vertices": [ 77 | -10.0, 0.0, -10.0, 78 | 10.0, 0.0, -10.0, 79 | 10.0, 0.0, 10.0, 80 | -10.0, 0.0, 10.0 81 | ], 82 | "normals": [ 83 | 0, -1, 0, 84 | 0, -1, 0, 85 | 0, -1, 0, 86 | 0, -1, 0 87 | ], 88 | "uv": [ 89 | 0.0, 0.0, 90 | 1.0, 0.0, 91 | 1.0, 1.0, 92 | 0.0, 1.0 93 | ] 94 | } 95 | ] 96 | } -------------------------------------------------------------------------------- /Data/Scenes/sunsky.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/Data/Scenes/sunsky.exr -------------------------------------------------------------------------------- /Data/Scenes/uniform.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/Data/Scenes/uniform.exr -------------------------------------------------------------------------------- /ExampleFigure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgrit/SeeSharp/dbdeafa9f65627f6c6f5149e5e335a92f1e99a0c/ExampleFigure.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Pascal Grittmann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MaterialTest/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

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 |

Example experiment

10 | 11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 |

25 | @theta 26 | @phi 27 |

28 | 29 | 30 | 31 |
32 | 33 | 34 |

35 | @* @thetaOut 36 | @phiOut *@ 37 | 38 | @if(select != null) { 39 |
40 | @($"{select[0]:G2} {select[1]:G2} {select[2]:G2} ") 41 | } 42 | 43 | @* @if(totals != null) { 44 |
45 | @($"{totals[0]:G2} {totals[1]:G2} {totals[2]:G2} ") 46 | } *@ 47 |

48 |
49 | 50 |
51 | 52 | @code { 53 | bool resultsAvailable = false; 54 | 55 | float theta { 56 | get => field; 57 | set { 58 | field = value; 59 | RunExperiment(); 60 | } 61 | } 62 | float phi { 63 | get => field; 64 | set { 65 | field = value; 66 | RunExperiment(); 67 | } 68 | } 69 | 70 | float thetaOut {get;set;} 71 | float phiOut {get;set;} 72 | 73 | SimpleImageIO.FlipBook flip, fliprender; 74 | } -------------------------------------------------------------------------------- /MaterialTest/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | @using System.Reflection 4 | @using System.Text.RegularExpressions 5 | 6 | 7 |
8 | 16 |
17 | 18 | 19 | @code { 20 | /// Enumerates all .razor components in this folder 21 | public IEnumerable<(string Name, string Url)> GetExperimentPages() 22 | { 23 | var routableComponents = Assembly 24 | .GetExecutingAssembly() 25 | .ExportedTypes 26 | .Where(t => t.IsSubclassOf(typeof(ComponentBase))) 27 | .Where(c => c 28 | .GetCustomAttributes(inherit: true) 29 | .OfType() 30 | .Count() > 0); 31 | 32 | foreach (var routableComponent in routableComponents) 33 | { 34 | string name = routableComponent.ToString().Replace("MaterialTest.Pages.", string.Empty); 35 | if (name != "Index") 36 | yield return (name, name); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /MaterialTest/Pages/IntegratorTest.razor: -------------------------------------------------------------------------------- 1 | @using SeeSharp.Experiments 2 | @using SeeSharp 3 | @using SeeSharp.Blazor 4 | 5 | @inject IJSRuntime JS 6 | 7 | @page "/IntegratorTest" 8 | 9 | 10 | 11 |
12 |
13 | @if (readyToRun) 14 | { 15 |

16 | } 17 | 18 | 19 | 20 |
21 | 22 | @if (!running) 23 | { 24 | @if (resultsAvailable) 25 | { 26 |
27 | 28 | 29 | @if (selected.HasValue && selected.Value) 30 | { 31 | 32 | 33 | 34 | 35 | 36 |
Mesh@(selected.Value.Mesh.Name)
Material@(selected.Value.Mesh.Material.Name) (roughness: @(selected.Value.Mesh.Material.GetRoughness(selected.Value)), transmissive: @(selected.Value.Mesh.Material.IsTransmissive(selected.Value)))
Distance@(selected.Value.Distance)
Position@(selected.Value.Position)
37 | } 38 |
39 | } 40 | } 41 | else 42 | { 43 |

Rendering...

44 | } 45 |
46 | 47 | @code { 48 | SceneSelector sceneSelector; 49 | Scene scene; 50 | bool readyToRun = false; 51 | bool running = false; 52 | bool sceneJustLoaded = false; 53 | bool resultsAvailable = false; 54 | ElementReference runButton; 55 | 56 | SimpleImageIO.FlipBook flip; 57 | 58 | async Task OnSceneLoaded(SceneFromFile sceneFromFile) 59 | { 60 | await Task.Run(() => scene = sceneFromFile.MakeScene()); 61 | flip = null; 62 | resultsAvailable = false; 63 | readyToRun = true; 64 | sceneJustLoaded = true; 65 | } 66 | 67 | protected override async Task OnAfterRenderAsync(bool firstRender) 68 | { 69 | if (readyToRun && sceneJustLoaded) 70 | { 71 | await runButton.FocusAsync(); 72 | } 73 | 74 | sceneJustLoaded = false; 75 | } 76 | 77 | async Task OnRunClick() 78 | { 79 | readyToRun = false; 80 | resultsAvailable = false; 81 | running = true; 82 | await Task.Run(() => RunExperiment()); 83 | readyToRun = true; 84 | running = false; 85 | resultsAvailable = true; 86 | } 87 | } -------------------------------------------------------------------------------- /MaterialTest/Pages/IntegratorTest.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using SeeSharp.Blazor; 3 | 4 | namespace MaterialTest.Pages; 5 | 6 | public partial class IntegratorTest : ComponentBase 7 | { 8 | const int Width = 640; 9 | const int Height = 480; 10 | const int MaxDepth = 10; 11 | 12 | int NumSamples = 1; 13 | 14 | void RunExperiment() 15 | { 16 | flip = new FlipBook(660, 580) 17 | .SetZoom(FlipBook.InitialZoom.FillWidth) 18 | .SetToneMapper(FlipBook.InitialTMO.Exposure(scene.RecommendedExposure)) 19 | .SetToolVisibility(false); 20 | 21 | scene.FrameBuffer = new(Width, Height, null); 22 | scene.Prepare(); 23 | VertexConnectionAndMerging vcm = new() 24 | { 25 | NumIterations = NumSamples, 26 | MaxDepth = MaxDepth, 27 | RenderTechniquePyramid = true 28 | }; 29 | vcm.Render(scene); 30 | flip.Add($"VCM", scene.FrameBuffer.Image); 31 | 32 | flip.AddAll(vcm.TechPyramidRaw.GetImagesForPathLength(2)); 33 | } 34 | 35 | SurfacePoint? selected; 36 | 37 | void OnFlipClick(FlipViewer.OnClickEventArgs args) 38 | { 39 | if (args.CtrlKey) 40 | { 41 | RNG rng = new(1241512); 42 | var ray = scene.Camera.GenerateRay(new Vector2(args.X + 0.5f, args.Y + 0.5f), ref rng).Ray; 43 | selected = (SurfacePoint)scene.Raytracer.Trace(ray); 44 | 45 | SurfaceShader shader = new(selected.Value, -ray.Direction, false); 46 | var s = shader.Sample(rng.NextFloat(), rng.NextFloat2D()); 47 | Console.WriteLine(s); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /MaterialTest/Pages/_Host.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @using Microsoft.AspNetCore.Components.Web 3 | @namespace MaterialTest.Pages 4 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | @Html.Raw(SeeSharp.Blazor.Scripts.AllScripts) 16 | 17 | 18 | 19 | 20 | 21 |
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 | -------------------------------------------------------------------------------- /MaterialTest/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using Microsoft.AspNetCore.Components.Web; 3 | 4 | SceneRegistry.AddSourceRelativeToScript("../Data/Scenes"); 5 | 6 | var builder = WebApplication.CreateBuilder(args); 7 | builder.Services.AddRazorPages(); 8 | builder.Services.AddServerSideBlazor(); 9 | 10 | var app = builder.Build(); 11 | 12 | if (!app.Environment.IsDevelopment()) 13 | { 14 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 15 | app.UseHsts(); 16 | } 17 | 18 | app.UseHttpsRedirection(); 19 | 20 | app.UseStaticFiles(); 21 | 22 | app.UseRouting(); 23 | 24 | app.MapBlazorHub(); 25 | app.MapFallbackToPage("/_Host"); 26 | 27 | app.Run(); 28 | -------------------------------------------------------------------------------- /MaterialTest/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Routing 2 | @using Microsoft.AspNetCore.Components.Web 3 | @using Microsoft.JSInterop 4 | @using MaterialTest 5 | 6 | @using SeeSharp.Blazor -------------------------------------------------------------------------------- /MaterialTest/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DetailedErrors": true, 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft.AspNetCore": "Warning" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /MaterialTest/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /MaterialTest/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | #blazor-error-ui { 2 | background: lightyellow; 3 | bottom: 0; 4 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 5 | display: none; 6 | left: 0; 7 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 8 | position: fixed; 9 | width: 100%; 10 | z-index: 1000; 11 | } 12 | 13 | #blazor-error-ui .dismiss { 14 | cursor: pointer; 15 | position: absolute; 16 | right: 3.5rem; 17 | top: 0.5rem; 18 | } 19 | 20 | .blazor-error-boundary { 21 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 22 | padding: 1rem 1rem 1rem 3.7rem; 23 | color: white; 24 | } 25 | 26 | .blazor-error-boundary::after { 27 | content: "An error has occurred." 28 | } 29 | 30 | html { 31 | font-family: system-ui; 32 | } 33 | 34 | button { 35 | background-color: #a4e1f2; 36 | border-style: none; 37 | /* border-width: 2px; 38 | border-color: #245e6f; */ 39 | color: black; 40 | font-size: medium; 41 | padding-left: 8px; 42 | padding-right: 8px; 43 | padding-bottom: 4px; 44 | padding-top: 4px; 45 | } 46 | button:hover { 47 | background-color: #c9eff4; 48 | cursor: pointer; 49 | } 50 | button:disabled { 51 | background-color: #e5f1f5; 52 | color: #96b4bd; 53 | border-color: #96b4bd; 54 | } 55 | 56 | .experiment-settings { 57 | display: flex; 58 | gap: 0.25em; 59 | flex-direction: column; 60 | float: left; 61 | margin-right: 1em; 62 | } 63 | 64 | .experiment-results { 65 | display: flex; 66 | gap: 10px; 67 | flex-wrap: wrap; 68 | } -------------------------------------------------------------------------------- /SeeSharp.Benchmark/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SeeSharp.Benchmark; 3 | using SeeSharp.Experiments; 4 | using SeeSharp.Integrators; 5 | using SeeSharp.Integrators.Bidir; 6 | 7 | SceneRegistry.AddSourceRelativeToScript("../data/scenes"); 8 | 9 | BenchRender("PathTracer - 16spp", new PathTracer() { 10 | TotalSpp = 16, 11 | }); 12 | 13 | BenchRender("BDPT - 8spp", new VertexCacheBidir() { 14 | NumIterations = 8, 15 | }); 16 | 17 | BenchRender("VCM - 8spp", new VertexConnectionAndMerging() { 18 | NumIterations = 8, 19 | }); 20 | 21 | void BenchRender(string name, Integrator integrator) { 22 | var scene = 23 | // SceneRegistry.LoadScene("StillLife").MakeScene(); 24 | SceneRegistry.LoadScene("CornellBox").MakeScene(); 25 | 26 | // Dry run to eliminate JIT overhead 27 | scene.FrameBuffer = new(512, 512, ""); 28 | scene.Prepare(); 29 | integrator.Render(scene); 30 | 31 | int num = 2; 32 | long total = 0; 33 | for (int i = 0; i < num; ++i) { 34 | scene.FrameBuffer = new(512, 512, ""); 35 | integrator.Render(scene); 36 | total += scene.FrameBuffer.RenderTimeMs; 37 | } 38 | Console.WriteLine($"{name}: {total / (double)num}"); 39 | 40 | scene.FrameBuffer.WriteToFile(name + ".exr"); 41 | } 42 | 43 | GenericMaterial_Sampling.QuickTest(); 44 | 45 | Console.WriteLine("Warmup run"); 46 | GenericMaterial_Sampling.BenchPerformance(100000); 47 | GenericMaterial_Sampling.BenchPerformanceComponentPdfs(100000); 48 | Console.WriteLine("======================="); 49 | GenericMaterial_Sampling.BenchPerformance(500000); 50 | GenericMaterial_Sampling.BenchPerformanceComponentPdfs(500000); 51 | 52 | GenericMaterial_Sampling.Benchmark(); 53 | 54 | VectorBench.BenchComputeBasisVectors(10000000); 55 | -------------------------------------------------------------------------------- /SeeSharp.Benchmark/RunBench.fsx: -------------------------------------------------------------------------------- 1 | open System.Diagnostics 2 | open System 3 | 4 | // Waits for the process to finish, if the return code is not zero, prints a message and terminates 5 | let WaitAndCheck (proc : Process) = 6 | proc.WaitForExit() 7 | if proc.ExitCode <> 0 then 8 | Console.WriteLine("Error: process failed") 9 | exit(-1) 10 | 11 | if not Environment.Is64BitOperatingSystem then 12 | Console.WriteLine("Error: only 64 bit OS supported") 13 | exit -1 14 | 15 | let baseName = IO.DirectoryInfo(Environment.CurrentDirectory).Name 16 | let (rid, exeName) = 17 | if OperatingSystem.IsLinux() then "linux-x64", baseName 18 | elif OperatingSystem.IsWindows() then "win-x64", baseName + ".exe" 19 | else "osx-x64", baseName 20 | 21 | // Publish a binary with AOT compilation, so there is little to no JIT overhead polluting the benchmark 22 | Process.Start("dotnet", 23 | $"publish -c Release -r {rid} --no-self-contained -p:PublishReadyToRun=true -o bin/Benchmark") 24 | |> WaitAndCheck 25 | 26 | // Check if we should attach a profiler 27 | if fsi.CommandLineArgs.Length = 1 then 28 | Process.Start($"bin/Benchmark/{exeName}") |> WaitAndCheck 29 | else 30 | if "--profile" <> fsi.CommandLineArgs.[1] then 31 | Console.WriteLine($"Error: Unknown argument '{fsi.CommandLineArgs.[1]}'") 32 | exit(-1) 33 | 34 | // Make sure dotnet-trace is available as a global tool 35 | Process.Start("dotnet", "tool install --global dotnet-trace") 36 | .WaitForExit() 37 | Process.Start("dotnet-trace", $"collect --format speedscope -- bin/Benchmark/{exeName}") 38 | |> WaitAndCheck 39 | -------------------------------------------------------------------------------- /SeeSharp.Benchmark/SeeSharp.Benchmark.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | net9.0 10 | false 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /SeeSharp.Benchmark/VectorBench.cs: -------------------------------------------------------------------------------- 1 | using static SeeSharp.Shading.ShadingSpace; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Numerics; 5 | 6 | namespace SeeSharp.Benchmark { 7 | public class VectorBench { 8 | public static void BenchComputeBasisVectors(int numTrials) { 9 | Random rng = new(1337); 10 | Vector3 NextVector() => new ( 11 | (float) rng.NextDouble(), 12 | (float) rng.NextDouble(), 13 | (float) rng.NextDouble()); 14 | 15 | Vector3 avg = Vector3.Zero; 16 | 17 | Stopwatch stop = Stopwatch.StartNew(); 18 | for (int i = 0; i < numTrials; ++i) { 19 | Vector3 tan, binorm; 20 | ComputeBasisVectors(NextVector(), out tan, out binorm); 21 | avg += (tan + binorm) / numTrials * 0.5f; 22 | } 23 | Console.WriteLine($"Computing {numTrials} basis vectors took {stop.ElapsedMilliseconds}ms - {avg.Length()}"); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /SeeSharp.Blazor/AutocompleteInput.razor: -------------------------------------------------------------------------------- 1 | @using System.Text 2 | @namespace SeeSharp.Blazor 3 | 4 |
5 |
6 |

7 |
8 | 9 | @if (autocompleteList != null) 10 | { 11 | @foreach (string str in autocompleteList) 12 | { 13 | 14 | } 15 | } 16 | 17 |
18 | 19 | @code { 20 | [Parameter] public IEnumerable Candidates { get; set; } 21 | 22 | [Parameter] 23 | public EventCallback OnTextChanged { get; set; } 24 | 25 | [Parameter] 26 | public EventCallback OnSubmit { get; set; } 27 | 28 | [Parameter] 29 | public string Label { get; set; } 30 | 31 | IEnumerable autocompleteList; 32 | 33 | bool valid; 34 | 35 | public bool Valid => valid; 36 | 37 | public string Text 38 | { 39 | get => text; 40 | set 41 | { 42 | text = value; 43 | autocompleteList = Candidates.Where(str => str.StartsWith(value)); 44 | 45 | valid = autocompleteList.Contains(text); 46 | 47 | OnTextChanged.InvokeAsync(text); 48 | } 49 | } 50 | string text = ""; 51 | } -------------------------------------------------------------------------------- /SeeSharp.Blazor/AutocompleteInput.razor.css: -------------------------------------------------------------------------------- 1 | .invalid { 2 | border-width: 2px; 3 | border-color: red; 4 | border-style: solid; 5 | } 6 | 7 | p:has(.invalid)::after { 8 | content: " scene not found"; 9 | color: red; 10 | } 11 | 12 | input { 13 | font-size: medium; 14 | margin-left: 8px; 15 | } -------------------------------------------------------------------------------- /SeeSharp.Blazor/BoolSetting.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Mvc 2 | 3 | @namespace SeeSharp.Blazor 4 | 5 | @inherits SettingBase 6 | 7 | @{ 8 | base.BuildRenderTree(__builder); 9 | } 10 | 11 | @code { 12 | protected override string Type => "checkbox"; 13 | 14 | protected override bool ParseValue(ChangeEventArgs e) 15 | { 16 | return (bool)e.Value; 17 | } 18 | 19 | protected override Dictionary CustomAttributes => new() { 20 | { "checked", Value } 21 | }; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /SeeSharp.Blazor/FlipViewer.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.JSInterop 2 | @using System.Text.Json.Serialization 3 | @inject IJSRuntime JSRuntime 4 | 5 | @namespace SeeSharp.Blazor 6 | 7 | @if (flipCode != null) 8 | @((MarkupString)flipCode) 9 | 10 | @code { 11 | string flipCode; 12 | string flipJson; 13 | 14 | [Parameter] 15 | public SimpleImageIO.FlipBook Flip { get; set; } 16 | SimpleImageIO.FlipBook lastFlip; 17 | 18 | public record struct OnClickEventArgs 19 | ( 20 | int X, 21 | int Y, 22 | bool CtrlKey 23 | ) 24 | { 25 | } 26 | 27 | [Parameter] 28 | public EventCallback OnClick { get; set; } 29 | 30 | protected override async Task OnParametersSetAsync() 31 | { 32 | if (Flip == null) 33 | { 34 | flipCode = null; 35 | lastFlip = null; 36 | return; 37 | } 38 | 39 | if (lastFlip == Flip) return; 40 | 41 | await Task.Run(() => { 42 | if (Flip.Count == 0) { 43 | flipJson = null; 44 | flipCode = "

empty flip book

"; 45 | return; 46 | } 47 | var code = Flip.Generate(); 48 | flipCode = code.Html; 49 | flipJson = code.Data; 50 | }); 51 | 52 | lastFlip = Flip; 53 | } 54 | 55 | public struct _OnFlipClickArgs 56 | { 57 | [JsonInclude] public bool ctrlKey; 58 | } 59 | 60 | [JSInvokable] 61 | public void _OnFlipClick(int x, int y, _OnFlipClickArgs eventArgs) 62 | { 63 | OnClick.InvokeAsync(new(x, y, eventArgs.ctrlKey)).Wait(); 64 | } 65 | 66 | protected override async Task OnAfterRenderAsync(bool firstRender) 67 | { 68 | // Need to wait with invoking the JS code until the HTML got added to the DOM on the client side 69 | if (flipJson != null) 70 | { 71 | await JSRuntime.InvokeVoidAsync("makeFlipBook", flipJson, DotNetObjectReference.Create(this), nameof(_OnFlipClick)); 72 | flipJson = null; 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /SeeSharp.Blazor/FloatSetting.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Mvc 2 | 3 | @namespace SeeSharp.Blazor 4 | 5 | @inherits SettingBase 6 | 7 | @{ 8 | base.BuildRenderTree(__builder); 9 | } 10 | 11 | @code { 12 | protected override string Type => "number"; 13 | 14 | [Parameter] public float? Step { get; set; } = null; 15 | 16 | protected override float ParseValue(ChangeEventArgs e) 17 | { 18 | if (float.TryParse((string)e.Value, out float result)) 19 | return result; 20 | return Value; 21 | } 22 | 23 | protected override Dictionary CustomAttributes => new() { 24 | { "step", Step?.ToString() ?? "any" } 25 | }; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /SeeSharp.Blazor/HtmlProgressBar.razor: -------------------------------------------------------------------------------- 1 | @namespace SeeSharp.Blazor 2 | 3 |
14 | 15 | @if(progressBar != null && progressBar.NumWorkDone < progressBar.TotalWork && FunStuff != null) { 16 | 22 | } 23 |

24 | @if (progressBar != null && progressBar.TotalWork != 0) { 25 | @if(progressBar.NumWorkDone < progressBar.TotalWork) { 26 | 32 | } else { 33 | @(progressBar.Label) 34 | @: done after 35 | @($"{progressBar.TimeElapsedSeconds:f2}s") 36 | ( 37 | @progressBar.NumWorkDone 38 | / 39 | @progressBar.TotalWork 40 | ) 41 | } 42 | } else { 43 | @:Idle 44 | } 45 |

46 |
47 | 48 | @code { 49 | SeeSharp.Common.ProgressBar progressBar; 50 | System.Random rng = new(); 51 | 52 | [Parameter] 53 | public string[] FunStuff { get; set; } 54 | 55 | void Sync(SeeSharp.Common.ProgressBar progressBar) { 56 | this.progressBar = progressBar; 57 | InvokeAsync(StateHasChanged); 58 | } 59 | 60 | protected override void OnInitialized() { 61 | SeeSharp.Common.ProgressBar.OnUpdate += Sync; 62 | } 63 | } -------------------------------------------------------------------------------- /SeeSharp.Blazor/IntSetting.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Mvc 2 | 3 | @namespace SeeSharp.Blazor 4 | 5 | @inherits SettingBase 6 | 7 | @{ 8 | base.BuildRenderTree(__builder); 9 | } 10 | 11 | @code { 12 | protected override string Type => "number"; 13 | 14 | protected override int ParseValue(ChangeEventArgs e) 15 | { 16 | if (int.TryParse((string)e.Value, out int result)) 17 | return result; 18 | return Value; 19 | } 20 | 21 | protected override Dictionary CustomAttributes => new() { 22 | { "step", 1 } 23 | }; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /SeeSharp.Blazor/LogOutput.razor.css: -------------------------------------------------------------------------------- 1 | .header { 2 | background: #8ecbdc; 3 | border-top-style: none; 4 | border-bottom-style: solid; 5 | border-bottom-width: 1pt; 6 | margin: 0; 7 | padding-bottom: 0.2em; 8 | text-align: center; 9 | } 10 | 11 | .error { 12 | background-color: rgb(211, 86, 86); 13 | color: rgb(69, 4, 4); 14 | } 15 | 16 | .warning { 17 | background-color: rgb(244, 210, 75); 18 | color: rgb(57, 38, 7); 19 | } 20 | 21 | .debug { 22 | background-color: rgb(196, 196, 196); 23 | } 24 | 25 | .error>.msg::before { 26 | content: "[Error] " 27 | } 28 | 29 | .warning>.msg::before { 30 | content: "[Warning] " 31 | } 32 | 33 | .debug>.msg::before { 34 | content: "[Debug] " 35 | } 36 | 37 | .list { 38 | overflow: scroll; 39 | resize: both; 40 | background: #e6f1f4; 41 | border-style: solid; 42 | border-width: 1pt; 43 | border-color: #2b3f44; 44 | 45 | width: 500px; 46 | min-width: 100px; 47 | height: 125px; 48 | min-height: 50px; 49 | 50 | display: flex; 51 | flex-direction: column; 52 | } 53 | 54 | .container { 55 | overflow: scroll; 56 | flex-grow: 1; 57 | } 58 | 59 | td:nth-child(1) { 60 | padding-right: 2em; 61 | } -------------------------------------------------------------------------------- /SeeSharp.Blazor/Readme.md: -------------------------------------------------------------------------------- 1 | Reusable Razor components for SeeSharp. 2 | 3 | The following must be added to the `` part of _Host.cshtml for the FlipBook integration to work: 4 | ```html 5 | @Html.Raw(SeeSharp.Blazor.Scripts.FlipBookScript) 6 | ``` -------------------------------------------------------------------------------- /SeeSharp.Blazor/RotationInput.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Mvc 2 | @using Microsoft.JSInterop 3 | 4 | @namespace SeeSharp.Blazor 5 | 6 | @inject IJSRuntime JS 7 | 8 | 9 | 10 | @code { 11 | [Parameter] 12 | public int Size { get; set; } = 100; 13 | 14 | public enum Mode { 15 | FullCircle, 16 | HalfCircle, 17 | QuarterCircle 18 | } 19 | 20 | [Parameter] 21 | public Mode RotationMode { get; set; } = Mode.FullCircle; 22 | 23 | ElementReference canvas; 24 | 25 | [Parameter] 26 | public float Value { get; set; } = 0.0f; 27 | [Parameter] public EventCallback ValueChanged { get; set; } 28 | 29 | protected override async Task OnAfterRenderAsync(bool firstRender) 30 | { 31 | if (!firstRender) 32 | return; 33 | 34 | switch (RotationMode) 35 | { 36 | case Mode.FullCircle: 37 | await JS.InvokeVoidAsync("initRotationInput", canvas, DotNetObjectReference.Create(this), Value); 38 | break; 39 | case Mode.HalfCircle: 40 | await JS.InvokeVoidAsync("initHalfRotationInput", canvas, DotNetObjectReference.Create(this), Value, false); 41 | break; 42 | case Mode.QuarterCircle: 43 | await JS.InvokeVoidAsync("initHalfRotationInput", canvas, DotNetObjectReference.Create(this), Value, true); 44 | break; 45 | } 46 | } 47 | 48 | [JSInvokable] 49 | public async Task OnValueChanged(float newAngle) 50 | { 51 | Value = newAngle; 52 | await ValueChanged.InvokeAsync(Value); 53 | } 54 | } -------------------------------------------------------------------------------- /SeeSharp.Blazor/SceneSelector.razor.css: -------------------------------------------------------------------------------- 1 | ::deep .scene-button { 2 | margin-top: 0px; 3 | margin-bottom: 2px; 4 | background-color: #cfe8ef; 5 | color: rgb(12, 12, 12); 6 | padding-left: 4px; 7 | padding-top: 2px; 8 | padding-bottom: 2px; 9 | overflow: hidden; 10 | } 11 | 12 | ::deep .scene-button:hover { 13 | cursor: pointer; 14 | background-color: #8edeee; 15 | } 16 | 17 | .dropdown-header { 18 | font-weight: bold; 19 | } 20 | .dropdown-header:hover { 21 | cursor: pointer; 22 | } 23 | 24 | .dropdown-body { 25 | column-count: auto; 26 | column-width: 12rem; 27 | margin-left: 8px; 28 | border-left-width: 8px; 29 | border-left-color: #208dab; 30 | border-left-style: solid; 31 | background-color: #cfe8ef; 32 | } 33 | 34 | .scene-picker { 35 | background-color: #e6f1f4; 36 | padding: 8px; 37 | 38 | border-style: solid; 39 | border-width: 1pt; 40 | border-color: #2b3f44; 41 | } 42 | 43 | .btn { 44 | background-color: #a4e1f2; 45 | border-style: none; 46 | /* border-width: 2px; 47 | border-color: #245e6f; */ 48 | color: black; 49 | font-size: medium; 50 | padding-left: 8px; 51 | padding-right: 8px; 52 | padding-bottom: 4px; 53 | padding-top: 4px; 54 | } 55 | .btn:hover { 56 | background-color: #c9eff4; 57 | cursor: pointer; 58 | } 59 | .btn:disabled { 60 | background-color: #e5f1f5; 61 | color: #96b4bd; 62 | border-color: #96b4bd; 63 | } -------------------------------------------------------------------------------- /SeeSharp.Blazor/SeeSharp.Blazor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | PreserveNewest 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /SeeSharp.Blazor/SettingBase.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Mvc 2 | 3 | @namespace SeeSharp.Blazor 4 | 5 | @typeparam T 6 | 7 | 10 | 11 | @code { 12 | [Parameter] public string HoverText { get; set; } 13 | [Parameter] public string Label { get; set; } 14 | 15 | [Parameter] public T Value { get; set; } 16 | [Parameter] public EventCallback ValueChanged { get; set; } 17 | 18 | protected ElementReference Input; 19 | 20 | protected virtual string Type { get => "number"; } 21 | 22 | protected virtual Dictionary CustomAttributes { get => []; } 23 | 24 | protected virtual T ParseValue(ChangeEventArgs e) 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | 29 | private async Task OnValueChanged(ChangeEventArgs e) 30 | { 31 | Value = ParseValue(e); 32 | await ValueChanged.InvokeAsync(Value); 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /SeeSharp.Blazor/SettingBase.razor.css: -------------------------------------------------------------------------------- 1 | .render-setting { 2 | padding: 0; 3 | margin: 0; 4 | display: flex; 5 | justify-content: space-between; 6 | font-size: smaller; 7 | } 8 | 9 | .render-setting:hover { 10 | background-color: #f8fbfc; 11 | } 12 | 13 | .render-setting input { 14 | margin-left: 1em; 15 | } 16 | 17 | .render-setting input[type="number"] { 18 | width: 6em; 19 | } 20 | 21 | .render-setting input[type="checkbox"] { 22 | width: 1em; 23 | } 24 | 25 | element { 26 | background-color: #e6f1f4; 27 | padding: 1em; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /SeeSharp.Blazor/SettingsGroup.razor: -------------------------------------------------------------------------------- 1 | @namespace SeeSharp.Blazor 2 | 3 |
4 |

@Title

5 | @ChildContent 6 |
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 |

Example experiment

10 | 11 |
12 |
13 | 14 |
15 | 16 |
17 | 18 |
19 |
20 | @if (readyToRun) 21 | { 22 |

23 | } 24 | 25 | 26 | 27 |
28 | 29 | @if (!running) 30 | { 31 | @if (resultsAvailable) 32 | { 33 |
34 | 35 | 36 | @if (selected.HasValue && selected.Value) 37 | { 38 | 39 | 40 | 41 | 42 | 43 |
Mesh@(selected.Value.Mesh.Name)
Material@(selected.Value.Mesh.Material.Name) (roughness: @(selected.Value.Mesh.Material.GetRoughness(selected.Value)), transmissive: @(selected.Value.Mesh.Material.IsTransmissive(selected.Value)))
Distance@(selected.Value.Distance)
Position@(selected.Value.Position)
44 | } 45 | 46 |
47 | } 48 | } 49 | else 50 | { 51 |

Rendering...

52 | } 53 |
54 | 55 | 56 | 57 | @code { 58 | SceneSelector sceneSelector; 59 | Scene scene; 60 | bool readyToRun = false; 61 | bool running = false; 62 | bool sceneJustLoaded = false; 63 | bool resultsAvailable = false; 64 | ElementReference runButton; 65 | 66 | SimpleImageIO.FlipBook flip; 67 | 68 | async Task OnSceneLoaded(SceneFromFile sceneFromFile) 69 | { 70 | await Task.Run(() => scene = sceneFromFile.MakeScene()); 71 | flip = null; 72 | resultsAvailable = false; 73 | readyToRun = true; 74 | sceneJustLoaded = true; 75 | } 76 | 77 | protected override async Task OnAfterRenderAsync(bool firstRender) 78 | { 79 | if (readyToRun && sceneJustLoaded) 80 | { 81 | await runButton.FocusAsync(); 82 | } 83 | 84 | sceneJustLoaded = false; 85 | } 86 | 87 | async Task OnRunClick() 88 | { 89 | readyToRun = false; 90 | resultsAvailable = false; 91 | running = true; 92 | await Task.Run(() => RunExperiment()); 93 | readyToRun = true; 94 | running = false; 95 | resultsAvailable = true; 96 | } 97 | } -------------------------------------------------------------------------------- /SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Experiment.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using SeeSharp.Blazor; 3 | 4 | namespace SeeSharp.Blazor.Template.Pages; 5 | 6 | public partial class Experiment : ComponentBase 7 | { 8 | const int Width = 1280; 9 | const int Height = 720; 10 | const int MaxDepth = 10; 11 | 12 | int NumSamples = 2; 13 | 14 | long renderTimePT, renderTimeVCM; 15 | 16 | void RunExperiment() 17 | { 18 | flip = new FlipBook(660, 580) 19 | .SetZoom(FlipBook.InitialZoom.FillWidth) 20 | .SetToneMapper(FlipBook.InitialTMO.Exposure(scene.RecommendedExposure)) 21 | .SetToolVisibility(false); 22 | 23 | scene.FrameBuffer = new(Width, Height, ""); 24 | scene.Prepare(); 25 | 26 | PathTracer pathTracer = new() 27 | { 28 | TotalSpp = NumSamples, 29 | MaxDepth = MaxDepth, 30 | }; 31 | pathTracer.Render(scene); 32 | flip.Add($"PT", scene.FrameBuffer.Image); 33 | renderTimePT = scene.FrameBuffer.RenderTimeMs; 34 | 35 | scene.FrameBuffer = new(Width, Height, ""); 36 | VertexConnectionAndMerging vcm = new() 37 | { 38 | NumIterations = NumSamples, 39 | MaxDepth = MaxDepth, 40 | }; 41 | vcm.Render(scene); 42 | flip.Add($"VCM", scene.FrameBuffer.Image); 43 | renderTimeVCM = scene.FrameBuffer.RenderTimeMs; 44 | } 45 | 46 | SurfacePoint? selected; 47 | 48 | void OnFlipClick(FlipViewer.OnClickEventArgs args) 49 | { 50 | if (args.CtrlKey) 51 | { 52 | selected = scene.RayCast(new(args.X, args.Y)); 53 | } 54 | } 55 | 56 | async Task OnDownloadClick() 57 | { 58 | HtmlReport report = new(); 59 | report.AddMarkdown(""" 60 | # Example experiment 61 | $$ L_\mathrm{o} = \int_\Omega L_\mathrm{i} f_\mathrm{r} |\cos\theta_\mathrm{i}| \, d\omega_\mathrm{i} $$ 62 | """); 63 | report.AddFlipBook(flip); 64 | await SeeSharp.Blazor.Scripts.DownloadAsFile(JS, "report.html", report.ToString()); 65 | } 66 | } -------------------------------------------------------------------------------- /SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/Index.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | @using System.Reflection 4 | @using System.Text.RegularExpressions 5 | 6 | 7 |
8 | 16 |
17 | 18 | 19 | @code { 20 | /// Enumerates all .razor components in this folder 21 | public IEnumerable<(string Name, string Url)> GetExperimentPages() 22 | { 23 | var routableComponents = Assembly 24 | .GetExecutingAssembly() 25 | .ExportedTypes 26 | .Where(t => t.IsSubclassOf(typeof(ComponentBase))) 27 | .Where(c => c 28 | .GetCustomAttributes(inherit: true) 29 | .OfType() 30 | .Count() > 0); 31 | 32 | foreach (var routableComponent in routableComponents) 33 | { 34 | string name = routableComponent.ToString().Replace("SeeSharp.Blazor.Template.Pages.", string.Empty); 35 | if (name != "Index") 36 | yield return (name, name); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /SeeSharp.Templates/content/SeeSharp.Blazor.Template/Pages/_Host.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @using Microsoft.AspNetCore.Components.Web 3 | @namespace SeeSharp.Blazor.Template.Pages 4 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | @Html.Raw(SeeSharp.Blazor.Scripts.AllScripts) 16 | 17 | 18 | 19 | 20 | 21 |
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) --------------------------------------------------------------------------------