FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
JSIL/Examples/SimpleRaytracer.cs at master · juneym/JSIL · GitHub
juneym
/
JSIL
Public
forked from
sq/JSIL
Notifications
You must be signed in to change notification settings
Fork
0
Star
1
Code
Pull requests
0
Actions
Projects
Wiki
Security and quality
0
Insights
Additional navigation options
Code
Pull requests
Actions
Projects
Wiki
Security and quality
Insights
Expand file tree
Breadcrumbs
JSIL
/
Examples
/
SimpleRaytracer.cs
Copy path
More file actions
More file actions
Latest commit
History
History
History
395 lines (318 loc) · 16.9 KB
Breadcrumbs
JSIL
/
Examples
/
SimpleRaytracer.cs
Copy path
File metadata and controls
395 lines (318 loc) · 16.9 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
// Based on http://www.coldcity.com/index.php/simple-csharp-raytracer/
// Original license comment follows
/*
* simpleray
* A simple raytracer for teaching purposes
*
* IainC, 2009
* License: Do WTF you want
*
* World coord system:
* Origin (0,0,0) is the center of the screen
* X increases towards right of screen
* Y increases towards top of screen
* Z increases into screen
*
* Enough vector maths to get you through:
* - The dot product of two vectors gives the cosine of the angle between them
* - Normalisation is scaling a vector to have magnitude 1: makes it a "unit vector"
* - To get a unit direction vector from point A to point B, do B-A and normalise the result
* - To move n units along a direction vector from an origin, new position = origin + (direction * n)
* - To reflect a vector in a surface with a known surface normal:
* negativeVec = -vecToReflect;
* reflectedVec = normal * (2.0f * negativeVec.Dot(normal)) - negativeVec;
*/
using
System
;
using
System
.
Drawing
;
using
System
.
Collections
.
Generic
;
using
JSIL
.
Meta
;
namespace
simpleray
{
public
class
Vector3f
{
public
float
x
,
y
,
z
;
public
Vector3f
(
float
x
=
0
,
float
y
=
0
,
float
z
=
0
)
{
this
.
x
=
x
;
this
.
y
=
y
;
this
.
z
=
z
;
}
public
float
Dot
(
Vector3f
b
)
{
return
(
x
*
b
.
x
+
y
*
b
.
y
+
z
*
b
.
z
)
;
}
public
void
Normalise
(
)
{
float
f
=
(
float
)
(
1.0f
/
Math
.
Sqrt
(
this
.
Dot
(
this
)
)
)
;
x
*=
f
;
y
*=
f
;
z
*=
f
;
}
public
float
Magnitude
(
)
{
return
(
float
)
Math
.
Sqrt
(
x
*
x
+
y
*
y
+
z
*
z
)
;
}
public
static
Vector3f
operator
-
(
Vector3f
a
,
Vector3f
b
)
{
return
new
Vector3f
(
a
.
x
-
b
.
x
,
a
.
y
-
b
.
y
,
a
.
z
-
b
.
z
)
;
}
public
static
Vector3f
operator
-
(
Vector3f
a
)
{
return
new
Vector3f
(
-
a
.
x
,
-
a
.
y
,
-
a
.
z
)
;
}
public
static
Vector3f
operator
*
(
Vector3f
a
,
float
b
)
{
return
new
Vector3f
(
a
.
x
*
b
,
a
.
y
*
b
,
a
.
z
*
b
)
;
}
public
static
Vector3f
operator
/
(
Vector3f
a
,
float
b
)
{
return
new
Vector3f
(
a
.
x
/
b
,
a
.
y
/
b
,
a
.
z
/
b
)
;
}
public
static
Vector3f
operator
+
(
Vector3f
a
,
Vector3f
b
)
{
return
new
Vector3f
(
a
.
x
+
b
.
x
,
a
.
y
+
b
.
y
,
a
.
z
+
b
.
z
)
;
}
public
Vector3f
ReflectIn
(
Vector3f
normal
)
{
Vector3f
negVector
=
-
this
;
Vector3f
reflectedDir
=
normal
*
(
2.0f
*
negVector
.
Dot
(
normal
)
)
-
negVector
;
return
reflectedDir
;
}
}
public
class
Light
{
public
Vector3f
position
;
public
Light
(
Vector3f
p
)
{
position
=
p
;
}
}
public
class
Ray
{
public
const
float
WORLD_MAX
=
1000.0f
;
public
Vector3f
origin
;
public
Vector3f
direction
;
public
RTObject
closestHitObject
;
public
float
closestHitDistance
;
public
Vector3f
hitPoint
;
public
Ray
(
Vector3f
o
,
Vector3f
d
)
{
origin
=
o
;
direction
=
d
;
closestHitDistance
=
WORLD_MAX
;
closestHitObject
=
null
;
}
}
public
abstract
class
RTObject
{
public
Color
color
;
public
abstract
float
Intersect
(
Ray
ray
)
;
public
abstract
Vector3f
GetSurfaceNormalAtPoint
(
Vector3f
p
)
;
}
class
Sphere
:
RTObject
{
// to specify a sphere we need it's position and radius
public
Vector3f
position
;
public
float
radius
;
public
Sphere
(
Vector3f
p
,
float
r
,
Color
c
)
{
position
=
p
;
radius
=
r
;
color
=
c
;
}
public
override
float
Intersect
(
Ray
ray
)
{
Vector3f
lightFromOrigin
=
position
-
ray
.
origin
;
// dir from origin to us
float
v
=
lightFromOrigin
.
Dot
(
ray
.
direction
)
;
// cos of angle between dirs from origin to us and from origin to where the ray's pointing
float
hitDistance
=
radius
*
radius
+
v
*
v
-
lightFromOrigin
.
x
*
lightFromOrigin
.
x
-
lightFromOrigin
.
y
*
lightFromOrigin
.
y
-
lightFromOrigin
.
z
*
lightFromOrigin
.
z
;
if
(
hitDistance
<
0
)
// no hit (do this check now before bothering to do the sqrt below)
return
-
1
;
hitDistance
=
v
-
(
float
)
Math
.
Sqrt
(
hitDistance
)
;
// get actual hit distance
if
(
hitDistance
<
0
)
return
-
1
;
else
return
(
float
)
hitDistance
;
}
public
override
Vector3f
GetSurfaceNormalAtPoint
(
Vector3f
p
)
{
Vector3f
normal
=
p
-
position
;
normal
.
Normalise
(
)
;
return
normal
;
}
}
class
Plane
:
RTObject
{
public
Vector3f
normal
;
public
float
distance
;
public
Plane
(
Vector3f
n
,
float
d
,
Color
c
)
{
normal
=
n
;
distance
=
d
;
color
=
c
;
}
public
override
float
Intersect
(
Ray
ray
)
{
float
normalDotRayDir
=
normal
.
Dot
(
ray
.
direction
)
;
if
(
normalDotRayDir
==
0
)
// Ray is parallel to plane (this early-out won't help very often!)
return
-
1
;
// Any none-parallel ray will hit the plane at some point - the question now is just
// if it in the positive or negative ray direction.
float
hitDistance
=
-
(
normal
.
Dot
(
ray
.
origin
)
-
distance
)
/
normalDotRayDir
;
if
(
hitDistance
<
0
)
// Ray dir is negative, ie we're behind the ray's origin
return
-
1
;
else
return
hitDistance
;
}
public
override
Vector3f
GetSurfaceNormalAtPoint
(
Vector3f
p
)
{
return
normal
;
// This is of course the same across the entire plane
}
}
class
RayTracer
{
const
float
PI
=
3.1415926536f
;
// maths constants
const
float
PI_X_2
=
6.2831853072f
;
const
float
PI_OVER_2
=
1.5707963268f
;
const
int
CANVAS_WIDTH
=
640
;
// output image dimensions
const
int
CANVAS_HEIGHT
=
480
;
const
float
TINY
=
0.0001f
;
// a very short distance in world space coords
const
int
MAX_DEPTH
=
3
;
// max recursion for reflections
const
float
MATERIAL_DIFFUSE_COEFFICIENT
=
0.5f
;
// material diffuse brightness
const
float
MATERIAL_REFLECTION_COEFFICIENT
=
0.5f
;
// material reflection brightness
const
float
MATERIAL_SPECULAR_COEFFICIENT
=
2.0f
;
// material specular highlight brightness
const
float
MATERIAL_SPECULAR_POWER
=
50.0f
;
// material shininess (higher values=smaller highlights)
static
Color
BG_COLOR
=
Color
.
BlueViolet
;
// scene bg colour
static
Vector3f
eyePos
=
new
Vector3f
(
0
,
0
,
-
5.0f
)
;
// eye pos in world space coords
static
Vector3f
screenTopLeftPos
=
new
Vector3f
(
-
6.0f
,
4.0f
,
0
)
;
// top-left corner of screen in world coords
static
Vector3f
screenBottomRightPos
=
new
Vector3f
(
6.0f
,
-
4.0f
,
0
)
;
// bottom-right corner of screen in world coords
static
float
pixelWidth
,
pixelHeight
;
// dimensions of screen pixel **in world coords**
static
List
<
RTObject
>
objects
;
// all RTObjects in the scene
static
List
<
Light
>
lights
;
// all lights
static
Random
random
;
// global random for repeatability
static
void
Main
(
string
[
]
args
)
{
// init structures
objects
=
new
List
<
RTObject
>
(
)
;
lights
=
new
List
<
Light
>
(
)
;
random
=
new
Random
(
01478650229
)
;
Bitmap
canvas
=
new
Bitmap
(
CANVAS_WIDTH
,
CANVAS_HEIGHT
)
;
// add some objects
for
(
int
i
=
0
;
i
<
30
;
i
++
)
{
float
x
=
(
float
)
(
random
.
NextDouble
(
)
*
10.0f
)
-
5.0f
;
// Range -5 to 5
float
y
=
(
float
)
(
random
.
NextDouble
(
)
*
10.0f
)
-
5.0f
;
// Range -5 to 5
float
z
=
(
float
)
(
random
.
NextDouble
(
)
*
10.0f
)
;
// Range 0 to 10
Color
c
=
Color
.
FromArgb
(
255
,
random
.
Next
(
255
)
,
random
.
Next
(
255
)
,
random
.
Next
(
255
)
)
;
Sphere
s
=
new
Sphere
(
new
Vector3f
(
x
,
y
,
z
)
,
(
float
)
(
random
.
NextDouble
(
)
)
,
c
)
;
objects
.
Add
(
s
)
;
}
//Sphere debugSphere = new Sphere(new Vector3f(0, 0, 5.0f), 0.2f, Color.ForestGreen);
//objects.Add(debugSphere);
Plane
floor
=
new
Plane
(
new
Vector3f
(
0
,
1.0f
,
0
)
,
-
10.0f
,
Color
.
Aquamarine
)
;
objects
.
Add
(
floor
)
;
// add some lights
lights
.
Add
(
new
Light
(
new
Vector3f
(
2.0f
,
0.0f
,
0
)
)
)
;
lights
.
Add
(
new
Light
(
new
Vector3f
(
0
,
10.0f
,
7.5f
)
)
)
;
// calculate width and height of a pixel in world space coords
pixelWidth
=
(
screenBottomRightPos
.
x
-
screenTopLeftPos
.
x
)
/
CANVAS_WIDTH
;
pixelHeight
=
(
screenTopLeftPos
.
y
-
screenBottomRightPos
.
y
)
/
CANVAS_HEIGHT
;
// render it
int
dotPeriod
=
CANVAS_HEIGHT
/
10
;
System
.
Console
.
WriteLine
(
"Rendering...
\n
"
)
;
System
.
Console
.
WriteLine
(
"|0%---100%|"
)
;
RenderRow
(
canvas
,
dotPeriod
,
0
)
;
// save the pretties
canvas
.
Save
(
"output.png"
)
;
}
static
void
RenderRow
(
System
.
Drawing
.
Bitmap
canvas
,
int
dotPeriod
,
int
y
)
{
if
(
y
>=
CANVAS_HEIGHT
)
return
;
if
(
(
y
%
dotPeriod
)
==
0
)
System
.
Console
.
Write
(
"*"
)
;
for
(
int
x
=
0
;
x
<
CANVAS_WIDTH
;
x
++
)
{
Color
c
=
RenderPixel
(
x
,
y
)
;
canvas
.
SetPixel
(
x
,
y
,
c
)
;
}
SetTimeout
(
0
,
(
)
=>
RenderRow
(
canvas
,
dotPeriod
,
y
+
1
)
)
;
}
[
JSReplacement
(
"setTimeout($action, $timeoutMs)"
)
]
static
void
SetTimeout
(
int
timeoutMs
,
Action
action
)
{
action
(
)
;
}
// Given a ray with origin and direction set, fill in the intersection info
static
void
CheckIntersection
(
ref
Ray
ray
)
{
foreach
(
RTObject
obj
in
objects
)
{
// loop through objects, test for intersection
float
hitDistance
=
obj
.
Intersect
(
ray
)
;
// check for intersection with this object and find distance
if
(
hitDistance
<
ray
.
closestHitDistance
&&
hitDistance
>
0
)
{
ray
.
closestHitObject
=
obj
;
// object hit and closest yet found - store it
ray
.
closestHitDistance
=
hitDistance
;
}
}
ray
.
hitPoint
=
ray
.
origin
+
(
ray
.
direction
*
ray
.
closestHitDistance
)
;
// also store the point of intersection
}
// raytrace a pixel (ie, set pixel color to result of a trace of a ray starting from eye position and
// passing through the world coords of the pixel)
static
Color
RenderPixel
(
int
x
,
int
y
)
{
// First, calculate direction of the current pixel from eye position
float
sx
=
screenTopLeftPos
.
x
+
(
x
*
pixelWidth
)
;
float
sy
=
screenTopLeftPos
.
y
-
(
y
*
pixelHeight
)
;
Vector3f
eyeToPixelDir
=
new
Vector3f
(
sx
,
sy
,
0
)
-
eyePos
;
eyeToPixelDir
.
Normalise
(
)
;
// Set up primary (eye) ray
Ray
ray
=
new
Ray
(
eyePos
,
eyeToPixelDir
)
;
// And trace it!
return
Trace
(
ray
,
0
)
;
}
// given a ray, trace it into the scene and return the colour of the surface it hits
// (handles reflections recursively)
static
Color
Trace
(
Ray
ray
,
int
traceDepth
)
{
// See if the ray intersected an object
CheckIntersection
(
ref
ray
)
;
if
(
ray
.
closestHitDistance
>=
Ray
.
WORLD_MAX
||
ray
.
closestHitObject
==
null
)
// No intersection
return
BG_COLOR
;
// Got a hit - set initial colour to ambient light
float
r
=
0.15f
*
ray
.
closestHitObject
.
color
.
R
;
float
g
=
0.15f
*
ray
.
closestHitObject
.
color
.
G
;
float
b
=
0.15f
*
ray
.
closestHitObject
.
color
.
B
;
// Set up stuff we'll need for shading calcs
Vector3f
surfaceNormal
=
ray
.
closestHitObject
.
GetSurfaceNormalAtPoint
(
ray
.
hitPoint
)
;
Vector3f
viewerDir
=
-
ray
.
direction
;
// Direction back to the viewer (simply negative of ray dir)
// Loop through the lights, adding contribution of each
foreach
(
Light
light
in
lights
)
{
Vector3f
lightDir
=
new
Vector3f
(
)
;
float
lightDistance
;
// Find light direction and distance
lightDir
=
light
.
position
-
ray
.
hitPoint
;
// Get direction to light
lightDistance
=
lightDir
.
Magnitude
(
)
;
//lightDir = lightDir / lightDistance; // Light exponential falloff
lightDir
.
Normalise
(
)
;
// Shadow check: check if this light's visible from the point
// NB: Step out slightly from the hitpoint first
Ray
shadowRay
=
new
Ray
(
ray
.
hitPoint
+
(
lightDir
*
TINY
)
,
lightDir
)
;
shadowRay
.
closestHitDistance
=
lightDistance
;
// IMPORTANT: We only want it to trace as far as the light!
CheckIntersection
(
ref
shadowRay
)
;
if
(
shadowRay
.
closestHitObject
!=
null
)
// We hit something -- ignore this light entirely
continue
;
float
cosLightAngleWithNormal
=
surfaceNormal
.
Dot
(
lightDir
)
;
if
(
MATERIAL_DIFFUSE_COEFFICIENT
>
TINY
)
{
// Calculate light's diffuse component - note that this is view independant
// Dot product of surface normal and light direction gives cos of angle between them so will be in
// range -1 to 1. We use that as a scaling factor; common technique, called "cosine shading".
if
(
cosLightAngleWithNormal
<=
0
)
continue
;
// Add this light's diffuse contribution to our running totals
r
+=
MATERIAL_DIFFUSE_COEFFICIENT
*
cosLightAngleWithNormal
*
ray
.
closestHitObject
.
color
.
R
;
g
+=
MATERIAL_DIFFUSE_COEFFICIENT
*
cosLightAngleWithNormal
*
ray
.
closestHitObject
.
color
.
G
;
b
+=
MATERIAL_DIFFUSE_COEFFICIENT
*
cosLightAngleWithNormal
*
ray
.
closestHitObject
.
color
.
B
;
}
if
(
MATERIAL_SPECULAR_COEFFICIENT
>
TINY
)
{
// Specular component - dot product of light's reflection vector and viewer direction
// Direction to the viewer is simply negative of the ray direction
Vector3f
lightReflectionDir
=
surfaceNormal
*
(
cosLightAngleWithNormal
*
2
)
-
lightDir
;
float
specularFactor
=
viewerDir
.
Dot
(
lightReflectionDir
)
;
if
(
specularFactor
>
0
)
{
// To get smaller, sharper highlights we raise it to a power and multiply it
specularFactor
=
MATERIAL_SPECULAR_COEFFICIENT
*
(
float
)
Math
.
Pow
(
specularFactor
,
MATERIAL_SPECULAR_POWER
)
;
// Add the specular contribution to our running totals
r
+=
specularFactor
*
ray
.
closestHitObject
.
color
.
R
;
g
+=
specularFactor
*
ray
.
closestHitObject
.
color
.
G
;
b
+=
specularFactor
*
ray
.
closestHitObject
.
color
.
B
;
}
}
}
// Now do reflection, unless we're too deep
if
(
traceDepth
<
MAX_DEPTH
&&
MATERIAL_REFLECTION_COEFFICIENT
>
TINY
)
{
// Set up the reflected ray - notice we move the origin out a tiny bit again
Vector3f
reflectedDir
=
ray
.
direction
.
ReflectIn
(
surfaceNormal
)
;
Ray
reflectionRay
=
new
Ray
(
ray
.
hitPoint
+
reflectedDir
*
TINY
,
reflectedDir
)
;
// And trace!
Color
reflectionCol
=
Trace
(
reflectionRay
,
traceDepth
+
1
)
;
// Add reflection results to running totals, scaling by reflect coeff.
r
+=
MATERIAL_REFLECTION_COEFFICIENT
*
reflectionCol
.
R
;
g
+=
MATERIAL_REFLECTION_COEFFICIENT
*
reflectionCol
.
G
;
b
+=
MATERIAL_REFLECTION_COEFFICIENT
*
reflectionCol
.
B
;
}
// Clamp RGBs
if
(
r
>
255
)
r
=
255
;
if
(
g
>
255
)
g
=
255
;
if
(
b
>
255
)
b
=
255
;
return
(
Color
.
FromArgb
(
255
,
(
int
)
r
,
(
int
)
g
,
(
int
)
b
)
)
;
}
}
}
Back
|
FazBrowse Home
|
New Git URL