-
Notifications
You must be signed in to change notification settings - Fork 0
/
SchwarzschildRayProcessor.cs
205 lines (162 loc) · 6.68 KB
/
SchwarzschildRayProcessor.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
using BlackHoleRaytracer.Equation;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Threading;
namespace BlackHoleRaytracer
{
public class SchwarzschildRayProcessor
{
private int width;
private int height;
private Scene scene;
private int[] outputBitmap;
private string outputFileName;
private const int NumIterations = 10000;
private bool debug;
public SchwarzschildRayProcessor(int width, int height, Scene scene, string outputFileName)
: this(width, height, scene, outputFileName, true)
{ }
public SchwarzschildRayProcessor(int width, int height, Scene scene, string outputFileName, bool debug)
{
this.width = width;
this.height = height;
this.scene = scene;
this.outputFileName = outputFileName;
this.debug = debug;
}
public void Process()
{
// Create main bitmap for writing pixels
int bufferLength = width * height;
outputBitmap = new int[bufferLength];
int numThreads = Environment.ProcessorCount - 1;
DateTime startTime = DateTime.Now;
Log("Launching {0} threads...", numThreads);
var lineLists = new List<List<int>>();
var paramList = new List<ThreadParams>();
for (int i = 0; i < numThreads; i++)
{
var lineList = new List<int>();
lineLists.Add(lineList);
paramList.Add(new ThreadParams()
{
JobId = i,
LinesList = lineList,
Equation = new SchwarzschildBlackHoleEquation(scene.SchwarzschildEquation),
Thread = new Thread(new ParameterizedThreadStart(RayTraceThread)),
});
}
for (int j = 0; j < height; j++)
{
lineLists[j % numThreads].Add(j);
}
foreach (var param in paramList)
{
param.Thread.Start(param);
}
foreach (var param in paramList)
{
param.Thread.Join();
}
GCHandle gcHandle = GCHandle.Alloc(outputBitmap, GCHandleType.Pinned);
Bitmap resultBmp = new Bitmap(width, height, width * 4, PixelFormat.Format32bppArgb, gcHandle.AddrOfPinnedObject());
resultBmp.Save(outputFileName, ImageFormat.Png);
if (resultBmp != null) { resultBmp.Dispose(); resultBmp = null; }
if (gcHandle.IsAllocated) { gcHandle.Free(); }
Log("Finished in {0} seconds.", (DateTime.Now - startTime).TotalSeconds);
}
public void RayTraceThread(object threadParams)
{
var param = (ThreadParams)threadParams;
Log("Starting thread {0}...", param.JobId);
float tanFov = (float)Math.Tan((Math.PI / 180.0) * scene.Fov);
var front = Vector3.Normalize(scene.CameraLookAt - scene.CameraPosition);
var left = Vector3.Normalize(Vector3.Cross(scene.UpVector, front));
var nUp = Vector3.Cross(front, left);
var viewMatrix = new Matrix4x4(left.X, left.Y, left.Z, 0,
nUp.X, nUp.Y, nUp.Z, 0,
front.X, front.Y, front.Z, 0,
0, 0, 0, 0);
bool debug = false;
Color color;
int x, yOffset;
Vector3 point, prevPoint;
double sqrNorm, prevSqrNorm;
bool stop = false;
try
{
foreach (int y in param.LinesList)
{
yOffset = y * width;
for (x = 0; x < width; x++)
{
color = Color.Transparent;
var view = new Vector3(((float)x / width - 0.5f) * tanFov,
((-(float)y / height + 0.5f) * height / width) * tanFov,
1f);
view = Vector3.Transform(view, viewMatrix);
var velocity = Vector3.Normalize(view);
point = scene.CameraPosition;
sqrNorm = point.LengthSquared();
param.Equation.SetInitialConditions(ref point, ref velocity);
for (int iter = 0; iter < NumIterations; iter++)
{
prevPoint = point;
prevSqrNorm = sqrNorm;
sqrNorm = param.Equation.Function(ref point, ref velocity);
// Check if the ray hits anything
foreach (var hitable in scene.hitables)
{
stop = false;
if (hitable.Hit(ref point, sqrNorm, ref prevPoint, prevSqrNorm, ref velocity, param.Equation, ref color, ref stop, debug))
{
if (stop)
{
// The ray has found its stopping point (or rather its starting point).
break;
}
}
}
if (stop)
{
break;
}
}
outputBitmap[yOffset + x] = color.ToArgb();
}
Log("Thread {0}: Line {1} rendered.", param.JobId, y);
}
}
catch (Exception e)
{
Log("Thread {0} error: {1}", param.JobId, e.Message);
}
Log("Thread {0} finished.", param.JobId);
}
private void Log(string message, object arg0)
{
if (debug)
{
Console.WriteLine(message, arg0);
}
}
private void Log(string message, object arg0, object arg1)
{
if (debug)
{
Console.WriteLine(message, arg0, arg1);
}
}
}
class ThreadParams
{
public int JobId { get; set; }
public List<int> LinesList { get; set; }
public SchwarzschildBlackHoleEquation Equation { get; set; }
public Thread Thread { get; set; }
}
}