示例#1
0
        private Vector3 CalcColor(Ray ray, int pathTracingLimit, int reflectionLimit)
        {
            var rgb = Vector3.Zero;

            var hitPoint = FindClosestHitPoint(ray);

            if (hitPoint != null)
            {
                var sphere = hitPoint.Sphere;

                var rayVecNorm  = Vector3.Normalize(hitPoint.Ray.DirectionVec);
                var rayVec      = (hitPoint.Ray.Lambda * rayVecNorm) + (-rayVecNorm * 0.001f) /* nudging..? */;
                var hitPointVec = hitPoint.Ray.StartVec + rayVec;
                var nVecNorm    = Vector3.Normalize(hitPointVec - hitPoint.Sphere.Center);

                var material = sphere.CalcColor(hitPointVec);

                if (!PathTracing)
                {
                    if (sphere.Brightness > 0)
                    {
                        rgb += material * sphere.Brightness;
                    }

                    if (Reflection)
                    {
                        // Reflection
                        if (!sphere.IsWall && /* ignore walls */
                            reflectionLimit > 0)
                        {
                            var reflectionMaterial = Conversions.FromColor(Colors.White);

                            var refVecNorm      = Vector3.Normalize(rayVecNorm - 2 * Vector3.Dot(nVecNorm, rayVecNorm) * nVecNorm);
                            var refVec2         = refVecNorm + (Vector3.One - refVecNorm) * (float)Math.Pow((1 - Vector3.Dot(nVecNorm, refVecNorm)), 5);
                            var reflectionRay   = new Ray(hitPointVec, refVec2);
                            var reflectionColor = CalcColor(reflectionRay, 0, --reflectionLimit);
                            var reflection      = Vector3.Multiply(reflectionColor, reflectionMaterial) * 0.25f;
                            rgb += reflection;
                        }
                    }

                    for (int i = 0; i < _lightSources.Length; i++)
                    {
                        var lightSource = _lightSources[i];

                        var lVec     = lightSource.Center - hitPointVec;
                        var lVecNorm = Vector3.Normalize(lVec);

                        var light_cos = Vector3.Dot(nVecNorm, lVecNorm);

                        if (light_cos >= 0)
                        {
                            var light = Conversions.FromColor(lightSource.Color);

                            if (Shadows)
                            {
                                if (SoftShadows)
                                {
                                    light_cos *= GetShadowedRatio(hitPointVec, lightSource);
                                }
                                else
                                {
                                    // Shadows
                                    if (HasObjectInFrontOfLightSource(hitPointVec, lightSource.Center))
                                    {
                                        light_cos *= 0.01f;
                                    }
                                }
                            }

                            if (DiffuseLambert)
                            {
                                // Diffuse "Lambert"
                                var diffuse = Vector3.Multiply(light, material) * light_cos;
                                rgb += diffuse;
                            }

                            if (SpecularPhong)
                            {
                                // Ignore walls
                                if (!sphere.IsWall)
                                {
                                    // Specular "Phong"
                                    var sVec     = (lVec - ((Vector3.Dot(lVec, nVecNorm)) * nVecNorm));
                                    var rVec     = lVec - (2 * sVec);
                                    var specular = light * (float)Math.Pow((Vector3.Dot(Vector3.Normalize(rVec), rayVecNorm)), SpecularPhongFactor);
                                    rgb += specular;
                                }
                            }
                        }
                    }
                }
                else
                {
                    if (pathTracingLimit > 0)
                    {
                        var numberOfRandomRays = 0;

                        bool isBounced = pathTracingLimit < PathTracingMaxBounces;

                        if (!isBounced)
                        {
                            numberOfRandomRays = PathTracingRays;
                        }
                        else
                        {
                            var russianRoulette = (float)_random.NextDouble();

                            if (russianRoulette > 0.2f)
                            {
                                numberOfRandomRays = 1;
                            }
                        }

                        if (numberOfRandomRays > 0)
                        {
                            // Source: https://www.scratchapixel.com/lessons/3d-basic-rendering/global-illumination-path-tracing/global-illumination-path-tracing-practical-implementation

                            Vector3 indirectDiffuse = Vector3.Zero;

                            Vector3 ntVec = Vector3.Zero;

                            if (Math.Abs(nVecNorm.X) > Math.Abs(nVecNorm.Y))
                            {
                                ntVec = (new Vector3(nVecNorm.Z, 0, -nVecNorm.X) / (float)Math.Sqrt(nVecNorm.X * nVecNorm.X + nVecNorm.Z * nVecNorm.Z));
                            }
                            else
                            {
                                ntVec = (new Vector3(0, -nVecNorm.Z, nVecNorm.Y) / (float)Math.Sqrt(nVecNorm.Y * nVecNorm.Y + nVecNorm.Z * nVecNorm.Z));
                            }

                            var nbVec = Vector3.Cross(nVecNorm, ntVec);

                            for (int i = 0; i < numberOfRandomRays; i++)
                            {
                                var cosTheta = (float)_random.NextDouble();
                                var phi      = (float)(_random.NextDouble() * 2 * Math.PI);

                                var   sinTheta = (float)Math.Sqrt(1 - cosTheta * cosTheta);
                                float x        = sinTheta * (float)Math.Cos(phi);
                                float z        = sinTheta * (float)Math.Sin(phi);

                                var sampleLocalVec = new Vector3(x, cosTheta, z);
                                var sampleWorldVec = new Vector3()
                                {
                                    X = sampleLocalVec.X * nbVec.X + sampleLocalVec.Y * nVecNorm.X + sampleLocalVec.Z * ntVec.X,
                                    Y = sampleLocalVec.X * nbVec.Y + sampleLocalVec.Y * nVecNorm.Y + sampleLocalVec.Z * ntVec.Y,
                                    Z = sampleLocalVec.X * nbVec.Z + sampleLocalVec.Y * nVecNorm.Z + sampleLocalVec.Z * ntVec.Z
                                };

                                var randomRay = new Ray(hitPointVec, sampleWorldVec);
                                indirectDiffuse += CalcColor(randomRay, pathTracingLimit - 1, 0) * cosTheta;
                            }

                            indirectDiffuse /= numberOfRandomRays * _pdf;

                            rgb += Vector3.Multiply(indirectDiffuse, material) * sphere.Reflectiveness;
                        }
                        else
                        {
                            rgb += material * sphere.Brightness;
                        }
                    }
                    else
                    {
                        rgb += material * sphere.Brightness;
                    }
                }
            }

            return(rgb);
        }
示例#2
0
        public ImageSource GetImage()
        {
            //** clear buffers

            Array.Clear(_rgbArray, 0, _rgbArray.Length);

            for (int x = 0; x < _screenWidth; x++)
            {
                for (int y = 0; y < _screenHeight; y++)
                {
                    _zBufferArray[x, y] = float.PositiveInfinity;
                }
            }

            if (_useDeferredRenderer)
            {
                //** first pass (Z-Prepass)

                for (int i = 0; i < _triangles.Length; i++)
                {
                    var triangle = _triangles[i];

                    if (!triangle.IsBackfacing)
                    {
                        for (int x = (int)Math.Min(0, Math.Max(triangle.MinScreenX, 0)); x < (int)Math.Min(Math.Max(triangle.MaxScreenX, 0), _screenWidth); x++)
                        {
                            for (int y = (int)Math.Min(0, Math.Max(triangle.MinScreenY, 0)); y < (int)Math.Min(Math.Max(triangle.MaxScreenY, 0), _screenHeight); y++)
                            {
                                var z = triangle.CalcZ(x, y);

                                if (!float.IsInfinity(z) &&
                                    !float.IsNaN(z) &&
                                    z < _zBufferArray[x, y])
                                {
                                    _zBufferArray[x, y] = z;
                                }
                            }
                        }
                    }
                }

                //** second pass

                for (int i = 0; i < _triangles.Length; i++)
                {
                    var triangle = _triangles[i];

                    if (!triangle.IsBackfacing)
                    {
                        for (int x = (int)Math.Min(0, Math.Max(triangle.MinScreenX, 0)); x < (int)Math.Min(Math.Max(triangle.MaxScreenX, 0), _screenWidth); x++)
                        {
                            for (int y = (int)Math.Min(0, Math.Max(triangle.MinScreenY, 0)); y < (int)Math.Min(Math.Max(triangle.MaxScreenY, 0), _screenHeight); y++)
                            {
                                float z = _zBufferArray[x, y];

                                if (_visualizeZBuffer)
                                {
                                    var zcolor = (int)((z - ZBUFFER_VISUALIZE_MIN) / (ZBUFFER_VISUALIZE_MAX - ZBUFFER_VISUALIZE_MIN) * 255) * new Vector3(1f / 255, 1f / 255, 1f / 255);
                                    _rgbArray[x, y] = zcolor;
                                }
                                else
                                {
                                    var rgb = Vector3.Zero;
                                    if (triangle.CalcColorDeferred(x, y, _lightSources, z, out rgb))
                                    {
                                        _rgbArray[x, y] = rgb;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                for (int i = 0; i < _triangles.Length; i++)
                {
                    var triangle = _triangles[i];

                    if (!triangle.IsBackfacing)
                    {
                        for (int x = (int)Math.Min(0, Math.Max(triangle.MinScreenX, 0)); x < (int)Math.Min(Math.Max(triangle.MaxScreenX, 0), _screenWidth); x++)
                        {
                            for (int y = (int)Math.Min(0, Math.Max(triangle.MinScreenY, 0)); y < (int)Math.Min(Math.Max(triangle.MaxScreenY, 0), _screenHeight); y++)
                            {
                                float   z;
                                Vector3 rgb;

                                if (triangle.CalcColor(x, y, _lightSources, out z, out rgb) &&
                                    !float.IsInfinity(z) &&
                                    !float.IsNaN(z))
                                {
                                    if (z < _zBufferArray[x, y])
                                    {
                                        _zBufferArray[x, y] = z;

                                        if (_visualizeZBuffer)
                                        {
                                            var zcolor = (int)((z - ZBUFFER_VISUALIZE_MIN) / (ZBUFFER_VISUALIZE_MAX - ZBUFFER_VISUALIZE_MIN) * 255) * new Vector3(1f / 255, 1f / 255, 1f / 255);
                                            _rgbArray[x, y] = zcolor;
                                        }
                                        else
                                        {
                                            _rgbArray[x, y] = rgb;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            //** buffer to image

            for (int x = 0; x < _screenWidth; x++)
            {
                for (int y = 0; y < _screenHeight; y++)
                {
                    var rgb = _rgbArray[x, y];
                    var c   = Conversions.FromRGB(rgb, _gammaCorrect);
                    _bitmap.Set(x, y, c);
                }
            }

            return(_bitmap.GetImageSource());
        }
示例#3
0
        public string GetImageFileName(int width, int height, double dpiX, double dpiY, CancellationToken cancellationToken, string settingsSummary, string exportDirectory)
        {
            var sw = Stopwatch.StartNew();

            OutputLogEveryXPixel = (width * height / 100);

            if (AccelerationStructure)
            {
                _accelerationStructure = BVHNode.BuildTopDown(_spheres, _logger);
            }

            var bitmap = new BitmapImage(width, height, dpiX, dpiY);

            var divideX = width / (float)2;
            var alignX  = 1 - divideX;

            var divideY = height / (float)2;
            var alignY  = 1 - divideY;

            int workDone  = 0;
            int totalWork = width * height;

            if (Parallelize)
            {
                var options = new ParallelOptions()
                {
                    CancellationToken = cancellationToken, MaxDegreeOfParallelism = Environment.ProcessorCount
                };

                Parallel.For(0, width, options, i =>
                {
                    for (int j = 0; j < height; j++)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            break;
                        }

                        if (AntiAliasing)
                        {
                            Vector3 result = Vector3.Zero;
                            for (int k = 0; k < AntiAliasingSampleSize; k++)
                            {
                                var dx = (float)_random.NextGaussian(0d, 0.5d);
                                var dy = (float)_random.NextGaussian(0d, 0.5d);

                                var x   = (i + alignX + dx) / divideX;
                                var y   = ((height - j) + alignY + dy) / divideY;
                                var rgb = GetColor(x, y);
                                result += rgb;
                            }

                            var avg_rgb = result * (1f / AntiAliasingSampleSize);
                            var c       = Conversions.FromRGB(avg_rgb, GammaCorrect);
                            bitmap.Set(i, j, c);
                        }
                        else
                        {
                            var x   = (i + alignX) / divideX;
                            var y   = ((height - j) + alignY) / divideY;
                            var rgb = GetColor(x, y);
                            var c   = Conversions.FromRGB(rgb, GammaCorrect);
                            bitmap.Set(i, j, c);
                        }

                        var value = Interlocked.Increment(ref workDone);

                        if (value % OutputLogEveryXPixel == 0)
                        {
                            var progress = (float)value / totalWork;
                            WriteOutput($"{(progress * 100):F3}% progress. Running {sw.Elapsed}. Remaining {TimeSpan.FromMilliseconds(sw.Elapsed.TotalMilliseconds / progress * (1f - progress))}.");
                        }
                    }
                });
            }
            else
            {
                for (int i = 0; i < width; i++)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        break;
                    }

                    for (int j = 0; j < height; j++)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            break;
                        }

                        if (AntiAliasing)
                        {
                            Vector3 result = Vector3.Zero;
                            for (int k = 0; k < AntiAliasingSampleSize; k++)
                            {
                                var x   = (i + alignX + (float)_random.NextGaussian(0d, 0.5d)) / divideX;
                                var y   = ((height - j) + alignY + (float)_random.NextGaussian(0d, 0.5d)) / divideY;
                                var rgb = GetColor(x, y);
                                result += rgb;
                            }

                            var avg_rgb = result * (1f / AntiAliasingSampleSize);
                            var c       = Conversions.FromRGB(avg_rgb, GammaCorrect);
                            bitmap.Set(i, j, c);
                        }
                        else
                        {
                            // Question: Why is it mirrored?
                            var x   = (i + alignX) / divideX;
                            var y   = ((height - j) + alignY) / divideY;
                            var rgb = GetColor(x, y);
                            var c   = Conversions.FromRGB(rgb, GammaCorrect);
                            bitmap.Set(i, j, c);
                        }

                        ++workDone;

                        if (workDone % OutputLogEveryXPixel == 0)
                        {
                            var progress = (float)workDone / totalWork;
                            WriteOutput($"{(progress * 100):F3}% progress. Running {sw.Elapsed}. Remaining {TimeSpan.FromMilliseconds(sw.Elapsed.TotalMilliseconds / progress * (1f - progress))}.");
                        }
                    }
                }
            }

            if (cancellationToken.IsCancellationRequested)
            {
                WriteOutput("Operation canceled by user.");
                return(null);
            }

            var imageSource = bitmap.GetImageSource();

            sw.Stop();

            return(SaveImage(imageSource, sw.Elapsed, settingsSummary, exportDirectory));
        }