-
Notifications
You must be signed in to change notification settings - Fork 2
/
raytracer.pbk
291 lines (243 loc) · 9.27 KB
/
raytracer.pbk
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
<languageVersion : 1.0;>
#define PI 3.1415926535897932384626433832795
kernel RayTracer
< namespace : "Newgrounds";
vendor : "Newgrounds";
version : 1;
description : "Pixel Blender Raytracing";
>
{
output pixel4 dst;
parameter float viewPlaneDistance
<
minValue: 0.1;
maxValue: 5.0;
defaultValue: 2.0;
>;
parameter float3 lightPos
<
minValue: float3(-6.0, -6.0, -25.0);
maxValue: float3(6.0, 6.0, 0.0);
defaultValue: float3(0.0, 2.0, -4.0);
>;
parameter float3 sphere0Position
<
minValue: float3(-6.0, -6.0, -25.0);
maxValue: float3(6.0, 6.0, -2.0);
defaultValue: float3(0.0, 2.0, -10.0);
>;
parameter float sphere0Radius
<
minValue: 0.1;
maxValue: 8.0;
defaultValue: 2.0;
>;
parameter float3 sphere0Color
<
minValue: float3(0.0, 0.0, 0.0);
maxValue: float3(1.0, 1.0, 1.0);
defaultValue: float3(0.8, 0.8, 0.8);
>;
parameter float4 sphere0Material
<
minValue: float4(0.0, 0.0, 0.0, 0.0);
maxValue: float4(1.0, 1.0, 1.0, 1.0);
defaultValue: float4(0.05, 0.1, 1.0, 1.0);
>;
const float RENDER_WIDTH = 512.0;
const float RENDER_HEIGHT = 512.0;
const float SPECULAR_EXPONENT = 50.0;
const int MAX_RAY_SHOTS = 4;
const int NUM_SPHERES = 35;
const int SPHERE_PARAMETER_COUNT = 11;
dependent float sphereArray[NUM_SPHERES*SPHERE_PARAMETER_COUNT];
// initialize our sphere parameters
void evaluateDependents()
{
// SPHERE PARAMETRS
// (x, y, z, radius, r, g, b, ambient, diffuse, specular, reflectivity)
sphereArray[0] = sphere0Position.x;
sphereArray[1] = sphere0Position.y;
sphereArray[2] = sphere0Position.z;
sphereArray[3] = sphere0Radius;
sphereArray[4] = sphere0Color.x;
sphereArray[5] = sphere0Color.y;
sphereArray[6] = sphere0Color.z;
sphereArray[7] = sphere0Material.x;
sphereArray[8] = sphere0Material.y;
sphereArray[9] = sphere0Material.z;
sphereArray[10] = sphere0Material.w;
sphereArray[11] = 0.0;
sphereArray[12] = -1003.0;
sphereArray[13] = -8.0;
sphereArray[14] = 1000.0;
sphereArray[15] = 0.6;
sphereArray[16] = 0.6;
sphereArray[17] = 0.6;
sphereArray[18] = 0.1;
sphereArray[19] = 0.8;
sphereArray[20] = 0.5;
sphereArray[21] = 0.5;
// let's make a bunch of fakely random spheres
for(int i=SPHERE_PARAMETER_COUNT*2; i<NUM_SPHERES*SPHERE_PARAMETER_COUNT; i+=SPHERE_PARAMETER_COUNT)
{
float ifloat = float(i);
sphereArray[i] = sin(ifloat/5.0)*6.0;
sphereArray[i+1] = sin(ifloat/4.1)*2.5;
sphereArray[i+2] = -18.0 - sin(ifloat/3.1+1.2)*10.0;
sphereArray[i+3] = pow(sin(ifloat/1.34+65.3)*0.5+0.5, 3.0)*1.0 + 0.2;
sphereArray[i+4] = cos(ifloat/2.1+1.3)*0.5+0.5;
sphereArray[i+5] = cos(ifloat/0.1+1.3)*0.5+0.5;
sphereArray[i+6] = cos(ifloat/5.1+6.3)*0.5+0.5;
sphereArray[i+7] = 0.1;
sphereArray[i+8] = 0.7;
sphereArray[i+9] = 1.0;
sphereArray[i+10] = pow( sin(ifloat/2.1 + 1.243)*0.5 + 0.5, 5.0);
}
}
// shootRay(): fires a ray from origin, toward dir
// returns first intersection
void shootRay(
in float3 origin,
in float3 dir,
out int hit,
out float3 pos,
out float t,
out int sphereNum
)
{
float curT;
float B, C, disc;
float3 spherePos;
float3 sphereToOrigin;
float sphereRadius;
hit = 0;
t = 99999.0;
// cycle through all spheres and find the smallest t>0 that we hit
for(int i=0; i<NUM_SPHERES*SPHERE_PARAMETER_COUNT; i+=SPHERE_PARAMETER_COUNT)
{
spherePos = float3(sphereArray[i], sphereArray[i+1], sphereArray[i+2]);
sphereRadius = sphereArray[i+3];
sphereToOrigin = origin - spherePos;
B = dot(sphereToOrigin, dir);
C = dot(sphereToOrigin, sphereToOrigin) - sphereRadius*sphereRadius;
disc = B*B-C;
if(disc>0.0)
{
curT = -B-sqrt(disc);
if(curT>0.0 && curT<t)
{
sphereNum = i;
t = curT;
hit = 1;
}
}
}
pos = origin + dir*t;
}
void evaluatePixel()
{
dst = pixel4(0,0,0, 1.0);
float3 origin = float3(0, 0, 0);
// calculate direction vector for this pixel
float3 dir = float3(
2.0*outCoord().x/RENDER_WIDTH - 1.0,
-2.0*outCoord().y/RENDER_HEIGHT + 1.0,
-viewPlaneDistance
);
// sphere parameters
int sphereNum;
float3 spherePos;
float sphereRadius;
float3 sphereColor;
float4 sphereMaterial;
float3 hitPoint;
float t;
int hit;
float3 sphereHit; // hit point relative to sphere
float3 n; // surface normal
float3 lightVector; // surface to light
float lightVectorLen;
float3 l; // normalized light vector
float3 lReflect; // reflected off surface
float3 dirReflect;
int shadowTest;
float3 temp;
int temp2;
int rayShots = MAX_RAY_SHOTS;
float3 colorScale = float3(1.0, 1.0, 1.0);
// lighting
float specular;
float diffuse;
float lightVal;
// texturing
float phi;
float2 uv;
while( rayShots > 0 )
{
// let's make sure dir is properly normalized
dir = normalize(dir);
// INTERSECTION TEST
// find the first sphere we intersect with
shootRay(origin, dir, hit, hitPoint, t, sphereNum);
if(hit != 0)
{
// grab the parameters for the sphere we hit
spherePos = float3( sphereArray[sphereNum], sphereArray[sphereNum+1], sphereArray[sphereNum+2] );
sphereRadius = sphereArray[sphereNum+3];
sphereColor = float3( sphereArray[sphereNum+4], sphereArray[sphereNum+5], sphereArray[sphereNum+6] );
sphereMaterial = float4( sphereArray[sphereNum+7], sphereArray[sphereNum+8], sphereArray[sphereNum+9], sphereArray[sphereNum+10] );
sphereHit = hitPoint - spherePos;
n = sphereHit / sphereRadius; // normal at the point we hit
lightVector = lightPos - hitPoint; // hit point to light
lightVectorLen = length(lightVector);
l = lightVector / lightVectorLen;
// SHADOW TEST
// fire a ray from our hit position towards the light
shootRay(hitPoint, l, shadowTest, temp, t, temp2);
if(shadowTest == 0) // if we didn't hit anything, we can see the light
shadowTest = 1;
else if(t < lightVectorLen) // if we hit something before the light, we are in shadow
shadowTest = 0;
diffuse = dot(l, n);
lReflect = l - 2.0*diffuse*n; // reflect the light vector
specular = dot(dir, lReflect);
diffuse = max( diffuse, 0.0 );
specular = pow( max(specular, 0.0), SPECULAR_EXPONENT );
// ground checkboard texture
if(sphereNum == 11)
{
phi = acos( -dot(float3(1.0, 0.0, 0.0), n) );
uv = float2(
acos( dot( float3(0.0, 0.0, 1.0), n) /sin(phi) )/(2.0*PI),
phi/PI
);
// we could do sampleLinear here to do some actual texturing. :)
sphereColor *= ( mod(floor(uv.x*2000.0)+floor(uv.y*2000.0),2.0)==0.0 )? 0.5 : 1.0;
}
// finally, blend our color into this pixel
lightVal = (sphereMaterial.x + float(shadowTest)*(diffuse*sphereMaterial.y + specular*sphereMaterial.z));
pixel3 res = colorScale*lightVal*sphereColor;
dst += pixel4(res.x, res.y, res.z, 0.0);
// reflection
if(sphereMaterial.w > 0.0)
{
dirReflect = dir - 2.0*dot(dir, n)*n; // reflect our view vector
dirReflect = normalize(dirReflect);
// originate at our hit position, fire at reflected angle
origin = hitPoint;
dir = dirReflect;
rayShots--;
// blend according to reflectivity
colorScale *= sphereMaterial.w*sphereColor;
}
else rayShots = 0;
}
else rayShots = 0;
}
}
region generated()
{
return region(float4(0, 0, RENDER_WIDTH, RENDER_HEIGHT));
}
}