Advertisement
demonixis

MT-Bugged-Raytracer.cs

Apr 27th, 2018
290
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.04 KB | None | 0 0
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using MonoGameRayTracer.Utils;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Threading;
  8.  
  9. namespace MonoGameRayTracer
  10. {
  11.     public class RayTracer : IDisposable
  12.     {
  13.         struct ThreadData
  14.         {
  15.             public int StartX;
  16.             public int StartY;
  17.             public int EndX;
  18.             public int EndY;
  19.         }
  20.  
  21.         private Texture2D m_backBufferTexture;
  22.         private Color[] m_BackBuffer;
  23.         private Thread m_Thread;
  24.         private Stopwatch m_Stopwatch;
  25.         private int m_RenderWidth;
  26.         private int m_RenderHeight;
  27.         private int m_ThreadSleepTime = 10;
  28.         private int m_MaxDepth = 50;
  29.         private int m_Step = 1;
  30.         private float m_LastFrameTime = 0.0f;
  31.         private float m_Scale = 1.0f;
  32.         private bool m_ThreadRunning = false;
  33.         private int m_CountDone;
  34.         private Camera m_Camera;
  35.         private Hitable m_World;
  36.         private List<ThreadData> m_ThreadData = new List<ThreadData>();
  37.  
  38.         public int MaxDepth
  39.         {
  40.             get => m_MaxDepth;
  41.             set
  42.             {
  43.                 m_MaxDepth = value;
  44.                 if (m_MaxDepth < 1)
  45.                     m_MaxDepth = 1;
  46.             }
  47.         }
  48.  
  49.         public int Step
  50.         {
  51.             get => m_Step;
  52.             set
  53.             {
  54.                 m_Step = value;
  55.                 if (m_Step < 1)
  56.                     m_Step = 1;
  57.             }
  58.         }
  59.  
  60.         public float FrameTime => m_LastFrameTime;
  61.  
  62.         public bool ThreadRunning => m_ThreadRunning;
  63.  
  64.         public float Scale => m_Scale;
  65.  
  66.         public RayTracer(GraphicsDevice device, float multiplier)
  67.         {
  68.             var screenWidth = device.PresentationParameters.BackBufferWidth;
  69.             var screenHeight = device.PresentationParameters.BackBufferHeight;
  70.  
  71.             m_RenderWidth = (int)(screenWidth * multiplier);
  72.             m_RenderHeight = (int)(screenHeight * multiplier);
  73.  
  74.             m_backBufferTexture = new Texture2D(device, m_RenderWidth, m_RenderHeight, false, SurfaceFormat.Color);
  75.             m_BackBuffer = new Color[m_RenderWidth * m_RenderHeight];
  76.  
  77.             m_Scale = multiplier;
  78.             m_Stopwatch = new Stopwatch();
  79.         }
  80.  
  81.         private Vector3 GetColor(Ray ray, Hitable world, int depth) => GetColor(ref ray, world, depth);
  82.  
  83.         private Vector3 GetColor(ref Ray ray, Hitable world, int depth)
  84.         {
  85.             HitRecord record = new HitRecord();
  86.             if (world.Hit(ref ray, 0.001f, float.MaxValue, ref record))
  87.             {
  88.                 var scattered = new Ray();
  89.                 var attenuation = Vector3.Zero;
  90.  
  91.                 if (record.Material != null)
  92.                 {
  93.                     if (depth < m_MaxDepth && record.Material.Scatter(ref ray, ref record, ref attenuation, ref scattered))
  94.                         return attenuation * GetColor(ref scattered, world, depth + 1);
  95.                     else
  96.                         return Vector3.Zero;
  97.                 }
  98.  
  99.                 var target = record.P + record.Normal + Mathf.RandomInUnitySphere();
  100.                 return 0.5f * GetColor(new Ray(record.P, target - record.P), world, depth);
  101.             }
  102.  
  103.             var unitDirection = Mathf.UnitVector(ray.Direction);
  104.             var t = 0.5f * (unitDirection.Y + 1.0f);
  105.             return (1.0f - t) * Vector3.One + t * new Vector3(0.5f, 0.7f, 1.0f);
  106.         }
  107.  
  108.         public void StartThreadedRenderLoop(Camera camera, Hitable world)
  109.         {
  110.             if (m_ThreadRunning)
  111.                 throw new Exception("The Thread is still running");
  112.  
  113.             m_ThreadRunning = true;
  114.  
  115.             m_Thread = new Thread(() =>
  116.             {
  117.                 while (m_ThreadRunning)
  118.                 {
  119.                     Render(camera, world);
  120.                     Thread.Sleep(m_ThreadSleepTime);
  121.                 }
  122.             });
  123.  
  124.             m_Thread.Start();
  125.         }
  126.  
  127.         public void StopThreadedRenderingLoop()
  128.         {
  129.             if (m_Thread != null && m_Thread.IsAlive)
  130.             {
  131.                 m_ThreadRunning = false;
  132.                 m_Thread.Abort();
  133.             }
  134.         }
  135.  
  136.         public void StartMultiThreadedRenderLoop(Camera camera, Hitable world)
  137.         {
  138.             if (m_ThreadRunning)
  139.                 throw new Exception("The Thread is still running");
  140.  
  141.             m_ThreadRunning = true;
  142.  
  143.             var threadCount = 4;
  144.             var threadCountPerTwo = threadCount / 2;
  145.             var chunckX = m_RenderWidth / threadCount;
  146.             var chunckY = m_RenderHeight / threadCount;
  147.  
  148.             m_Camera = camera;
  149.             m_World = world;
  150.  
  151.             var callbacks = new WaitCallback[threadCount];
  152.             for (var i = 0; i < threadCount; i++)
  153.             {
  154.                 callbacks[i] = new WaitCallback(UpdatePixels);
  155.  
  156.                 m_ThreadData.Add(new ThreadData()
  157.                 {
  158.                     StartX = i * chunckX,
  159.                     EndX = i * chunckX + chunckX,
  160.                     StartY = i * chunckY,
  161.                     EndY = i * chunckY + chunckY
  162.                 });
  163.             }
  164.  
  165.             m_Thread = new Thread(() =>
  166.             {
  167.                 while (m_ThreadRunning)
  168.                 {
  169.                     m_Stopwatch.Restart();
  170.  
  171.                     m_CountDone = 0;
  172.  
  173.                     for (var i = 0; i < threadCount; i++)
  174.                         ThreadPool.QueueUserWorkItem(callbacks[i], i);
  175.                    
  176.                     while (m_CountDone < threadCount)
  177.                         Thread.Sleep(10);
  178.  
  179.                     m_backBufferTexture.SetData<Color>(m_BackBuffer);
  180.  
  181.                     m_Stopwatch.Stop();
  182.                     m_LastFrameTime = m_Stopwatch.ElapsedMilliseconds;
  183.                 }
  184.             });
  185.  
  186.             m_Thread.Start();
  187.         }
  188.  
  189.         private void UpdatePixels(object obj)
  190.         {
  191.             var index = (int)obj;
  192.             var data = m_ThreadData[index];
  193.  
  194.             Console.WriteLine("Started: " + index);
  195.  
  196.             for (var j = data.StartY; j < data.EndY; j++)
  197.                 for (var i = data.StartX; i < data.EndX; i++)
  198.                     UpdatePixel(ref i, ref j, m_Camera, m_World);
  199.  
  200.             m_CountDone++;
  201.  
  202.             Console.WriteLine("Done: " + index);
  203.         }
  204.  
  205.         public void Render(Camera camera, Hitable world)
  206.         {
  207.             m_Stopwatch.Restart();
  208.  
  209.             for (var j = m_RenderHeight - 1; j >= 0; j--)
  210.                 for (var i = 0; i < m_RenderWidth; i++)
  211.                     UpdatePixel(ref i, ref j, camera, world);
  212.  
  213.             m_backBufferTexture.SetData<Color>(m_BackBuffer);
  214.  
  215.             m_Stopwatch.Stop();
  216.             m_LastFrameTime = m_Stopwatch.ElapsedMilliseconds;
  217.         }
  218.  
  219.         public void Present(SpriteBatch spriteBatch, ref Rectangle rectangle)
  220.         {
  221.             spriteBatch.Draw(m_backBufferTexture, rectangle, null, Color.White, 0, Vector2.Zero, SpriteEffects.FlipVertically, 0);
  222.         }
  223.  
  224.         private void UpdatePixel(ref int i, ref int j, Camera camera, Hitable world)
  225.         {
  226.             var random = new System.Random(DateTime.Now.Millisecond);
  227.             var color = Vector3.Zero;
  228.  
  229.             for (var s = 0; s < m_Step; s++)
  230.             {
  231.                 var u = (float)(i + (float)random.NextDouble()) / m_RenderWidth;
  232.                 var v = (float)(j + (float)random.NextDouble()) / m_RenderHeight;
  233.                 var ray = camera.GetRay(ref u, ref v);
  234.                 color += GetColor(ref ray, world, 0);
  235.             }
  236.  
  237.             color /= (float)m_Step;
  238.             color.X = Mathf.Sqrt(color.X);
  239.             color.Y = Mathf.Sqrt(color.Y);
  240.             color.Z = Mathf.Sqrt(color.Z);
  241.  
  242.             m_BackBuffer[i + j * m_RenderWidth] = new Color(color.X, color.Y, color.Z);
  243.         }
  244.  
  245.         public void Dispose()
  246.         {
  247.             StopThreadedRenderingLoop();
  248.             m_backBufferTexture.Dispose();
  249.         }
  250.     }
  251. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement