/// <summary> /// Renders a frame for the current world across a given number of threads /// </summary> /// <param name="world">Current world with camera</param> /// <param name="numThreads">Number of threads to split workload across</param> public virtual void RenderSceneMultithreaded(World world, int numThreads) { // local ref for viewplane for minimized member accesses ViewPlane vp = world.CurrentViewPlane; // set zoom for frame zero to ensure animation does not cause increasing zoom if(GlobalVars.frameno == 0) vp.PixelSize /= _zoom; // list of all active threads, for spinwait List<RenderFragmentParameters> threads = new List<RenderFragmentParameters>(); // concurrent queue for thread safe dequeue of render chunks taskQueue = new ConcurrentQueue<RenderFragmentParameters>(); //Find out how many chunks the screen can be divided into; int hChunks = (int)Math.Floor((decimal)vp.HorizontalResolution / (decimal)GlobalVars.FRAGMENT_SIZE); int vChunks = (int)Math.Floor((decimal)vp.VerticalResolution / (decimal)GlobalVars.FRAGMENT_SIZE); //Find out the size of the boundaries which do not fit into chunks int hRemaining = vp.HorizontalResolution % GlobalVars.FRAGMENT_SIZE; int vRemaining = vp.VerticalResolution % GlobalVars.FRAGMENT_SIZE; int threadNo = 0; //Label for each thread created //Queue up render chunks int hor0, hor1, vert0, vert1; RenderFragmentParameters parameters; for(int v = 0; v < vChunks; v++) { for(int h = 0; h < hChunks; h++) { //Queue up threads hor0 = GlobalVars.FRAGMENT_SIZE * h; hor1 = GlobalVars.FRAGMENT_SIZE * (h + 1); vert0 = GlobalVars.FRAGMENT_SIZE * v; vert1 = GlobalVars.FRAGMENT_SIZE * (v + 1); parameters = new RenderFragmentParameters(world, hor0, hor1, vert0, vert1, threadNo); threads.Add(parameters); taskQueue.Enqueue(parameters); threadNo++; } //Add in additional fragments for the right screen edge if resolution isn't nicely //divisible if(hRemaining != 0) { hor0 = GlobalVars.FRAGMENT_SIZE * hChunks; hor1 = vp.HorizontalResolution; vert0 = GlobalVars.FRAGMENT_SIZE * v; vert1 = GlobalVars.FRAGMENT_SIZE * (v + 1); parameters = new RenderFragmentParameters(world, hor0, hor1, vert0, vert1, threadNo); threads.Add(parameters); taskQueue.Enqueue(parameters); } } //Add in additional fragments for the top edge if resolution isn't nicely divisible if(vRemaining != 0) { for(int h = 0; h < hChunks; h++) { hor0 = GlobalVars.FRAGMENT_SIZE * h; hor1 = GlobalVars.FRAGMENT_SIZE * (h + 1); vert0 = GlobalVars.FRAGMENT_SIZE * vChunks; vert1 = vp.VerticalResolution; parameters = new RenderFragmentParameters(world, hor0, hor1, vert0, vert1, threadNo); threads.Add(parameters); taskQueue.Enqueue(parameters); } //Add in corner edge fragment if the right edge of the screen wasn't nicely divisible if (hRemaining != 0) { hor0 = GlobalVars.FRAGMENT_SIZE * hChunks; hor1 = vp.HorizontalResolution; vert0 = GlobalVars.FRAGMENT_SIZE * vChunks; vert1 = vp.VerticalResolution; parameters = new RenderFragmentParameters(world, hor0, hor1, vert0, vert1, threadNo); threads.Add(parameters); taskQueue.Enqueue(parameters); } } // dequeue either the provided number of threads, or the total number of threads, whichever is smaller int numThreadsToDequeue = (numThreads < threadNo) ? numThreads : threadNo; // dequeue the initial thread count of threads RenderFragmentParameters[] initialThreads = new RenderFragmentParameters[numThreadsToDequeue]; for (int i = 0; i < numThreadsToDequeue; i++) { while(!taskQueue.TryDequeue(out initialThreads[i])) { // ensure every dequeue succeeds } } // begin rendering in all dequeued threads foreach(RenderFragmentParameters renderFragment in initialThreads) { renderFragment.Begin(); } //Set the thread priority for main thread to low Thread.CurrentThread.Priority = ThreadPriority.Lowest; //Spinwait for all threads bool allDone = false; do { allDone = true; foreach (RenderFragmentParameters thread in threads) { if (thread.thread.IsAlive) { allDone = false; } } world.PollEvents(); } while (!allDone); // update liveimage world.LiveView.LiveImage = new SFML.Graphics.Image((uint)vp.HorizontalResolution, (uint)vp.VerticalResolution, world.RenderImage); }
/// <summary> /// Renders the world on a single thread [deprecated, use RenderSceneMultiThreaded] /// </summary> /// <param name="world">World reference</param> public override void RenderScene(World worldRef) { RGBColor lightingSum; ViewPlane vp = worldRef.CurrentViewPlane; Ray ray = new Ray(_eye,new Vect3D(0,0,0)); int depth = 0; //Depth of recursion Point2D sp = new Point2D(); //Sample point on a unit square Point2D pp = new Point2D(); ; //Sample point translated into screen space worldRef.OpenWindow(vp.HorizontalResolution, vp.VerticalResolution); vp.PixelSize /= _zoom; for(int row = 0; row < vp.VerticalResolution; row++) { for(int column = 0; column < vp.HorizontalResolution; column++) { lightingSum = GlobalVars.COLOR_BLACK; //Start with no color, everything is additive for(int sample = 0; sample < vp.NumSamples; sample ++) { sp = worldRef.CurrentViewPlane.ViewPlaneSampler.SampleUnitSquare(); pp.coords.X = worldRef.CurrentViewPlane.PixelSize * (column - 0.5f * vp.HorizontalResolution + sp.coords.X); pp.coords.Y = worldRef.CurrentViewPlane.PixelSize * (row - 0.5f * vp.VerticalResolution + sp.coords.Y); ray.Direction = GetRayDirection(pp); lightingSum = lightingSum + worldRef.CurrentTracer.TraceRay(ray, depth); } lightingSum /= vp.NumSamples; lightingSum *= _exposureTime; worldRef.DisplayPixel(row, column, lightingSum); //Poll events in live render view worldRef.PollEvents(); } } }
public override void RenderScene(World w) { RGBColor L = new RGBColor(); Ray ray = new Ray(); ViewPlane vp = w.CurrentViewPlane; int depth = 0; Point2D sp = new Point2D(); Point2D pp = new Point2D(); Point2D dp = new Point2D(); Point2D lp = new Point2D(); //w.open_window(vp.hres, vp.vres); vp.PixelSize /= _zoom; for(int r = 0; r < vp.VerticalResolution-1; r++) { for(int c = 0; c < vp.HorizontalResolution-1; c++) { L = GlobalVars.COLOR_BLACK; for(int n = 0; n < vp.NumSamples; n++) { //Sample on unit square sp = vp.ViewPlaneSampler.SampleUnitSquare(); //Sample in screenspace pp.coords.X = vp.PixelSize * (c - vp.HorizontalResolution * 0.5f + sp.coords.X); pp.coords.Y = vp.PixelSize * (r - vp.VerticalResolution * 0.5f + sp.coords.Y); dp = depthSampler.SampleDisk(); lp.coords.X = dp.coords.X * radius; lp.coords.Y = dp.coords.Y * radius; ray.Origin = _eye + lp.coords.X * u + lp.coords.Y * v; ray.Direction = GetRayDirection(pp, lp); L += w.CurrentTracer.TraceRay(ray, depth); } L /= vp.NumSamples; L *= _exposureTime; w.DisplayPixel(r, c, L); w.PollEvents(); } } }
static void Main(string[] args) { bool multithread = false; int threads = 2; int a = 0; int numArgs = args.Length; if(numArgs == 0) { Console.WriteLine("Usage: scsraytracer -I \"Input XML path\" -O \"Output bmp path\""); Console.WriteLine("Additional options:\n-V: Verbose output, default off\n-T #: Number of threads"); Console.ReadKey(); return; } try { //Cycle through all arguments while (a < numArgs) { string arg = args[a]; if (arg.Equals("-I") && GlobalVars.inFile == null) { if ((a + 1 < numArgs) && File.Exists(args[a+1])) { GlobalVars.inFile = args[a + 1]; a += 2; } else { throw new ArgumentException("Invalid input file location"); } } else if (arg.Equals("-O") && GlobalVars.outFile == null) { if ((a + 1 < numArgs)) { GlobalVars.outFile = args[a + 1]; a += 2; } else { throw new ArgumentException("Invalid output file location"); } } else if (arg.Equals("-V")) { GlobalVars.verbose = true; a++; } else if (arg.Equals("-T")) { multithread = true; threads = Convert.ToInt32(args[a + 1]); a += 2; } } if(GlobalVars.outFile == null) { throw new ArgumentException(); } if(File.Exists(GlobalVars.outFile)) { GetUserInput: Console.Write("File " + GlobalVars.outFile + " exists! Overwrite? (y/n): "); switch(Console.ReadKey().KeyChar) { case 'y': //do nothing break; case 'n': Console.WriteLine("\nOk, exiting now."); return; default: Console.Write("\n"); goto GetUserInput; } } } catch (ArgumentException e) { Console.WriteLine(e.ToString()); Console.WriteLine("Usage: scsraytracer -I \"Input XML path\" -O \"Output bmp path\""); Console.WriteLine("Additional options:\n-V: Verbose output, default off\n-T #: Number of threads"); Console.ReadKey(); return; } //Elevate process priority to high using (Process p = Process.GetCurrentProcess()) p.PriorityClass = ProcessPriorityClass.High; World w = new World(); GlobalVars.WORLD_REF = w; w.Build(); w.OpenWindow(w.CurrentViewPlane.HorizontalResolution, w.CurrentViewPlane.VerticalResolution); //GlobalVars.frameno = 120; //w.Animate(); //while (GlobalVars.frameno < 130) //{ //w.camera.setEye(new Point3D(200, 200, GlobalVars.cam_zcoord)); // w.camera.setLookat(new Point3D(0, 0, GlobalVars.lookat_zcoord)); // w.camera.compute_uvw(); switch (multithread) { case false: w.Camera.RenderSceneMultithreaded(w, 1); break; case true: w.Camera.RenderSceneMultithreaded(w, threads); break; } w.SaveDisplayedImage(GlobalVars.outFile); //w.SaveDisplayedImage("E:\\weird\\frame_" + GlobalVars.frameno + ".bmp"); // GlobalVars.cam_zcoord -= 10; // GlobalVars.lookat_zcoord -= 10; // GlobalVars.frameno += 1; //GlobalVars.frameno+=1; //w.Animate(); //} while(!GlobalVars.should_close) { w.PollEvents(); } }