/// <summary> /// When called will trace the scene from the given camera location. /// Width * height must equal the length of the color array. /// Each pixel in the color array will be traced. The convention is X lines first then y lines (accessed via tracingTarget[x + y * width]). /// </summary> /// <param name="scene"></param> /// <param name="camera"></param> /// <param name="options"></param> /// <param name="token">Optional cancellation token. If cancelled the method will return false.</param> /// <returns>True if the scene was fully traced, false otherwise.</returns>> public virtual bool TraceScene(Scene scene, Camera camera, TracingOptions options, CancellationToken?token = null) { var width = options.Width; var height = options.Height; var rayCountX = width - 1; var rayCountY = height - 1; var tracingTarget = options.TracingTarget; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (token?.IsCancellationRequested ?? false) { return(false); } Vector3 averageColor = Vector3.Zero; for (int i = 0; i < options.SampleCount; i++) { var ray = camera.GetRayForRasterPosition(x, y, rayCountX, rayCountY); var cv = GetColorVectorForRay(scene, ray, 0, i); cv = Vector3.Clamp(cv, Vector3.Zero, Vector3.One); averageColor += cv; } averageColor /= options.SampleCount; tracingTarget[x + y * width] = new Color(averageColor); } } return(true); }
protected override void Initialize() { base.Initialize(); SetupScene(); IsMouseVisible = false; // instead of creating a texture of the size width * height where we then end up filling Raster*Raster sized blocks with the same pixeldata // we create a smaller buffer and scale it when rendering var primaryWidth = _options.Width / _options.RealtimeRasterLevel; var primaryHeight = _options.Height / _options.RealtimeRasterLevel; var pixels = new Color[primaryWidth * primaryHeight]; _primaryTracingOptions = new TracingOptions(primaryWidth, primaryHeight, pixels, _options.RealtimeSampleCount); if (_options.BackgroundRasterLevel < _options.RealtimeRasterLevel) { // only enable background rendering if it is more accurate than realtime rendering, otherwise disable it by leaving the backgroundTracingOptions null var backgroundWidth = _options.Width / _options.BackgroundRasterLevel; var backgroundHeight = _options.Width / _options.BackgroundRasterLevel; var secondBuffer = new Color[backgroundWidth * backgroundHeight]; _backgroundTracingOptions = new TracingOptions(backgroundWidth, backgroundHeight, secondBuffer, _options.BackgroundSampleCount); _backgroundScene = new Texture2D(GraphicsDevice, backgroundWidth, backgroundHeight); } _primaryScene = new Texture2D(GraphicsDevice, primaryWidth, primaryHeight); // we have 2 distinct textures: primary and background // primary is usually smaller (for real time rendering) // for rendering we always use _renderReference which just points to one of the 2 other textures // start of with the primary scene _renderReference = _primaryScene; _spriteBatch = new SpriteBatch(GraphicsDevice); _sceneChanged = true; _watch = new Stopwatch(); }
public override bool TraceScene(Scene scene, Camera camera, TracingOptions options, CancellationToken?token = null) { // using Parallel.For to automatically get multithreading var width = options.Width; var height = options.Height; var rayCountX = width - 1; var rayCountY = height - 1; var tracingTarget = options.TracingTarget; int range = options.Width * options.Height; // divided by 2 because we usually have foreground + background raytracer running at the same time, if we don't then each will spawn as many threads as we have cores which will cause additional lag // due to too many thread context switches var pi = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount / 2 }; var r = Parallel.For(0, range, pi, (i, loopState) => { int x = i / options.Width; int y = i % options.Height; if (loopState.ShouldExitCurrentIteration) { return; } if (token?.IsCancellationRequested ?? false) { loopState.Stop(); return; } Vector3 averageColor = Vector3.Zero; for (int sample = 0; sample < options.SampleCount; sample++) { var ray = camera.GetRayForRasterPosition(x, y, rayCountX, rayCountY); var cv = GetColorVectorForRay(scene, ray, 0, sample); cv = Vector3.Clamp(cv, Vector3.Zero, Vector3.One); averageColor += cv; } averageColor /= options.SampleCount; tracingTarget[x + y * width] = new Color(averageColor); }); return(r.IsCompleted); }