Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Engine3D/Engine3D-Tests/Engine3D-Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
</Choose>
<ItemGroup>
<Compile Include="Raytrace\RendererTests.cs" />
<Compile Include="Raytrace\ShadowMethodTests.cs" />
<Compile Include="Raytrace\SpatialSubdivisionTests.cs" />
<Compile Include="Raytrace\SphereTests.cs" />
<Compile Include="Raytrace\TriangleTests.cs" />
Expand Down
7 changes: 4 additions & 3 deletions Engine3D/Engine3D-Tests/Raytrace/RendererTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public class RendererTests

private int numMissingBaselines = 0;

private static int[] pixels = new int[maxImageWidth * maxImageHeight];
private static int[] pixels;

[TestInitialize]
public void Init()
Expand All @@ -69,6 +69,7 @@ private static void RendererSetup(Renderer renderer, string modelFileName,
//renderer.BackgroundColor = 0xff0000; // red
//renderer.BackgroundColor = 0x0000ff; // blue

pixels = new int[resolution * resolution];
renderer.SetRenderingSurface(resolution, resolution, pixels);

// Load 3D model from disk
Expand Down Expand Up @@ -167,7 +168,7 @@ public void RaytraceDynamicShadow()
public void RaytraceStaticShadow()
{
RaytraceScenario(shadows: true, staticShadows: true);
RaytraceScenario(shadows: true, staticShadows: true, shading: false);
//RaytraceScenario(shadows: true, staticShadows: true, shading: false);

if (numMissingBaselines > 0)
Assert.Fail("{0} missing baseline images were recreated", numMissingBaselines);
Expand All @@ -192,7 +193,7 @@ public void RaytraceShadowAndFocalBlur()
// TODO: produced image is slightly different if this test is run in isolation vs run together with test RaytraceStaticShadow. Why?
// TODO: number of threads affects resulting image, slighty changing quite a few pixels
// Most likely the threads are racing each other to update the shadow cache, causing non-deterministic behaviour!
[TestMethod, Ignore]
[TestMethod /*, Ignore*/]
public void RaytraceStaticShadowAndFocalBlur()
{
RaytraceScenario(focalBlur: true, shadows: true, staticShadows: true, subPixelRes: 4);
Expand Down
78 changes: 78 additions & 0 deletions Engine3D/Engine3D-Tests/Raytrace/ShadowMethodTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System;
using Engine3D;
using Engine3D.Raytrace;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Assert = Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
using Scene = Engine3D.Scene;
using Triangle = Engine3D.Raytrace.Triangle;
using Vector = Engine3D.Vector;

namespace Engine3D_Tests.Raytrace
{
[TestClass]
public class ShadowMethodTests
{
private static readonly Random Random = new Random();

[TestMethod]
public void DynamicVsStaticShadowMethods()
{
const int numRays = 100000;
const int numTriangles = 100;

var geometry = new GeometryCollection();
for (var i = 0; i < numTriangles; i++)
{
geometry.Add(new Triangle(
MakeRandomVector(-1, 1, -1, 1, -1, 1),
MakeRandomVector(-1, 1, -1, 1, -1, 1),
MakeRandomVector(-1, 1, -1, 1, -1, 1),
Color.Cyan.ToARGB()));
}

//IRayIntersectable geometry = new Sphere(Vector.Zero, 0.5);
var scene = new Scene();
const byte resolution = 10;
string instanceKey = null;
const int randomSeed = 12345678;

var context = new RenderContext(new Random(randomSeed));
var dynamicShadowMethod = new ShadowMethod(geometry, scene, false, resolution, instanceKey, context);

context = new RenderContext(new Random(randomSeed));
var staticShadowMethod = new ShadowMethod(geometry, scene, true, resolution, instanceKey, context);

var numRaysHit = 0;

for (var i = 0; i < numRays; i++)
{
var start = MakeRandomVector(-2, 2, -2, 2, -2, 2);
var dir = MakeRandomVector(-1, 1, -1, 1, -1, 1);
var info = dynamicShadowMethod.IntersectRay(start, dir, context);
if (info != null)
numRaysHit++;

var info2 = staticShadowMethod.IntersectRay(start, dir, context);

// TODO: find a scenario where this fails, e.g. multi-threaded render; vary number of threads; cache shadows to disk; repeat rays
Assert.AreEqual(info, info2);
}

//Assert.AreEqual(numRays, numRaysHit, "Num rays hit {0} should be the same as total rays {1}", numRaysHit, numRays);
//Assert.IsTrue(numRays * 0.498 < numRaysHit && numRaysHit < numRays * 0.502, "Num rays hit {0} should be roughly half of total rays {1}", numRaysHit, numRays);
Console.WriteLine("Num rays hit: {0} / {1}", numRaysHit, numRays);
}

private Vector MakeRandomVector(double minX, double maxX, double minY, double maxY, double minZ, double maxZ)
{
return new Vector((maxX - minX) * NextRandomDouble() + minX,
(maxY - minY) * NextRandomDouble() + minY,
(maxZ - minZ) * NextRandomDouble() + minZ);
}

private double NextRandomDouble()
{
return Random.NextDouble();
}
}
}
20 changes: 20 additions & 0 deletions Engine3D/Raytrace/IRayIntersectable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,26 @@ public class IntersectionInfo
// TODO: store triangle index or reference to intersected object?
//public IRayIntersectable objHit;
public int triIndex = -1; // -1 means that the intersected geometry is not a triangle

public override bool Equals(object obj)
{
var other = obj as IntersectionInfo;
if (other == null)
return false;

return rayFrac == other.rayFrac &&
pos == other.pos &&
normal == other.normal &&
color == other.color;
}

public override int GetHashCode()
{
return ((rayFrac.GetHashCode()
* 31 + pos.GetHashCode())
* 31 + normal.GetHashCode())
* 31 + color.GetHashCode();
}
}

public class RenderContext
Expand Down
6 changes: 3 additions & 3 deletions Engine3D/Raytrace/Texture3DCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ public T Sample(Vector pos)
{
// TODO: sometimes a surface point coordinate is very slightly outside the unit cube (i.e. Z coordinate of 0.50000000001)
// TODO: clamp 'close' coordinates to the unit cube?
Assert.IsTrue(-0.5 <= pos.x && pos.x <= 0.5, "Texture3D coordinate is outside unit cube");
Assert.IsTrue(-0.5 <= pos.y && pos.y <= 0.5, "Texture3D coordinate is outside unit cube");
Assert.IsTrue(-0.5 <= pos.z && pos.z <= 0.5, "Texture3D coordinate is outside unit cube");
Contract.Assert(-0.5 <= pos.x && pos.x <= 0.5, "Texture3D coordinate is outside unit cube");
Contract.Assert(-0.5 <= pos.y && pos.y <= 0.5, "Texture3D coordinate is outside unit cube");
Contract.Assert(-0.5 <= pos.z && pos.z <= 0.5, "Texture3D coordinate is outside unit cube");
var cacheIndex = (int)((pos.x + 0.5) * (cacheSize - 1)) * cacheSize * cacheSize +
(int)((pos.y + 0.5) * (cacheSize - 1)) * cacheSize +
(int)((pos.z + 0.5) * (cacheSize - 1));
Expand Down
7 changes: 1 addition & 6 deletions Engine3D/Texture3D.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Engine3D
namespace Engine3D
{
interface Texture3D<T>
{
Expand Down