1#version 450
2// A lot of spheres. Created by Reinder Nijhoff 2013
3// @reindernijhoff
4//
5// https://www.shadertoy.com/view/lsX3WH
6//
7
8layout(std140, set = 0, binding = 0) uniform UBO
9{
10   mat4 MVP;
11   vec4 OutputSize;
12   vec4 OriginalSize;
13   vec4 SourceSize;
14   uint FrameCount;
15} global;
16
17#pragma stage vertex
18layout(location = 0) in vec4 Position;
19layout(location = 1) in  vec2 TexCoord;
20layout(location = 0) out vec2 vTexCoord;
21const vec2 madd = vec2(0.5, 0.5);
22void main()
23{
24   gl_Position = global.MVP * Position;
25   vTexCoord = gl_Position.xy;
26}
27
28#pragma stage fragment
29layout(location = 0) in vec2 vTexCoord;
30layout(location = 0) out vec4 FragColor;
31float iGlobalTime = float(global.FrameCount)*0.025;
32vec2 iResolution = global.OutputSize.xy;
33
34#define SHADOW
35#define REFLECTION
36
37
38#define RAYCASTSTEPS 40
39
40#define EXPOSURE 0.9
41#define EPSILON 0.0001
42#define MAXDISTANCE 400.
43#define GRIDSIZE 8.
44#define GRIDSIZESMALL 5.
45#define MAXHEIGHT 10.
46#define SPEED 0.5
47
48#define time iGlobalTime
49
50//
51// math functions
52//
53
54const mat2 mr = mat2 (0.84147,  0.54030,
55					  0.54030, -0.84147 );
56float hash( float n ) {
57	return fract(sin(n)*43758.5453);
58}
59vec2 hash2( float n ) {
60	return fract(sin(vec2(n,n+1.0))*vec2(2.1459123,3.3490423));
61}
62vec2 hash2( vec2 n ) {
63	return fract(sin(vec2( n.x*n.y, n.x+n.y))*vec2(2.1459123,3.3490423));
64}
65vec3 hash3( float n ) {
66	return fract(sin(vec3(n,n+1.0,n+2.0))*vec3(3.5453123,4.1459123,1.3490423));
67}
68vec3 hash3( vec2 n ) {
69	return fract(sin(vec3(n.x, n.y, n+2.0))*vec3(3.5453123,4.1459123,1.3490423));
70}
71//
72// intersection functions
73//
74
75bool intersectPlane(vec3 ro, vec3 rd, float height, out float dist) {
76	if (rd.y==0.0) {
77		return false;
78	}
79
80	float d = -(ro.y - height)/rd.y;
81	d = min(100000.0, d);
82	if( d > 0. ) {
83		dist = d;
84		return true;
85	}
86	return false;
87}
88
89bool intersectUnitSphere ( in vec3 ro, in vec3 rd, in vec3 sph, out float dist, out vec3 normal ) {
90	vec3  ds = ro - sph;
91	float bs = dot( rd, ds );
92	float cs = dot(  ds, ds ) - 1.0;
93	float ts = bs*bs - cs;
94
95	if( ts > 0.0 ) {
96		ts = -bs - sqrt( ts );
97		if( ts>0. ) {
98			normal = normalize( (ro+ts*rd)-sph );
99			dist = ts;
100			return true;
101		}
102	}
103
104	return false;
105}
106
107//
108// Scene
109//
110
111void getSphereOffset( vec2 grid, inout vec2 center ) {
112	center = (hash2( grid+vec2(43.12,1.23) ) - vec2(0.5) )*(GRIDSIZESMALL);
113}
114void getMovingSpherePosition( vec2 grid, vec2 sphereOffset, inout vec3 center ) {
115	// falling?
116	float s = 0.1+hash( grid.x*1.23114+5.342+754.324231*grid.y );
117	float t = 14.*s + time/s;
118
119	float y =  s * MAXHEIGHT * abs( cos( t ) );
120	vec2 offset = grid + sphereOffset;
121
122	center = vec3( offset.x, y, offset.y ) + 0.5*vec3( GRIDSIZE, 2., GRIDSIZE );
123}
124void getSpherePosition( vec2 grid, vec2 sphereOffset, inout vec3 center ) {
125	vec2 offset = grid + sphereOffset;
126	center = vec3( offset.x, 0., offset.y ) + 0.5*vec3( GRIDSIZE, 2., GRIDSIZE );
127}
128vec3 getSphereColor( vec2 grid ) {
129	return normalize( hash3( grid+vec2(43.12*grid.y,12.23*grid.x) ) );
130}
131
132vec3 trace(vec3 ro, vec3 rd, out vec3 intersection, out vec3 normal, out float dist, out int material) {
133	material = 0; // sky
134	dist = MAXDISTANCE;
135	float distcheck;
136
137	vec3 sphereCenter, col, normalcheck;
138
139	if( intersectPlane( ro,  rd, 0., distcheck) && distcheck < MAXDISTANCE ) {
140		dist = distcheck;
141		material = 1;
142		normal = vec3( 0., 1., 0. );
143		col = vec3( 1. );
144	} else {
145		col = vec3( 0. );
146	}
147
148
149	// trace grid
150	vec3 pos = floor(ro/GRIDSIZE)*GRIDSIZE;
151	vec3 ri = 1.0/rd;
152	vec3 rs = sign(rd) * GRIDSIZE;
153	vec3 dis = (pos-ro + 0.5  * GRIDSIZE + rs*0.5) * ri;
154	vec3 mm = vec3(0.0);
155	vec2 offset;
156
157	for( int i=0; i<RAYCASTSTEPS; i++ )	{
158		if( material > 1 || distance( ro.xz, pos.xz ) > dist+GRIDSIZE ) break;
159		vec2 offset;
160		getSphereOffset( pos.xz, offset );
161
162		getMovingSpherePosition( pos.xz, -offset, sphereCenter );
163
164		if( intersectUnitSphere( ro, rd, sphereCenter, distcheck, normalcheck ) && distcheck < dist ) {
165			dist = distcheck;
166			normal = normalcheck;
167			material = 2;
168		}
169
170		getSpherePosition( pos.xz, offset, sphereCenter );
171		if( intersectUnitSphere( ro, rd, sphereCenter, distcheck, normalcheck ) && distcheck < dist ) {
172			dist = distcheck;
173			normal = normalcheck;
174			col = vec3( 2. );
175			material = 3;
176		}
177		mm = step(dis.xyz, dis.zyx);
178		dis += mm * rs * ri;
179		pos += mm * rs;
180	}
181
182	vec3 color = vec3( 0. );
183	if( material > 0 ) {
184		intersection = ro + rd*dist;
185		vec2 map = floor(intersection.xz/GRIDSIZE)*GRIDSIZE;
186
187		if( material == 1 || material == 3 ) {
188			// lightning
189			vec3 c = vec3( -GRIDSIZE,0., GRIDSIZE );
190			for( int x=0; x<3; x++ ) {
191				for( int y=0; y<3; y++ ) {
192					vec2 mapoffset = map+vec2( c[x], c[y] );
193					vec2 offset;
194					getSphereOffset( mapoffset, offset );
195					vec3 lcolor = getSphereColor( mapoffset );
196					vec3 lpos;
197					getMovingSpherePosition( mapoffset, -offset, lpos );
198
199					float shadow = 1.;
200#ifdef SHADOW
201					if( material == 1 ) {
202						for( int sx=0; sx<3; sx++ ) {
203							for( int sy=0; sy<3; sy++ ) {
204								if( shadow < 1. ) continue;
205
206								vec2 smapoffset = map+vec2( c[sx], c[sy] );
207								vec2 soffset;
208								getSphereOffset( smapoffset, soffset );
209								vec3 slpos, sn;
210								getSpherePosition( smapoffset, soffset, slpos );
211								float sd;
212								if( intersectUnitSphere( intersection, normalize( lpos - intersection ), slpos, sd, sn )  ) {
213									shadow = 0.;
214								}
215							}
216						}
217					}
218#endif
219					color += col * lcolor * ( shadow * max( dot( normalize(lpos-intersection), normal ), 0.) *
220											 (1. - clamp( distance( lpos, intersection )/GRIDSIZE, 0., 1.) ) );
221				}
222			}
223		} else {
224			// emitter
225			color = (1.5+dot(normal, vec3( 0.5, 0.5, -0.5) )) *getSphereColor( map );
226		}
227	}
228	return color;
229}
230
231
232void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
233	vec2 q = fragCoord.xy/iResolution.xy;
234	vec2 p = -1.0+2.0*q;
235	p.x *= iResolution.x/iResolution.y;
236
237	// camera
238	vec3 ce = vec3( cos( 0.232*time) * 10., 6.+3.*cos(0.3*time), GRIDSIZE*(time/SPEED) );
239	vec3 ro = ce;
240	vec3 ta = ro + vec3( -sin( 0.232*time) * 10., -2.0+cos(0.23*time), 10.0 );
241
242	float roll = -0.15*sin(0.5*time);
243
244	// camera tx
245	vec3 cw = normalize( ta-ro );
246	vec3 cp = vec3( sin(roll), cos(roll),0.0 );
247	vec3 cu = normalize( cross(cw,cp) );
248	vec3 cv = normalize( cross(cu,cw) );
249	vec3 rd = normalize( p.x*cu + p.y*cv + 1.5*cw );
250
251	// raytrace
252	int material;
253	vec3 normal, intersection;
254	float dist;
255
256	vec3 col = trace(ro, rd, intersection, normal, dist, material);
257
258#ifdef REFLECTION
259	if( material > 0 ) {
260		vec3 ro = intersection + EPSILON*normal;
261		rd = reflect( rd, normal );
262		col += 0.05 * trace(ro, rd, intersection, normal, dist, material);
263	}
264#endif
265
266	col = pow( col, vec3(EXPOSURE, EXPOSURE, EXPOSURE) );
267	col = clamp(col, 0.0, 1.0);
268
269	// vigneting
270	col *= 0.25+0.75*pow( 16.0*q.x*q.y*(1.0-q.x)*(1.0-q.y), 0.15 );
271
272	fragColor = vec4( col,1.0);
273}
274
275void main(void)
276{
277  //just some shit to wrap shadertoy's stuff
278  vec2 FragmentCoord = vTexCoord.xy*global.OutputSize.xy;
279  FragmentCoord.y = -FragmentCoord.y;
280  mainImage(FragColor,FragmentCoord);
281}
282