-
Notifications
You must be signed in to change notification settings - Fork 5
/
js.js
120 lines (97 loc) · 3.33 KB
/
js.js
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
w = 120; //width doubles as height, to save 6 bytes overall
W = w*5; //pixel width
c.width = c.height = W;
//b.style.background = "#000"; //i like a black background, but this is way too expensive
//some shortcuts
M = Math
R = M.random
function A(){ return R()-0.5 } //random range from -0.5 to 0.5
//create the image we'll be writing to.
//writing directly to this sort of image,
//and then using built in methods to write it to the canvas,
//is a ton faster than trying write pixels directly to the canvas
T = a.createImageData(W,W);
D = T.data; //the actual image data to write to directly
//a function to detect if a photon is inside a sphere, plus the X and Y offset of the sphere.
//(z is always 0 for the spheres in this scene, so i don't need it)
function z(v,x,y){
X = v[0]+x;
Y = v[1]+y;
return X*X+Y*Y+v[2]*v[2] < 1;
}
f = [] // f for framebuffer
// m is the vertical row count, which we use later, and save a byte by initializing here
for( q=m=0; q < w*w*3; ) f[q++]=0
// m is the framecount
B = function(){
//for( var i = 0; i < 10000; i++ ){
//for( q=0;q<w*h; ){
for( L=0;L++<w*20; ){
x = q%w/w;
y = (q-q%w)/w/w;
//camera position
p = [0,0,-3.5];
//create the initial direction vector, based on screen position.
//also, randomize it within that pixel for antialising
v = [ (0.5-(x+R()/w))*0.03,(0.5-(y+R()/w))*0.03,0.03 ]; //vector
d = 1; //to tell if the surface was hit
//do 200 steps before giving up
for( r=g=b=I=0; I++ < 200; ){
//add vector to photon position
for(i=0;i<3;) p[i]+=v[i++]//*(1-R()/2);
//foggy blur
//this is by far the most obscene omtimization here...
//instead of reinitializing i=0, we know it's 3 from the last loop,
//so we iterate downwards instead.
//also, the loop stops at zero, so i omit ;i>=0 in favor of truthyness.
//it saved 5 bytes.
if( R()<0.004 )for(;i;)v[--i] += A()*0.01;
//heart shape
x = 0.7*(p[0]);
y = 0.7*(p[2]-1);
i = 0.7*(p[1]+0.3); //z
j = x*x+9/4*y*y+i*i-1;
j*=j*j;
j -= x*x*i*i*i+9/80*y*y*i*i*i;
if( j < 0 ){
v=[A()/10,A()/10,A()/10];
d=0.5;
}
//light sources
if( z(p,2.5,1) ){
g = 2; b=r=3;
}
if( z(p,-2.5,1) ){
r = 3;
}
if( z(p,0,-2.5) || z(p,0,3) ){
g=b=2;r=3;
}
}
//beams that end facing up should get lit from a skybox
j = M.max(0,v[1]*27);
//add the light values to the frame buffer
f[Q=q*3] += r+j*1.5;
f[++Q] += d*g+j;
f[++Q] += d*b+j;
q = ++m%(w*w);
}
I = M.ceil(m/w/w)/255;
//put it all into a framebuffer, scaled up.
//to render it at a high resolution ran too slow,
//so I wanted to scale it up to make it cheaper.
//but the default image scaling in most browsers uses
//smoothing, and I think the aliased, pixelly look is prettier.
for( x = 0; x < W; x++)
for( y = 0; y < W; ){
i = (~~(x/5)+~~(y/5)*w);
j = (x+ + y++ *W)*4;
if( i > q && q ) break
for( L = 0; L < 3; ) D[j+L] = f[i*3+L++]/I;
D[j+3] = 255;
}
a.putImageData( T,0,0 );
//and start the loop over
setTimeout(B);
}
B();