Exemplo n.º 1
0
        public void SimpleQuad_ShouldBeIntersected()
        {
            var vertices = new Vector3[] {
                new Vector3(-1, 0, -1),
                new Vector3(1, 0, -1),
                new Vector3(1, 0, 1),
                new Vector3(-1, 0, 1)
            };

            var indices = new int[] {
                0, 1, 2,
                0, 2, 3
            };

            TriangleMesh mesh = new(vertices, indices);

            using var rt = new Raytracer();
            rt.AddMesh(mesh);
            rt.CommitScene();

            Hit hit = rt.Trace(new Ray {
                Origin      = new Vector3(-0.5f, -10, 0),
                Direction   = new Vector3(0, 1, 0),
                MinDistance = 1.0f
            });

            Assert.Equal(10.0f, hit.Distance, 0);
            Assert.Equal(1u, hit.PrimId);
            Assert.Equal(mesh, hit.Mesh);
        }
Exemplo n.º 2
0
        public void SimpleQuad_ShouldBeMissed()
        {
            var vertices = new Vector3[] {
                new Vector3(-1, 0, -1),
                new Vector3(1, 0, -1),
                new Vector3(1, 0, 1),
                new Vector3(-1, 0, 1)
            };

            var indices = new int[] {
                0, 1, 2,
                0, 2, 3
            };

            TriangleMesh mesh = new TriangleMesh(vertices, indices);

            var rt = new Raytracer();

            rt.AddMesh(mesh);
            rt.CommitScene();

            Hit hit = rt.Trace(new Ray {
                Origin      = new Vector3(-0.5f, -10, 0),
                Direction   = new Vector3(0, -1, 0),
                MinDistance = 1.0f
            });

            Assert.False(hit);
        }
Exemplo n.º 3
0
        static void MeasurePinvokeOverhead(int numTrials)
        {
            var vertices = new Vector3[] {
                new Vector3(-1, 0, -1),
                new Vector3(1, 0, -1),
                new Vector3(1, 0, 1),
                new Vector3(-1, 0, 1)
            };

            var indices = new int[] {
                0, 1, 2,
                0, 2, 3
            };

            TriangleMesh mesh = new TriangleMesh(vertices, indices);

            var rt = new Raytracer();

            rt.AddMesh(mesh);
            rt.CommitScene();

            Stopwatch stop = Stopwatch.StartNew();

            for (int k = 0; k < numTrials; ++k)
            {
                for (int i = 0; i < 1000000; ++i)
                {
                    rt.Trace(new Ray {
                        Origin      = new Vector3(-0.5f, -10, 0),
                        Direction   = new Vector3(0, 1, 0),
                        MinDistance = 1.0f
                    });
                }
            }
            stop.Stop();
            Console.WriteLine($"One million rays intersected in {stop.ElapsedMilliseconds / numTrials}ms");
        }
Exemplo n.º 4
0
    public override Color GetColor(Raytracer raytracer, Ray ray, RaycastHit hit, TraceData traceData)
    {
        Vector2 texCoord;
        Vector3 surfaceNormal;
        Vector3 tangent, binormal;
        bool    texCoordAndTangentRequired = NormalMap != null || DiffuseTexture != null || SpecularMap != null || ReflectionMap != null;

        texCoordAndTangentRequired = true;              // DEBUG

        if (texCoordAndTangentRequired)
        {
            Vector3 localNormal;
            Vector3 localTangent;

            ColliderUtils.GetTexCoordAndTangent(
                hit,
                out texCoord, out localNormal, out localTangent
                );

            tangent       = hit.collider.transform.TransformDirection(localTangent);
            surfaceNormal = hit.collider.transform.TransformDirection(localNormal);
            binormal      = Vector3.Cross(tangent, surfaceNormal);

            #region Debug Visualisation
#if DEBUG_SHOW_TEX_COORDS
            return(new Color(texCoord.x, texCoord.y, 0, 1));
#endif

#if DEBUG_SHOW_BARYCENTRIC_COORDS
            Vector3 dbgBc = hit.barycentricCoordinate;

            return(new Color(dbgBc.x, dbgBc.y, dbgBc.z, 1));
#endif

#if DEBUG_SHOW_NORMALS
            Vector3 dbgLocalNormal = localNormal;
            //dbgLocalNormal = new Vector3 (
            //	Mathf.Abs ( dbgLocalNormal.x ),
            //	Mathf.Abs ( dbgLocalNormal.y ),
            //	Mathf.Abs ( dbgLocalNormal.z )
            //);
            dbgLocalNormal = (dbgLocalNormal + Vector3.one) * 0.5f;

            return(new Color(dbgLocalNormal.x, dbgLocalNormal.y, dbgLocalNormal.z, 1));
#endif

#if DEBUG_SHOW_TANGENTS
            Vector3 dbgLocalTangent = localTangent;

            if (false)
            {
                dbgLocalTangent = new Vector3(
                    Mathf.Abs(dbgLocalTangent.x),
                    Mathf.Abs(dbgLocalTangent.y),
                    Mathf.Abs(dbgLocalTangent.z)
                    );
            }
            else if (false)
            {
                dbgLocalTangent = new Vector3(
                    Mathf.Abs(dbgLocalTangent.x > 0 ? dbgLocalTangent.x : 0),
                    Mathf.Abs(dbgLocalTangent.y > 0 ? dbgLocalTangent.y : 0),
                    Mathf.Abs(dbgLocalTangent.z > 0 ? dbgLocalTangent.z : 0)
                    );
            }
            else
            {
                dbgLocalTangent = (dbgLocalTangent + Vector3.one) * 0.5f;
            }

            return(new Color(dbgLocalTangent.x, dbgLocalTangent.y, dbgLocalTangent.z, 1));
#endif

#if DEBUG_SHOW_BINORMALS
            Vector3 localBinormal = Vector3.Cross(localTangent, localNormal);
            Vector3 dbgBinormal   = localBinormal;
            dbgBinormal = new Vector3(
                Mathf.Abs(dbgBinormal.x),
                Mathf.Abs(dbgBinormal.y),
                Mathf.Abs(dbgBinormal.z)
                );

            return(new Color(dbgBinormal.x, dbgBinormal.y, dbgBinormal.z, 1));
#endif
            #endregion Debug Visualisation
        }
        else
        {
            texCoord      = Vector2.zero;
            surfaceNormal = hit.normal;
            tangent       = Vector3.zero;
            binormal      = Vector3.zero;
        }

        bool entering = Vector3.Dot(hit.normal, ray.direction) <= 0;
        surfaceNormal = entering ? surfaceNormal : -surfaceNormal;

        /* TODO: revise where "entering" calculated upon hit.normal should be replaced
         * with "entering" calculated upon surfaceNormal transformed with TBN. */

        if (NormalMap != null && NormalMapInfluence > 0)
        {
            var     normalMapRt    = TextureCache.FromUnityTexture(NormalMap);
            Color   normalMapColor = normalMapRt.GetFilteredPixel(texCoord.x, texCoord.y);
            Vector3 texNormal      = new Vector3(normalMapColor.r, normalMapColor.g, normalMapColor.b);
            texNormal = 2 * texNormal - Vector3.one;
            texNormal.Normalize();
            Vector3 texNormalWorld = Raytracer.TransformTbn(texNormal, tangent, binormal, surfaceNormal);

            float normalMapInfluence = Mathf.Clamp01(NormalMapInfluence);
            surfaceNormal = Vector3.Lerp(surfaceNormal, texNormalWorld, normalMapInfluence).normalized;

#if DEBUG_SHOW_SURFACE_NORMALS
            Vector3 dbgSurfaceNormal = surfaceNormal;
            //dbgSurfaceNormal = new Vector3 (
            //	Mathf.Abs ( dbgSurfaceNormal.x ),
            //	Mathf.Abs ( dbgSurfaceNormal.y ),
            //	Mathf.Abs ( dbgSurfaceNormal.z )
            //);
            dbgSurfaceNormal = (dbgSurfaceNormal + Vector3.one) * 0.5f;

            return(new Color(dbgSurfaceNormal.x, dbgSurfaceNormal.y, dbgSurfaceNormal.z, 1));
#endif
        }

        float specularIntensity = SpecularComponent;

        if (SpecularMap != null && SpecularMapInfluence > 0 && specularIntensity > 0)
        {
            var   specularMapRt    = TextureCache.FromUnityTexture(SpecularMap);
            Color specularMapColor = specularMapRt.GetFilteredPixel(texCoord.x, texCoord.y);
            specularIntensity *= specularMapColor.grayscale * specularMapColor.a;

            float specularMapInfluence = Mathf.Clamp01(SpecularMapInfluence);
            specularIntensity = Mathf.Lerp(SpecularComponent, specularIntensity, specularMapInfluence);
        }

        Color totalColor           = Color.black;
        Color diffuseLightSumColor = raytracer.OverrideAmbientLight ? raytracer.AmbientLight : RenderSettings.ambientLight;
        diffuseLightSumColor *= DiffuseComponent;
        Color specularLightSumColor = Color.black;

        var lights = Light.GetLights(LightType.Point, 0);

        foreach (var light in lights)
        {
            Vector3 vToLight          = light.transform.position - hit.point;
            float   lightVolumeRadius = light.range * 0.00625f;                 // Empirical coefficient.
            float   distance          = vToLight.magnitude - lightVolumeRadius;

            if (distance < 0)
            {
                distance = 0;
            }
            else if (distance >= light.range)
            {
                continue;
            }

            Vector3 dirToLight = vToLight.normalized;
            float   attenuation;
            attenuation = 1 - distance / light.range;
            attenuation = attenuation * attenuation;

            float lightIntensity = light.intensity * LightIntensityFactor;

            if (DiffuseComponent > 0)
            {
                float diffuseIntensity = Vector3.Dot(dirToLight, surfaceNormal);

                if (diffuseIntensity > 0)
                {
                    diffuseIntensity = Mathf.Pow(diffuseIntensity, DiffuseExponent);
                    Color diffuseLightColor = light.color * attenuation * diffuseIntensity * lightIntensity;
                    diffuseLightSumColor += diffuseLightColor;
                }
            }

            if (specularIntensity > 0)
            {
                Vector3 reflectionDir = Vector3.Reflect(-dirToLight, surfaceNormal);
                Vector3 vToView       = raytracer.Camera.transform.position - hit.point;
                Vector3 dirToView     = vToView.normalized;
                float   specularity   = Vector3.Dot(reflectionDir, dirToView);

                if (specularity > 0)
                {
                    specularity = Mathf.Pow(specularity, SpecularPower);
                    Color specularLightColor = light.color * attenuation * specularity * lightIntensity;
                    specularLightSumColor += specularLightColor;
                }
            }
        }

        Color diffuseColor;

        if (DiffuseTexture != null)
        {
            var diffuseTextureRt = TextureCache.FromUnityTexture(DiffuseTexture);
            // TODO: calculate miplevel, get the color according to its value.
            Color texColor = diffuseTextureRt.GetFilteredPixel(texCoord.x, texCoord.y);

            if (texColor.a < 1 && DiffuseColorIsBackground)
            {
                diffuseColor = Color.Lerp(this.DiffuseColor, texColor, texColor.a);
            }
            else
            {
                diffuseColor = Color.Lerp(Color.black, texColor, texColor.a);
            }

            diffuseColor.a = texColor.a;
        }
        else
        {
            diffuseColor = this.DiffuseColor;
        }

        totalColor = diffuseLightSumColor * diffuseColor * DiffuseComponent + specularLightSumColor * specularIntensity;

        if (raytracer.MustInterrupt(totalColor, traceData))
        {
            return(totalColor);
        }

        bool  willReflect;
        float reflectionIntensity;

        if (entering)
        {
            willReflect         = ReflectionComponent > 0 && traceData.NumReflections < raytracer.MaxReflections;
            reflectionIntensity = ReflectionComponent;
        }
        else
        {
            willReflect         = InnerReflectionComponent > 0 && traceData.NumInnerReflections < raytracer.MaxInnerReflections;
            reflectionIntensity = InnerReflectionComponent;
        }

        if (willReflect && ReflectionMap != null && ReflectionMapInfluence > 0)
        {
            var   reflectionMapRt        = TextureCache.FromUnityTexture(ReflectionMap);
            Color reflectionMapColor     = reflectionMapRt.GetFilteredPixel(texCoord.x, texCoord.y);
            float reflectionMapIntensity = reflectionMapColor.grayscale * reflectionMapColor.a;

            float reflectionMapInfluence = Mathf.Clamp01(ReflectionMapInfluence);
            reflectionIntensity = Mathf.Lerp(reflectionIntensity, reflectionMapIntensity * reflectionIntensity, reflectionMapInfluence);

            willReflect = reflectionIntensity > 0;
        }

        float refractionIntensity = RefractionComponent;

        if (RefractWhereTranslucent)
        {
            refractionIntensity *= 1 - diffuseColor.a * DiffuseComponent;
        }

        bool      willRefract     = refractionIntensity > 0 && traceData.NumRefractions < raytracer.MaxRefractions;
        bool      forkingRequired = willReflect && willRefract;
        TraceData tdForRefraction;

        if (forkingRequired)
        {
            tdForRefraction = traceData.Fork();
        }
        else
        {
            tdForRefraction = traceData;
        }

        if (willReflect)
        {
            if (entering)
            {
                traceData.NumReflections++;
                traceData.Counters.Reflections++;
            }
            else
            {
                traceData.NumInnerReflections++;
                traceData.Counters.InnerReflections++;
            }

            Vector3 reflectionDir   = Vector3.Reflect(ray.direction, surfaceNormal);
            Vector3 pushedOutPoint  = hit.point + reflectionDir * Raytracer.PushOutMagnitude;
            Color   reflectionColor = raytracer.Trace(new Ray(pushedOutPoint, reflectionDir), traceData);
            totalColor += reflectionColor * reflectionIntensity;

            if (raytracer.MustInterrupt(totalColor, traceData))
            {
                return(totalColor);
            }
        }

        if (willRefract)
        {
            tdForRefraction.NumRefractions++;
            tdForRefraction.Counters.Refractions++;
            CoefficientOut = RefractionIndex;
            CoefficientIn  = 1 / RefractionIndex;

            if (CoefficientOut > 1)
            {
                CriticalOutAngleCos = Mathf.Sqrt(1 - CoefficientIn * CoefficientIn);
                CriticalInAngleCos  = 0;
            }
            else
            {
                CriticalOutAngleCos = 0;
                CriticalInAngleCos  = Mathf.Sqrt(1 - CoefficientOut * CoefficientOut);
            }

            float   criticalAngleCos = entering ? CriticalInAngleCos : CriticalOutAngleCos;
            Vector3 refractionDir;
            float   nDotRay = Vector3.Dot(surfaceNormal, ray.direction);

            if (Mathf.Abs(nDotRay) >= criticalAngleCos)
            {
                if (entering)
                {
                    tdForRefraction.PenetrationStack.Push(hit);
                }
                else
                {
                    tdForRefraction.PenetrationStack.Pop();
                }

                float k = entering ? CoefficientIn : CoefficientOut;
                refractionDir = Raytracer.Refract(ray.direction, surfaceNormal, nDotRay, k);
                refractionDir.Normalize();
            }
            else                // Total internal reflection.
            {
                refractionDir = Vector3.Reflect(ray.direction, surfaceNormal);
            }

            Vector3 pushedOutPoint  = hit.point + refractionDir * Raytracer.PushOutMagnitude;
            Color   refractionColor = raytracer.Trace(new Ray(pushedOutPoint, refractionDir), tdForRefraction);

            if (ColorAberration != 0 && entering)
            {
                //float rDotRay = Vector3.Dot ( refractionDir, ray.direction );
                refractionColor = HsvColor.ChangeHue(refractionColor, (1 + nDotRay) * ColorAberration);
            }

            totalColor += refractionColor * refractionIntensity;

            if (raytracer.MustInterrupt(totalColor, tdForRefraction))
            {
                return(totalColor);
            }
        }

        return(totalColor);
    }
Exemplo n.º 5
0
        public static void MeasurePinvokeOverhead(int numTrials)
        {
            var vertices = new Vector3[] {
                new Vector3(-1, 0, -1),
                new Vector3(1, 0, -1),
                new Vector3(1, 0, 1),
                new Vector3(-1, 0, 1)
            };

            var indices = new int[] {
                0, 1, 2,
                0, 2, 3
            };

            TriangleMesh mesh = new TriangleMesh(vertices, indices);

            var rt = new Raytracer();

            rt.AddMesh(mesh);
            rt.CommitScene();

            Random    rng  = new(1337);
            Stopwatch stop = Stopwatch.StartNew();

            for (int k = 0; k < numTrials; ++k)
            {
                for (int i = 0; i < 1000000; ++i)
                {
                    rt.Trace(new Ray {
                        Origin = new Vector3(
                            (float)rng.NextDouble(),
                            (float)rng.NextDouble(),
                            (float)rng.NextDouble()),
                        Direction = new Vector3(
                            (float)rng.NextDouble(),
                            (float)rng.NextDouble(),
                            (float)rng.NextDouble()),
                        MinDistance = 0.0f
                    });
                }
            }
            stop.Stop();
            Console.WriteLine($"One million rays intersected in {stop.ElapsedMilliseconds / numTrials}ms");
            long traceCost = stop.ElapsedMilliseconds / numTrials;

            stop = Stopwatch.StartNew();
            for (int k = 0; k < numTrials; ++k)
            {
                for (int i = 0; i < 1000000; ++i)
                {
                    new Vector3(
                        (float)rng.NextDouble(),
                        (float)rng.NextDouble(),
                        (float)rng.NextDouble()
                        );
                    new Vector3(
                        (float)rng.NextDouble(),
                        (float)rng.NextDouble(),
                        (float)rng.NextDouble()
                        );
                }
            }
            stop.Stop();
            Console.WriteLine($"RNG overhead: {stop.ElapsedMilliseconds / numTrials}ms");
            long rngCost = stop.ElapsedMilliseconds / numTrials;

            Console.WriteLine($"Pure cost for tracing + overhead: {traceCost-rngCost}ms");
        }
Exemplo n.º 6
0
        public static void ComplexScene(int numTrials)
        {
            // the scene is compressed to avoid git issues
            if (!File.Exists("../Data/breakfast_room.obj"))
            {
                ZipFile.ExtractToDirectory("../Data/breakfast_room.zip", "../Data");
            }

            Stopwatch           stop   = Stopwatch.StartNew();
            List <TriangleMesh> meshes = new();
            Vector3             min    = Vector3.One * float.MaxValue;
            Vector3             max    = -Vector3.One * float.MaxValue;

            Assimp.AssimpContext context = new();
            var scene = context.ImportFile("../Data/breakfast_room.obj",
                                           Assimp.PostProcessSteps.GenerateNormals | Assimp.PostProcessSteps.JoinIdenticalVertices |
                                           Assimp.PostProcessSteps.PreTransformVertices | Assimp.PostProcessSteps.Triangulate);

            foreach (var m in scene.Meshes)
            {
                var    material     = scene.Materials[m.MaterialIndex];
                string materialName = material.Name;

                Vector3[] vertices = new Vector3[m.VertexCount];
                for (int i = 0; i < m.VertexCount; ++i)
                {
                    vertices[i] = new(m.Vertices[i].X, m.Vertices[i].Y, m.Vertices[i].Z);
                }

                meshes.Add(new(vertices, m.GetIndices()));

                min.X = MathF.Min(min.X, m.BoundingBox.Min.X);
                min.Y = MathF.Min(min.X, m.BoundingBox.Min.Y);
                min.Z = MathF.Min(min.X, m.BoundingBox.Min.Z);

                max.X = MathF.Max(max.X, m.BoundingBox.Max.X);
                max.Y = MathF.Max(max.X, m.BoundingBox.Max.Y);
                max.Z = MathF.Max(max.X, m.BoundingBox.Max.Z);
            }

            var diagonal = max - min;

            Console.WriteLine($"Scene loaded in {stop.ElapsedMilliseconds}ms");
            stop.Restart();

            var rt = new Raytracer();

            foreach (var m in meshes)
            {
                rt.AddMesh(m);
            }
            rt.CommitScene();

            Console.WriteLine($"Acceleration structures built in {stop.ElapsedMilliseconds}ms");

            Random rng = new(1337);

            Vector3 NextVector() => new Vector3(
                (float)rng.NextDouble(),
                (float)rng.NextDouble(),
                (float)rng.NextDouble());

            stop.Restart();
            float averageDistance = 0;

            for (int k = 0; k < numTrials; ++k)
            {
                for (int i = 0; i < 1000000; ++i)
                {
                    var hit = rt.Trace(new Ray {
                        Origin      = NextVector() * diagonal + min,
                        Direction   = NextVector(),
                        MinDistance = 0.0f
                    });

                    if (hit)
                    {
                        averageDistance += hit.Distance / 1000000 / numTrials;
                    }
                }
            }
            stop.Stop();
            Console.WriteLine($"One million closest hits found in {stop.ElapsedMilliseconds / numTrials}ms");
            Console.WriteLine($"Average distance: {averageDistance}");

            stop.Restart();
            float averageVisibility = 0;

            for (int k = 0; k < numTrials; ++k)
            {
                for (int i = 0; i < 1000000; ++i)
                {
                    bool occluded = rt.IsOccluded(new ShadowRay(new Ray {
                        Origin      = NextVector() * diagonal + min,
                        Direction   = NextVector(),
                        MinDistance = 0.0f
                    }, maxDistance: (float)rng.NextDouble() * averageDistance * 5));

                    if (!occluded)
                    {
                        averageVisibility += 1.0f / 1000000.0f / numTrials;
                    }
                }
            }
            stop.Stop();
            Console.WriteLine($"One million any hits found in {stop.ElapsedMilliseconds / numTrials}ms");
            Console.WriteLine($"Average visibility: {averageVisibility}");
        }
Exemplo n.º 7
0
        private static void RaycastingMesh(Texture2D texture, float3 cameraPosition, float3 lightPosition, float3 target)
        {
            // View and projection matrices
            var viewMatrix       = Transforms.LookAtLH(cameraPosition, target, float3.up);
            var projectionMatrix =
                Transforms.PerspectiveFovLH(pi_over_4, texture.Height / (float)texture.Width, 0.01f, 20);

            var scene = new Scene <PositionNormal>();

            CreateMeshScene(scene);

            // Raycaster to trace rays and check for shadow rays.
            var shadower = new Raytracer <ShadowRayPayload, PositionNormal>();

            shadower.OnAnyHit += (IRaycastContext context, PositionNormal attribute, ref ShadowRayPayload payload) =>
            {
                // If any object is found in ray-path to the light, the ray is shadowed.
                payload.Shadowed = true;
                // No necessary to continue checking other objects
                return(HitResult.Stop);
            };

            // Raycaster to trace rays and lit closest surfaces
            var raycaster = new Raytracer <RayPayload, PositionNormal>();

            raycaster.OnClosestHit += (IRaycastContext context, PositionNormal attribute, ref RayPayload payload) =>
            {
                // Move geometry attribute to world space
                attribute = attribute.Transform(context.FromGeometryToWorld);

                var V             = normalize(cameraPosition - attribute.Position);
                var L             = normalize(lightPosition - attribute.Position);
                var lambertFactor = max(0, dot(attribute.Normal, L));

                // Check ray to light...
                var shadow = new ShadowRayPayload();
                shadower.Trace(scene,
                               RayDescription.FromTo(
                                   attribute.Position +
                                   attribute.Normal * 0.001f, // Move an epsilon away from the surface to avoid self-shadowing
                                   lightPosition), ref shadow);

                payload.Color = shadow.Shadowed ? float3(0, 0, 0) : float3(1, 1, 1) * lambertFactor;
            };

            raycaster.OnMiss += (IRaycastContext context, ref RayPayload payload) =>
            {
                payload.Color = float3(0, 0, 1); // Blue, as the sky.
            };

            // Render all points of the screen
            for (var px = 0; px < texture.Width; px++)
            {
                for (var py = 0; py < texture.Height; py++)
                {
                    var progress = px * texture.Height + py;
                    if (progress % 100 == 0)
                    {
                        Console.Write("\r" + progress * 100 / (float)(texture.Width * texture.Height) + "%            ");
                    }

                    var ray = RayDescription.FromScreen(px + 0.5f, py + 0.5f, texture.Width, texture.Height,
                                                        inverse(viewMatrix), inverse(projectionMatrix), 0, 1000);

                    var coloring = new RayPayload();

                    raycaster.Trace(scene, ray, ref coloring);

                    texture.Write(px, py, float4(coloring.Color, 1));
                }
            }
            Console.Write("\r" + 100 + "%            ");
        }