Пример #1
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);
        }
Пример #2
0
        public virtual RgbColor StartFromEmitter(EmitterSample emitterSample, RgbColor initialWeight)
        {
            isOnLightSubpath = true;
            Ray ray = Raytracer.SpawnRay(emitterSample.Point, emitterSample.Direction);

            return(ContinueWalk(ray, emitterSample.Point, emitterSample.Pdf, initialWeight, 1));
        }
Пример #3
0
 private void Awake()
 {
     Instance      = this;
     _meshRenderer = GetComponent <MeshRenderer>();
     _meshRenderer.sharedMaterial = Instantiate(_meshRenderer.sharedMaterial);
     _objects = sceneObject.GetComponentsInChildren <RTObject>().ToList();
 }
Пример #4
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);
        }
Пример #5
0
        public override Vector3 GetIntensity(Ray ray, Intersection intersection, Scene scene)
        {
            float    diff = 0f, spec = 0f;
            Material mat     = intersection.primitive.material;
            bool     visible = false;

            // Calculate vector from intersection point to light point
            Vector3 L = direction;

            float NdotL = Vector3.Dot(intersection.normal, L);

            // Is the light direction on the same side as the normal?
            if (NdotL > 0f)
            {
                // check the intersection as late as possible:
                visible = !scene.DoesIntersect(new Ray(intersection.location, L), float.PositiveInfinity);
                if (visible)
                {
                    diff = NdotL;

                    if (ray.debug)
                    {
                        // Add a shadow ray to the debug screen
                        Ray debugray = new Ray(intersection.location, L);
                        debugray.debugSurface = ray.debugSurface;
                        debugray.camerapos    = ray.camerapos;
                        debugray.debugxunit   = ray.debugxunit;
                        debugray.debugyunit   = ray.debugyunit;
                        Raytracer.DrawRayDebug(debugray, null, 0x0000ff);
                    }
                }
            }

            // Add some specularity
            if (mat.isSpecular)
            {
                Vector3 viewDir = -ray.direction;
                Vector3 halfway = (L + viewDir).Normalized();
                float   NdotH   = Vector3.Dot(intersection.normal, halfway);
                // Are we on the good side of the normal?
                if (NdotH > 0f)
                {
                    // Check if this light is visible from the intersection (calculate if not calculated already)
                    if (NdotL <= 0f)
                    {
                        visible = !scene.DoesIntersect(new Ray(intersection.location, L), float.PositiveInfinity);
                    }
                    if (visible)
                    {
                        // Use a power law to let this hardness have on effect on the decay of intensity of the white effect
                        spec = (float)Math.Pow(NdotH, mat.hardness);
                    }
                }
            }
            // mix the colors, specularity (from light, not reflection) is always white.
            Vector3 diffuse = intersection.primitive.GetDiffuseColor(intersection);

            return(intensity * (diffuse * diff + Vector3.One * spec));
        }
Пример #6
0
 // initialize
 public void Init() //if camera is set at a point other than the origin, you first have to move the camera to get the right view.
 {
     rayTracer   = new Raytracer();
     a           = new Application();
     c           = new Camera(new Vector3(0, 0, 0), new Vector3(0, 0, 1), 90); //set FOV here
     c.direction = c.ProcessTarget(c.position + new Vector3(0, 0, 1));         //set target here
     c.Screen();
     s = new Scene();
 }
Пример #7
0
        private static byte[] Lab05()
        {
            Raytracer raytracer = new Raytracer(Premade.Lab05.Camera(), Premade.Lab05.Scene())
            {
                ShadowSamples = 5,
                SuperSampling = 50
            };

            return(raytracer.CalculatePixelsByteArray(imageResolution, imageResolution));
        }
Пример #8
0
        private static byte[] DepthOfField()
        {
            Raytracer raytracer = new Raytracer(Premade.DepthOfField.Camera(), Premade.DepthOfField.Scene())
            {
                SuperSampling = 512,
                ShadowSamples = 4
            };

            return(raytracer.CalculatePixelsByteArray(imageResolution, imageResolution));
        }
Пример #9
0
    public override Color GetColor(Raytracer raytracer, Ray ray, RaycastHit hit, TraceData traceData)
    {
        Color lightSumColor = RenderSettings.ambientLight;
        var   lights        = Light.GetLights(LightType.Point, 0);

        foreach (var light in lights)
        {
            Vector3 vToLight = light.transform.position - hit.point;
            float   distance = vToLight.magnitude;

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

            float lightVolumeRadius = distance * 0.2f;
            distance = Mathf.Abs(distance - lightVolumeRadius);
            Vector3 dir = vToLight.normalized;
            float   diffuseIntensity = Vector3.Dot(dir, hit.normal);

            if (diffuseIntensity <= 0)
            {
                continue;
            }

            diffuseIntensity = Mathf.Pow(diffuseIntensity, DiffuseExponent);

            float attenuation;

            switch (Attenuation)
            {
            case AttenuationKind.Logarithmic:
                const float AttenuationBase = 2.7f;
                attenuation = 1.0f / Mathf.Log(distance + AttenuationBase, AttenuationBase);
                break;

            case AttenuationKind.Quadratic:
                attenuation = 1 - distance / light.range;
                attenuation = attenuation * attenuation;
                break;

            case AttenuationKind.Linear:
            default:
                attenuation = 1 - distance / light.range;
                break;
            }

            Color lightColor = light.color * attenuation * diffuseIntensity * light.intensity * LightIntensityFactor;
            lightSumColor += lightColor;
        }

        Color totalColor = lightSumColor * this.Color;

        return(totalColor);
    }
Пример #10
0
        protected override void OnInitialize()
        {
            var scene = Scenes[1].GetScene(WIDTH, HEIGHT);

            Raytracer.LoadScene(scene);
            Raytracer.FrameBuffer = new RgbaFloat[WIDTH * HEIGHT];
            CreateDeviceResources();
            CommandLists.Add(commandList);
            Window.Resized += OnResized;
            base.OnInitialize();
            OnResized();
        }
Пример #11
0
    private void Start()
    {
        Screen.SetResolution(renderWindow.ResolutionWidth, renderWindow.ResolutionHeight, FullScreenMode.Windowed);

        SetupScene();
        SetupLights();
        SetupCameras();
        SetupCornellBox();

        raytracer = new Raytracer(scene, Color.black, mainCamera);
        RaytraceScene();
    }
Пример #12
0
    public bool MustInterrupt(Color c, TraceData traceData)
    {
        if (StopOnOverwhite && Raytracer.IsOverwhite(c))
        {
            traceData.Counters.Overwhites++;

            return(true);
        }
        else
        {
            return(false);
        }
    }
Пример #13
0
        protected override void Render()
        {
            if (isResizing)
            {
                return;
            }

            commandList.Begin();

            if (DrawModeCpu)
            {
                if (!renderOnce)
                {
                    Raytracer.RenderCpuNew();
                }

                if (!isResizing)
                {
                    unsafe
                    {
                        fixed(RgbaFloat *pixelDataPtr = Raytracer.FrameBuffer)
                        {
                            GraphicsDevice.UpdateTexture(transferTexture, (IntPtr)pixelDataPtr,
                                                         transferTexture.Width * transferTexture.Height * (uint)sizeof(RgbaFloat), 0, 0, 0,
                                                         transferTexture.Width, transferTexture.Height, 1, 0, 0);
                        }
                    }
                }
            }
            else
            {
                RenderGpu();
            }

            commandList.SetFramebuffer(GraphicsDevice.MainSwapchain.Framebuffer);
            commandList.SetPipeline(graphicsPipeline);
            commandList.SetGraphicsResourceSet(0, graphicsSet);
            commandList.Draw(3);
            commandList.End();

            if (!DrawModeCpu)
            {
                var rayCountView = GraphicsDevice.Map <uint>(rayCountReadback, MapMode.Read);
                var rPF          = rayCountView[0];
                GraphicsDevice.Unmap(rayCountReadback);
            }

            base.Render();
        }
Пример #14
0
        public override Vector3 GetIntensity(Ray ray, Intersection intersection, Scene scene)
        {
            //Calculate vector from intersection point to light point
            Vector3 lightvec = this.position - intersection.location;

            //If the best effect of the lightsource on the intersectionpoint is less than half the least visible difference
            if (lightvec.LengthSquared > maxIntensity * 512)
            {
                return(Vector3.Zero);
            }
            Vector3 lightnormal = Vector3.Normalize(lightvec);

            // The light is not inside the cone of angle cosangle
            if (Vector3.Dot(-lightnormal, direction) < cosangle)
            {
                return(Vector3.Zero);
            }

            float dot = Vector3.Dot(intersection.normal, lightnormal);

            // Is the light source on the same side as the normal?
            if (dot <= 0f)
            {
                return(Vector3.Zero);           // In this case not.
            }
            // check the intersection as late as possible:
            if (scene.DoesIntersect(new Ray(intersection.location, lightnormal), lightvec.Length))
            {
                return(Vector3.Zero);
            }

            if (ray.debug)
            {
                // Add a shadow ray to the debug screen
                Ray debugray = new Ray(intersection.location, lightnormal);
                debugray.debugSurface = ray.debugSurface;
                debugray.camerapos    = ray.camerapos;
                debugray.debugxunit   = ray.debugxunit;
                debugray.debugyunit   = ray.debugyunit;
                Intersection fakeintersection = new Intersection(this.position, 0, null, Vector3.UnitX);
                Raytracer.DrawRayDebug(debugray, fakeintersection, 0x0000ff);
            }
            // Use inverse square law for intensity at distance lightvec.Length
            return(intensity * dot / lightvec.LengthSquared);
        }
Пример #15
0
        private static void Main(string [] args)
        {
            RenderScene Scene = new RenderScene( );
            Sphere      S     = new Sphere
            {
                Position = new Vector3(0, 0, -40),
                Radius   = 1
            };

            Scene.AddRendererObject(S);
            RenderSettings Settings = new RaytracerSettings( );

            Renderer R = new Raytracer( );

            R.SetData(Scene, Settings);
            RendererImage Render = R.Render( );
            Bitmap        Bmp    = Render.ConvertToBitmap( );

            Bmp.Save("D:\\Temp\\tempimg.png");
            Process.Start("D:\\Temp\\tempimg.png");
            Console.ReadLine( );
        }
Пример #16
0
        public virtual RgbColor StartFromBackground(Ray ray, RgbColor initialWeight, float pdf)
        {
            isOnLightSubpath = true;

            // Find the first actual hitpoint on scene geometry
            var hit = scene.Raytracer.Trace(ray);

            if (!hit)
            {
                return(OnInvalidHit(ray, pdf, initialWeight, 1));
            }

            // Sample the next direction (required to know the reverse pdf)
            var(pdfNext, pdfReverse, weight, direction) = SampleNextDirection(hit, ray, initialWeight, 1);

            // Both pdfs have unit sr-1
            float pdfFromAncestor = pdf;
            float pdfToAncestor   = pdfReverse;

            RgbColor estimate = OnHit(ray, hit, pdfFromAncestor, initialWeight, 1, 1.0f);

            OnContinue(pdfToAncestor, 1);

            // Terminate if the maximum depth has been reached
            if (maxDepth <= 1)
            {
                return(estimate);
            }

            // Terminate absorbed paths and invalid samples
            if (pdfNext == 0 || weight == RgbColor.Black)
            {
                return(estimate);
            }

            // Continue the path with the next ray
            ray = Raytracer.SpawnRay(hit, direction);
            return(estimate + ContinueWalk(ray, hit, pdfNext, initialWeight * weight, 2));
        }
Пример #17
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");
        }
Пример #18
0
 public Application(Surface sur)
 {
     raytracer = new Raytracer(sur);
     camera    = raytracer.camera;
     prevState = Keyboard.GetState();
 }
Пример #19
0
 void Start()
 {
     raytracer = GetComponent <Raytracer> ();
 }
Пример #20
0
 public Application(Surface sur)
 {
     Screen = sur;
     R      = new Raytracer(sur);
     C      = Camera.Instance();
 }
Пример #21
0
        private static byte[] Lab04Textures()
        {
            Raytracer raytracer = new Raytracer(Premade.Lab04Textures.Camera(), Premade.Lab04Textures.Scene());

            return(raytracer.CalculatePixelsByteArray(imageResolution, imageResolution));
        }
Пример #22
0
        private static byte[] Lab04BVH()
        {
            Raytracer raytracer = new Raytracer(Premade.Lab04BVH.Camera(), Premade.Lab04BVH.Scene(Scene.AccelerationStructure.BVH));

            return(raytracer.CalculatePixelsByteArray(imageResolution, imageResolution));
        }
Пример #23
0
        protected override void Update(InputSnapshot snapshot, float elapsedSeconds)
        {
            base.Update(snapshot, elapsedSeconds);
            var frameRate   = 1f / ElapsedSecondsBetweenRenders;
            var tickRate    = 1f / elapsedSeconds;
            var rate        = Raytracer.RaysPerFrame * frameRate;
            var millionRate = rate / 1_000_000;
            var budget      = Stopwatch.Frequency / TargetTicksPerFrame;

            Window.Title =
                $"{millionRate:F2} MRays / sec. | {Raytracer.RaysPerFrame / 1_000_000:F2} MRays / Frame | {frameRate:F2} FPS | {tickRate:F2} TPS | {MillisecondsPerLogic:F0}ms / Update | {MillisecondsPerRender:F0}ms / Render | Budget: {budget:F0}ms / Frame";

            if (ImGui.Begin("Debug"))
            {
                if (ImGui.BeginMenu("Raytracer Debug"))
                {
                    if (ImGui.Button("Clear Framebuffer"))
                    {
                        Array.Clear(Raytracer.FrameBuffer, 0, Raytracer.FrameBuffer.Length);
                    }

                    ImGui.Checkbox("Render once on button press", ref renderOnce);

                    if (renderOnce && ImGui.Button(
                            renderThread != null && !renderThread.IsCompleted && !renderThread.IsCanceled
                            ? "Rendering"
                            : "Render Once"))
                    {
                        if (renderThread != null && !renderThread.IsCompleted && !renderThread.IsCanceled)
                        {
                            cancellationTokenSource?.Cancel();
                        }

                        if (renderThread == null || renderThread.IsCompleted || renderThread.IsCanceled)
                        {
                            cancellationTokenSource = new CancellationTokenSource(100);
                            renderThread            = Task.Run(Raytracer.RenderCpuNew,
                                                               cancellationTokenSource.Token);
                        }
                    }
                }
            }

            foreach (var keyEvent in snapshot.KeyEvents)
            {
                if (keyEvent.Down && keyEvent.Key == Key.D)
                {
                    Raytracer.SceneParams.Camera.Origin.X += 1f;
                    Raytracer.SceneParams.Camera.LookAt.X += 1f;
                    Camera.Recalculate(ref Raytracer.SceneParams.Camera);
                }
                else if (keyEvent.Down && keyEvent.Key == Key.A)
                {
                    Raytracer.SceneParams.Camera.Origin.X -= 1f;
                    Raytracer.SceneParams.Camera.LookAt.X -= 1f;
                    Camera.Recalculate(ref Raytracer.SceneParams.Camera);
                }
            }

            Raytracer.RenderSettings();
        }
Пример #24
0
        RgbColor ContinueWalk(Ray ray, SurfacePoint previousPoint, float pdfDirection, RgbColor throughput,
                              int depth)
        {
            // Terminate if the maximum depth has been reached
            if (depth >= maxDepth)
            {
                OnTerminate();
                return(RgbColor.Black);
            }

            var hit = scene.Raytracer.Trace(ray);

            if (!hit)
            {
                var result = OnInvalidHit(ray, pdfDirection, throughput, depth);
                OnTerminate();
                return(result);
            }

            // Convert the PDF of the previous hemispherical sample to surface area
            float pdfFromAncestor = pdfDirection * SampleWarp.SurfaceAreaToSolidAngle(previousPoint, hit);

            // Geometry term might be zero due to, e.g., shading normal issues
            // Avoid NaNs in that case by terminating early
            if (pdfFromAncestor == 0)
            {
                OnTerminate();
                return(RgbColor.Black);
            }

            RgbColor estimate = OnHit(ray, hit, pdfFromAncestor, throughput, depth,
                                      SampleWarp.SurfaceAreaToSolidAngle(hit, previousPoint));

            // Terminate with Russian roulette
            float survivalProb = ComputeSurvivalProbability(hit, ray, throughput, depth);

            if (rng.NextFloat() > survivalProb)
            {
                OnTerminate();
                return(estimate);
            }

            // Continue based on the splitting factor
            int numSplits = ComputeSplitFactor(hit, ray, throughput, depth);

            for (int i = 0; i < numSplits; ++i)
            {
                // Sample the next direction and convert the reverse pdf
                var(pdfNext, pdfReverse, weight, direction) = SampleNextDirection(hit, ray, throughput, depth);
                float pdfToAncestor = pdfReverse * SampleWarp.SurfaceAreaToSolidAngle(hit, previousPoint);

                if (pdfToAncestor == 0)
                {
                    Debug.Assert(pdfNext == 0);
                }

                OnContinue(pdfToAncestor, depth);

                if (pdfNext == 0 || weight == RgbColor.Black)
                {
                    OnTerminate();
                    continue;
                }

                // Account for splitting and roulette in the weight
                weight *= 1.0f / (survivalProb * numSplits);

                // Continue the path with the next ray
                var nextRay = Raytracer.SpawnRay(hit, direction);
                estimate += ContinueWalk(nextRay, hit, pdfNext, throughput * weight, depth + 1);
            }

            return(estimate);
        }
Пример #25
0
    }                           // It is here to allow enable/disable component.

    public abstract Color GetColor(Raytracer raytracer, Ray ray, RaycastHit hit, TraceData traceData);
Пример #26
0
 // initialize
 public void Init()
 {
     rayTracer = new Raytracer();
     c         = new Camera(Vector3.Zero, new Vector3(0, 0, 1));
     c.Screen();
 }
Пример #27
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");
        }
Пример #28
0
        public override Vector3 GetIntensity(Ray ray, Intersection intersection, Scene scene)
        {
            float    diff = 0f, spec = 0f;
            Material mat     = intersection.primitive.material;
            bool     visible = false;

            // Calculate vector from intersection point to light point
            Vector3 L = position - intersection.location;
            // Use inverse square law for intensity at a certain distance
            float dist = L.Length, distSq = L.LengthSquared;

            // If the best effect of the lightsource on the intersectionpoint is less than half the least visible difference
            if (distSq <= maxIntensity * 512)
            {
                L /= dist;
                float NdotL = Vector3.Dot(intersection.normal, L);
                // Is the light direction on the same side as the normal?
                if (NdotL > 0f)
                {
                    // check the intersection as late as possible:
                    visible = !scene.DoesIntersect(new Ray(intersection.location, L), dist);
                    if (visible)
                    {
                        diff = NdotL / distSq;

                        if (ray.debug)
                        {
                            // Add a shadow ray to the debug screen
                            Ray debugray = new Ray(intersection.location, L);
                            debugray.debugSurface = ray.debugSurface;
                            debugray.camerapos    = ray.camerapos;
                            debugray.debugxunit   = ray.debugxunit;
                            debugray.debugyunit   = ray.debugyunit;
                            // Fake intersection makes sure the line is drawn from the intersection point up to the light position
                            Intersection fakeintersection = new Intersection(this.position, 0, null, Vector3.UnitX);
                            Raytracer.DrawRayDebug(debugray, fakeintersection, 0x0000ff);
                        }
                    }
                }

                // Add some specularity
                if (mat.isSpecular)
                {
                    Vector3 viewDir = -ray.direction;
                    Vector3 halfway = (L + viewDir).Normalized();
                    float   NdotH   = Vector3.Dot(intersection.normal, halfway);
                    // Are we on the good side of the normal?
                    if (NdotH > 0f)
                    {
                        // Check if this light is visible from the intersection (calculate if not calculated already)
                        if (NdotL <= 0f)
                        {
                            visible = !scene.DoesIntersect(new Ray(intersection.location, L), dist);
                        }
                        if (visible)
                        {
                            // Use a power law to let this hardness have on effect on the decay of intensity of the white effect
                            spec = ((float)Math.Pow(NdotH, mat.hardness)) / distSq;
                        }
                    }
                }
            }
            // mix the colors, specularity (from light, not reflection) is always white.
            Vector3 diffuse = intersection.primitive.GetDiffuseColor(intersection);

            return(intensity * (diffuse * diff + Vector3.One * spec));
        }
Пример #29
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);
    }
Пример #30
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}");
        }