1// https://www.shadertoy.com/view/4ssGzn
2
3// ray marched fireball
4// http://http.download.nvidia.com/developer/presentations/2005/GDC/Sponsored_Day/GDC_2005_VolumeRenderingForGames.pdf
5
6// sgreen
7const int _VolumeSteps = 32;
8const float _StepSize = 0.1;
9uniform float _Density = 0.2; // Density, min=0., max=1.
10
11uniform float _SphereRadius = 2.0; // Sphere Radius, min=0., max=2.
12uniform float _NoiseFreq = 1.0; // Noise Frequency, min=0., max=5.
13uniform float _NoiseAmp = 3.0; // Noise Amplitude, min=0., max=10.
14const vec3 _NoiseAnim = vec3(0, -1, 0);
15
16// iq's nice integer-less noise function
17
18// matrix to rotate the noise octaves
19mat3 m = mat3( 0.00,  0.80,  0.60,
20              -0.80,  0.36, -0.48,
21              -0.60, -0.48,  0.64 );
22
23float hash( float n )
24{
25    return fract(sin(n)*43758.5453);
26}
27
28
29float noise( in vec3 x )
30{
31    vec3 p = floor(x);
32    vec3 f = fract(x);
33
34    f = f*f*(3.0-2.0*f);
35
36    float n = p.x + p.y*57.0 + 113.0*p.z;
37
38    float res = mix(mix(mix( hash(n+  0.0), hash(n+  1.0),f.x),
39                        mix( hash(n+ 57.0), hash(n+ 58.0),f.x),f.y),
40                    mix(mix( hash(n+113.0), hash(n+114.0),f.x),
41                        mix( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);
42    return res;
43}
44
45float fbm( vec3 p )
46{
47    float f;
48    f = 0.5000*noise( p ); p = m*p*2.02;
49    f += 0.2500*noise( p ); p = m*p*2.03;
50    f += 0.1250*noise( p ); p = m*p*2.01;
51    f += 0.0625*noise( p );
52    //p = m*p*2.02; f += 0.03125*abs(noise( p ));
53    return f;
54}
55
56// returns signed distance to surface
57float distanceFunc(vec3 p)
58{
59        float d = length(p) - _SphereRadius;	// distance to sphere
60
61        // offset distance with pyroclastic noise
62        //p = normalize(p) * _SphereRadius;	// project noise point to sphere surface
63        d += fbm(p*_NoiseFreq + _NoiseAnim*iTime) * _NoiseAmp;
64        return d;
65}
66
67// color gradient
68// this should be in a 1D texture really
69vec4 gradient(float x)
70{
71        // no constant array initializers allowed in GLES SL!
72        const vec4 c0 = vec4(2, 2, 1, 1);	// yellow
73        const vec4 c1 = vec4(1, 0, 0, 1);	// red
74        const vec4 c2 = vec4(0, 0, 0, 0); 	// black
75        const vec4 c3 = vec4(0, 0.5, 1, 0.5); 	// blue
76        const vec4 c4 = vec4(0, 0, 0, 0); 	// black
77
78        x = clamp(x, 0.0, 0.999);
79        float t = fract(x*4.0);
80        vec4 c;
81        if (x < 0.25) {
82                c =  mix(c0, c1, t);
83        } else if (x < 0.5) {
84                c = mix(c1, c2, t);
85        } else if (x < 0.75) {
86                c = mix(c2, c3, t);
87        } else {
88                c = mix(c3, c4, t);
89        }
90        //return vec4(x);
91        //return vec4(t);
92        return c;
93}
94
95// shade a point based on distance
96vec4 shade(float d)
97{
98        // lookup in color gradient
99        return gradient(d);
100        //return mix(vec4(1, 1, 1, 1), vec4(0, 0, 0, 0), smoothstep(1.0, 1.1, d));
101}
102
103// procedural volume
104// maps position to color
105vec4 volumeFunc(vec3 p)
106{
107        float d = distanceFunc(p);
108        return shade(d);
109}
110
111// ray march volume from front to back
112// returns color
113vec4 rayMarch(vec3 rayOrigin, vec3 rayStep, out vec3 pos)
114{
115        vec4 sum = vec4(0, 0, 0, 0);
116        pos = rayOrigin;
117        for(int i=0; i<_VolumeSteps; i++) {
118                vec4 col = volumeFunc(pos);
119                col.a *= _Density;
120                //col.a = min(col.a, 1.0);
121
122                // pre-multiply alpha
123                col.rgb *= col.a;
124                sum = sum + col*(1.0 - sum.a);
125#if 0
126                // exit early if opaque
127                if (sum.a > _OpacityThreshold)
128                        break;
129#endif
130                pos += rayStep;
131        }
132        return sum;
133}
134
135void mainImage( out vec4 fragColor, in vec2 fragCoord )
136{
137    vec2 p = (fragCoord.xy / iResolution.xy)*2.0-1.0;
138    p.x *= iResolution.x/ iResolution.y;
139    vec3 v = iResolution / 2.0;
140    float rotx = (v.y / iResolution.y)*4.0;
141    float roty = -(v.x / iResolution.x)*4.0;
142
143    float zoom = 4.0;
144
145    // camera
146    vec3 ro = zoom*normalize(vec3(cos(roty), cos(rotx), sin(roty)));
147    vec3 ww = normalize(vec3(0.0,0.0,0.0) - ro);
148    vec3 uu = normalize(cross( vec3(0.0,1.0,0.0), ww ));
149    vec3 vv = normalize(cross(ww,uu));
150    vec3 rd = normalize( p.x*uu + p.y*vv + 1.5*ww );
151
152    ro += rd*2.0;
153
154    // volume render
155    vec3 hitPos;
156    vec4 col = rayMarch(ro, rd*_StepSize, hitPos);
157    //vec4 col = gradient(p.x);
158
159    fragColor = col;
160}