Last active
August 29, 2015 14:00
-
-
Save micahscopes/9276cfbed0b1dc7f5536 to your computer and use it in GitHub Desktop.
rock paper scissors -> RGB -> FM modulation (WebAudio/WebGl a/v synthesis)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html><head> | |
<meta http-equiv="content-type" content="text/html; charset=windows-1252"> | |
<title>Rock Paper Scissor battling in RGB, FM modulating three sine waves | WebGL GPGPU + WebAudio</title> | |
<script id="shader-vs" type="x-shader/x-vertex"> | |
attribute vec3 aPos; | |
attribute vec2 aTexCoord; | |
varying vec2 uv; | |
varying vec2 uv_orig; | |
void main(void) { | |
gl_Position = vec4(aPos, 1.); | |
uv = aTexCoord; | |
uv_orig = uv; | |
} | |
</script> | |
<script id="shader-fs-inc" type="x-shader/x-fragment"> | |
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
varying vec2 uv; | |
varying vec2 uv_orig; | |
bool is_onscreen(vec2 uv){ | |
return (uv.x < 1.) && (uv.x > 0.) && (uv.y < 1.) && (uv.y > 0.); | |
} | |
float border(vec2 uv, float border, vec2 texSize){ | |
uv*=texSize; | |
return (uv.x<border || uv.x>texSize.x-border || uv.y<border || uv.y >texSize.y-border) ? 1.:.0; | |
} | |
#define pi 3.141592653589793238462643383279 | |
#define pi_inv 0.318309886183790671537767526745 | |
#define pi2_inv 0.159154943091895335768883763372 | |
float border(vec2 domain, float thickness){ | |
vec2 uv = fract(domain-vec2(0.5)); | |
uv = min(uv,1.-uv)*2.; | |
return clamp(max(uv.x,uv.y)-1.+thickness,0.,1.)/(thickness); | |
} | |
float square_mask(vec2 domain){ | |
return (domain.x <= 1. && domain.x >= 0. && domain.y <= 1. && domain.y >= 0.) ? 1. : 0.; | |
} | |
vec2 complex_mul(vec2 factorA, vec2 factorB){ | |
return vec2( factorA.x*factorB.x - factorA.y*factorB.y, factorA.x*factorB.y + factorA.y*factorB.x); | |
} | |
vec2 spiralzoom(vec2 domain, vec2 center, float n, float spiral_factor, float zoom_factor, vec2 pos){ | |
vec2 uv = domain - center; | |
float d = length(uv); | |
return vec2( atan(uv.y, uv.x)*n*pi2_inv + d*spiral_factor, -log(d)*zoom_factor) + pos; | |
} | |
vec2 complex_div(vec2 numerator, vec2 denominator){ | |
return vec2( numerator.x*denominator.x + numerator.y*denominator.y, | |
numerator.y*denominator.x - numerator.x*denominator.y)/ | |
vec2(denominator.x*denominator.x + denominator.y*denominator.y); | |
} | |
// HSL to RGB converter code from http://www.gamedev.net/topic/465948-hsl-shader-glsl-code/ | |
float Hue_2_RGB(float v1, float v2, float vH ) | |
{ | |
float ret; | |
if ( vH < 0.0 ) | |
vH += 1.0; | |
if ( vH > 1.0 ) | |
vH -= 1.0; | |
if ( ( 6.0 * vH ) < 1.0 ) | |
ret = ( v1 + ( v2 - v1 ) * 6.0 * vH ); | |
else if ( ( 2.0 * vH ) < 1.0 ) | |
ret = ( v2 ); | |
else if ( ( 3.0 * vH ) < 2.0 ) | |
ret = ( v1 + ( v2 - v1 ) * ( ( 2.0 / 3.0 ) - vH ) * 6.0 ); | |
else | |
ret = v1; | |
return ret; | |
} | |
vec3 hsl2rgb(float H, float S, float L){ | |
float var_2, var_1, R, G, B; | |
if (S == 0.0) | |
{ | |
R = L; | |
G = L; | |
B = L; | |
} | |
else | |
{ | |
if ( L < 0.5 ) | |
{ | |
var_2 = L * ( 1.0 + S ); | |
} | |
else | |
{ | |
var_2 = ( L + S ) - ( S * L ); | |
} | |
var_1 = 2.0 * L - var_2; | |
R = Hue_2_RGB( var_1, var_2, H + ( 1.0 / 3.0 ) ); | |
G = Hue_2_RGB( var_1, var_2, H ); | |
B = Hue_2_RGB( var_1, var_2, H - ( 1.0 / 3.0 ) ); | |
} | |
return vec3(R,G,B); | |
} | |
vec2 uv_zoom(vec2 uv, vec2 center, float zoom, float zoom_exp, float zoom_factor){ | |
return center + (uv - center)*(1. - zoom * pow(zoom_exp, zoom_factor*length(uv-center))); | |
} | |
vec2 gradient(sampler2D sampler, vec2 uv, vec2 d, vec4 selector){ | |
vec4 dX = texture2D(sampler, uv + vec2(1.,0.)*d) - texture2D(sampler, uv - vec2(1.,0.)*d); | |
vec4 dY = texture2D(sampler, uv + vec2(0.,1.)*d) - texture2D(sampler, uv - vec2(0.,1.)*d); | |
return -vec2( dot(dX, selector), dot(dY, selector) ); | |
} | |
vec2 rot90(vec2 vector){ | |
return vector.yx*vec2(1,-1); | |
} | |
float atan2(float y, float x){ | |
if(x>0.) return atan(y/x); | |
if(y>=0. && x<0.) return atan(y/x) + pi; | |
if(y<0. && x<0.) return atan(y/x) - pi; | |
if(y>0. && x==0.) return pi/2.; | |
if(y<0. && x==0.) return -pi/2.; | |
if(y==0. && x==0.) return pi/2.; // undefined usually | |
return pi/2.; | |
} | |
vec2 uv_polar(vec2 uv, vec2 center){ | |
vec2 c = uv - center; | |
float rad = length(c); | |
float ang = atan2(c.x,c.y); | |
return vec2(ang, rad); | |
} | |
vec2 uv_lens_half_sphere(vec2 uv, vec2 position, float radius, float refractivity){ | |
vec2 polar = uv_polar(uv, position); | |
float cone = clamp(1.-polar.y/radius, 0., 1.); | |
float halfsphere = sqrt(1.-pow(cone-1.,2.)); | |
float w = atan2(1.-cone, halfsphere); | |
float refrac_w = w-asin(sin(w)/refractivity); | |
float refrac_d = 1.-cone - sin(refrac_w)*halfsphere/cos(refrac_w); | |
vec2 refrac_uv = position + vec2(sin(polar.x),cos(polar.x))*refrac_d*radius; | |
return mix(uv, refrac_uv, float(length(uv-position)<radius)); | |
} | |
vec4 sat(sampler2D sampler_sat, vec2 pos, vec2 size){ | |
vec4 s = vec4(0); | |
s += texture2D(sampler_sat, pos ); | |
s += texture2D(sampler_sat, pos + size ); | |
s -= texture2D(sampler_sat, pos + size * vec2(1, 0) ); | |
s -= texture2D(sampler_sat, pos + size * vec2(0, 1) ); | |
return s / ( size.x * size.y ); | |
} | |
vec4 boxblur(sampler2D sampler_sat, vec2 uv, vec2 pixelSize, float boxsize){ | |
return sat(sampler_sat, uv - boxsize*0.5*pixelSize, boxsize*pixelSize) * pixelSize.x * pixelSize.y; | |
} | |
float line_segment(vec2 domain, vec2 p1, float d1, vec2 p2, float d2){ | |
float h = 1./(p2.x-p1.x); // helper registers | |
float h1 = (p2.y-p1.y)*h; | |
float h2 = 1./h1; | |
float xs = (-p1.y+h1*p1.x+h2*domain.x+domain.y)/(h2+h1);// coordinates of the point on the line between p1 and p2, | |
float ys = -h2*(xs-domain.x)+domain.y; // ^ orthogonally to the given point in the domain | |
float d = length(domain-vec2(xs,ys)); // the orthogonal distance from the domain point to the line (unlimited) | |
float s = 0.; // distance from domain point to p1 relative to p2 | |
if(p2.x == p1.x){ // division by zero fix | |
d = abs(domain.x - p1.x); | |
s = (p1.y-ys)/(p1.y-p2.y); | |
}else{ | |
s = (xs-p1.x)*h; | |
} | |
d = clamp(d*(d1*(1.-s)+d2*s),0., 1.); // adjusting the line thickness using a linear interpolation with s | |
float m1 = 0.; if(s > 0.) m1 = 1.; // masking out the segment between p1 and p2 | |
float m2 = 0.; if(s < 1.) m2 = 1.; | |
float result = clamp( m1*m2-d, 0., 1.); // return result as 1-distance in the range [0..1] | |
result = clamp(1.-length(domain-vec2(p1.x,p1.y))*d1-m1, result, 1.); // round corners if you will (half circles) | |
//result = clamp(1.-length(domain-vec2(p2.x,p2.y))*d2-m2, result, 1.); | |
return result; | |
} | |
float circle(vec2 uv, vec2 aspect, float scale){ | |
return clamp( 1. - length((uv-0.5)*aspect*scale), 0., 1.); | |
} | |
float sigmoid(float x) { | |
return 2./(1. + exp2(-x)) - 1.; | |
} | |
float smoothcircle(vec2 uv, vec2 aspect, float radius, float ramp){ | |
return 0.5 - sigmoid( ( length( (uv - 0.5) * aspect) - radius) * ramp) * 0.5; | |
} | |
// | |
// Description : Array and textureless GLSL 3D simplex noise function. | |
// Author : Ian McEwan, Ashima Arts. | |
// Maintainer : ijm | |
// Lastmod : 20110409 (stegu) | |
// License : Copyright (C) 2011 Ashima Arts. All rights reserved. | |
// Distributed under the MIT License. See LICENSE file. | |
// | |
//uniform float time; | |
varying vec3 vTexCoord3D; | |
varying vec3 vNormal; | |
varying vec3 vViewPosition; | |
vec4 permute( vec4 x ) { | |
return mod( ( ( x * 34.0 ) + 1.0 ) * x, 289.0 ); | |
} | |
vec4 taylorInvSqrt( vec4 r ) { | |
return 1.79284291400159 - 0.85373472095314 * r; | |
} | |
float snoise( vec3 v ) { | |
const vec2 C = vec2( 1.0 / 6.0, 1.0 / 3.0 ); | |
const vec4 D = vec4( 0.0, 0.5, 1.0, 2.0 ); | |
// First corner | |
vec3 i = floor( v + dot( v, C.yyy ) ); | |
vec3 x0 = v - i + dot( i, C.xxx ); | |
// Other corners | |
vec3 g = step( x0.yzx, x0.xyz ); | |
vec3 l = 1.0 - g; | |
vec3 i1 = min( g.xyz, l.zxy ); | |
vec3 i2 = max( g.xyz, l.zxy ); | |
// x0 = x0 - 0. + 0.0 * C | |
vec3 x1 = x0 - i1 + 1.0 * C.xxx; | |
vec3 x2 = x0 - i2 + 2.0 * C.xxx; | |
vec3 x3 = x0 - 1. + 3.0 * C.xxx; | |
// Permutations | |
i = mod( i, 289.0 ); | |
vec4 p = permute( permute( permute( | |
i.z + vec4( 0.0, i1.z, i2.z, 1.0 ) ) | |
+ i.y + vec4( 0.0, i1.y, i2.y, 1.0 ) ) | |
+ i.x + vec4( 0.0, i1.x, i2.x, 1.0 ) ); | |
// Gradients | |
// ( N*N points uniformly over a square, mapped onto an octahedron.) | |
float n_ = 1.0 / 7.0; // N=7 | |
vec3 ns = n_ * D.wyz - D.xzx; | |
vec4 j = p - 49.0 * floor( p * ns.z *ns.z ); // mod(p,N*N) | |
vec4 x_ = floor( j * ns.z ); | |
vec4 y_ = floor( j - 7.0 * x_ ); // mod(j,N) | |
vec4 x = x_ *ns.x + ns.yyyy; | |
vec4 y = y_ *ns.x + ns.yyyy; | |
vec4 h = 1.0 - abs( x ) - abs( y ); | |
vec4 b0 = vec4( x.xy, y.xy ); | |
vec4 b1 = vec4( x.zw, y.zw ); | |
vec4 s0 = floor( b0 ) * 2.0 + 1.0; | |
vec4 s1 = floor( b1 ) * 2.0 + 1.0; | |
vec4 sh = -step( h, vec4( 0.0 ) ); | |
vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; | |
vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; | |
vec3 p0 = vec3( a0.xy, h.x ); | |
vec3 p1 = vec3( a0.zw, h.y ); | |
vec3 p2 = vec3( a1.xy, h.z ); | |
vec3 p3 = vec3( a1.zw, h.w ); | |
// Normalise gradients | |
vec4 norm = taylorInvSqrt( vec4( dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3) ) ); | |
p0 *= norm.x; | |
p1 *= norm.y; | |
p2 *= norm.z; | |
p3 *= norm.w; | |
// Mix final noise value | |
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3) ), 0.0 ); | |
m = m * m; | |
return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), | |
dot(p2,x2), dot(p3,x3) ) ); | |
} | |
float heightMap( vec3 coord ) { | |
float n = abs( snoise( coord ) ); | |
n += 0.25 * abs( snoise( coord * 2.0 ) ); | |
n += 0.25 * abs( snoise( coord * 4.0 ) ); | |
n += 0.125 * abs( snoise( coord * 8.0 ) ); | |
n += 0.0625 * abs( snoise( coord * 16.0 ) ); | |
return n; | |
} | |
</script> | |
<script id="shader-fs-copy" type="x-shader/x-fragment"> | |
uniform sampler2D source; | |
void main(void) { | |
gl_FragColor = texture2D(source, uv); | |
} | |
</script> | |
<script id="shader-fs-init" type="x-shader/x-fragment"> | |
void main(void){ | |
gl_FragColor = vec4(0.); | |
} | |
</script> | |
<script id="shader-fs-move-particles" type="x-shader/x-fragment"> | |
uniform sampler2D sampler_prev; | |
uniform sampler2D sampler_prev_n; | |
uniform sampler2D sampler_blur; | |
uniform sampler2D sampler_blur2; | |
uniform sampler2D sampler_blur3; | |
uniform sampler2D sampler_blur4; | |
uniform sampler2D sampler_blur5; | |
uniform sampler2D sampler_blur6; | |
uniform sampler2D sampler_noise; | |
uniform sampler2D sampler_noise_n; | |
uniform sampler2D sampler_fluid; | |
uniform sampler2D sampler_particles; | |
uniform vec2 pixelSize; | |
uniform vec2 scale; | |
uniform vec4 rnd; | |
uniform float frame; | |
void main(void) { | |
vec4 p = texture2D(sampler_particles, uv); // location of the particle in the previous frame | |
vec2 v = texture2D(sampler_fluid, p.xy).xz; // motion vector for the actual location from the advection field texture | |
vec2 noise = texture2D(sampler_noise, uv + rnd.xy).xy-0.5; | |
float active = 1.;//float(frame < 10000.); | |
p.xy += v*scale*(1.-active); // verlet integration | |
p.xy += noise*(pixelSize/32. + pixelSize*float(frame<10000.+8. && frame > 10000.-8.)*0.); | |
p.xy += gradient(sampler_blur3, p.xy, pixelSize*16., vec4(0.,0.,-128.,0.))*pixelSize;//*(active); | |
// p.xy += rot90(gradient(sampler_blur2, p.xy, pixelSize*8., vec4(16,0,0,0)))*pixelSize; | |
// reset to random position when off canvas | |
gl_FragColor = mix( p, vec4(fract(p.xyz), 1.) , vec4((p.x < 0. || p.x > 1.)||(p.y < 0. || p.y > 1.)||(p.z < 0. || p.z > 1.)||(p.w < 0. || p.w > 1.))); | |
} | |
</script> | |
<script id="shader-fs-composite" type="x-shader/x-fragment"> | |
uniform sampler2D sampler_prev; | |
uniform sampler2D sampler_prev_n; | |
uniform sampler2D sampler_blur; | |
uniform sampler2D sampler_blur2; | |
uniform sampler2D sampler_blur3; | |
uniform sampler2D sampler_blur4; | |
uniform sampler2D sampler_blur5; | |
uniform sampler2D sampler_blur6; | |
uniform sampler2D sampler_noise; | |
uniform sampler2D sampler_noise_n; | |
uniform sampler2D sampler_fluid; | |
uniform sampler2D sampler_fluid_p; | |
uniform sampler2D sampler_particles; | |
uniform sampler2D sampler_particle_projection; | |
uniform sampler2D sampler_sat; | |
uniform vec4 rnd; | |
uniform vec4 rainbow; | |
uniform vec2 pixelSize; | |
uniform vec2 aspect; | |
uniform vec2 mouse; | |
uniform vec2 mouseV; | |
uniform float fps; | |
uniform float time; | |
uniform float frame; | |
void main(void) { | |
gl_FragColor = texture2D(sampler_prev, uv); // bypass | |
gl_FragColor.a = 1.; | |
} | |
</script> | |
<script id="shader-fs-advance" type="x-shader/x-fragment"> | |
uniform sampler2D sampler_prev; | |
uniform sampler2D sampler_prev_n; | |
uniform sampler2D sampler_blur; | |
uniform sampler2D sampler_blur2; | |
uniform sampler2D sampler_blur3; | |
uniform sampler2D sampler_blur4; | |
uniform sampler2D sampler_blur5; | |
uniform sampler2D sampler_blur6; | |
uniform sampler2D sampler_noise; | |
uniform sampler2D sampler_noise_n; | |
uniform sampler2D sampler_fluid; | |
uniform sampler2D sampler_particles; | |
uniform sampler2D sampler_particle_projection; | |
uniform vec4 rnd; | |
uniform vec4 rainbow; | |
uniform vec2 pixelSize; | |
uniform vec2 aspect; | |
uniform vec2 mouse; | |
uniform vec2 mouseV; | |
uniform float fps; | |
uniform float time; | |
uniform float frame; | |
/** | |
* Rock, Paper, Scissor! | |
* by Felix Woitzel, 2012 | |
*/ | |
void main(void) { | |
float warp = 2.5; | |
float variance = 0.01; | |
// voila, le smear finger | |
vec2 uv = mix(uv, uv-mouseV*pixelSize, smoothcircle(uv+0.5-mouse, aspect, 0.066, 128.)); | |
vec2 d = pixelSize * vec2(3.5); | |
vec4 e = texture2D(sampler_blur, uv + vec2(1.,0.)*d); | |
vec4 w = texture2D(sampler_blur, uv - vec2(1.,0.)*d); | |
vec4 s = texture2D(sampler_blur, uv + vec2(0.,1.)*d); | |
vec4 n = texture2D(sampler_blur, uv - vec2(0.,1.)*d); | |
vec2 dr, dg, db; | |
dr.x = e.r - w.r; | |
dr.y = s.r - n.r; | |
dg.x = e.g - w.g; | |
dg.y = s.g - n.g; | |
db.x = e.b - w.b; | |
db.y = s.b - n.b; | |
dr *= pixelSize * warp; | |
dg *= pixelSize * warp; | |
db *= pixelSize * warp; | |
gl_FragColor.r = texture2D(sampler_prev, uv + dr).r; | |
gl_FragColor.g = texture2D(sampler_prev, uv + dg).g; | |
gl_FragColor.b = texture2D(sampler_prev, uv + db).b; | |
gl_FragColor.rgb += variance; | |
gl_FragColor.rgb -= gl_FragColor.gbr * variance * 2.; | |
gl_FragColor = clamp( gl_FragColor, 0., 1.); | |
} | |
</script> | |
<script id="shader-fs-sat" type="x-shader/x-fragment"> | |
uniform sampler2D sampler_sat; // the texture that is also being rendered to | |
uniform vec2 offset; // add current value to the other pixel, variable for the two passes (rows, columns) | |
void main(){ | |
gl_FragColor = texture2D(sampler_sat, uv) + texture2D(sampler_sat, uv + offset); | |
} | |
</script> | |
<script id="shader-fs-blur-horizontal" type="x-shader/x-fragment"> | |
// original shader from http://www.gamerendering.com/2008/10/11/gaussian-blur-filter-shader/ | |
// horizontal blur fragment shader | |
uniform sampler2D src_tex; | |
uniform vec2 pixelSize; | |
void main(void) // fragment | |
{ | |
float h = pixelSize.x; | |
vec4 sum = vec4(0.0); | |
sum += texture2D(src_tex, vec2(uv.x - 4.0*h, uv.y) ) * 0.05; | |
sum += texture2D(src_tex, vec2(uv.x - 3.0*h, uv.y) ) * 0.09; | |
sum += texture2D(src_tex, vec2(uv.x - 2.0*h, uv.y) ) * 0.12; | |
sum += texture2D(src_tex, vec2(uv.x - 1.0*h, uv.y) ) * 0.15; | |
sum += texture2D(src_tex, vec2(uv.x + 0.0*h, uv.y) ) * 0.16; | |
sum += texture2D(src_tex, vec2(uv.x + 1.0*h, uv.y) ) * 0.15; | |
sum += texture2D(src_tex, vec2(uv.x + 2.0*h, uv.y) ) * 0.12; | |
sum += texture2D(src_tex, vec2(uv.x + 3.0*h, uv.y) ) * 0.09; | |
sum += texture2D(src_tex, vec2(uv.x + 4.0*h, uv.y) ) * 0.05; | |
gl_FragColor.xyz = sum.xyz/0.98; // normalize | |
gl_FragColor.a = 1.; | |
} | |
</script> | |
<script id="shader-fs-blur-vertical" type="x-shader/x-fragment"> | |
uniform sampler2D src_tex; | |
uniform vec2 pixelSize; | |
void main(void) // fragment | |
{ | |
float v = pixelSize.y; | |
vec4 sum = vec4(0.0); | |
sum += texture2D(src_tex, vec2(uv.x, - 4.0*v + uv.y) ) * 0.05; | |
sum += texture2D(src_tex, vec2(uv.x, - 3.0*v + uv.y) ) * 0.09; | |
sum += texture2D(src_tex, vec2(uv.x, - 2.0*v + uv.y) ) * 0.12; | |
sum += texture2D(src_tex, vec2(uv.x, - 1.0*v + uv.y) ) * 0.15; | |
sum += texture2D(src_tex, vec2(uv.x, + 0.0*v + uv.y) ) * 0.16; | |
sum += texture2D(src_tex, vec2(uv.x, + 1.0*v + uv.y) ) * 0.15; | |
sum += texture2D(src_tex, vec2(uv.x, + 2.0*v + uv.y) ) * 0.12; | |
sum += texture2D(src_tex, vec2(uv.x, + 3.0*v + uv.y) ) * 0.09; | |
sum += texture2D(src_tex, vec2(uv.x, + 4.0*v + uv.y) ) * 0.05; | |
gl_FragColor.xyz = sum.xyz/0.98; | |
gl_FragColor.a = 1.; | |
} | |
</script> | |
<script id="shader-fs-add-mouse-motion" type="x-shader/x-fragment"> | |
uniform sampler2D sampler_fluid; | |
uniform vec2 aspect; | |
uniform vec2 mouse; // mouse coordinate | |
uniform vec2 mouseV; // mouse velocity | |
uniform vec2 pixelSize; | |
uniform vec2 texSize; | |
float mouseFilter(vec2 uv){ | |
return clamp( 1.-length((uv-mouse)*texSize)/2., 0. , 1.); | |
} | |
void main(void){ | |
vec2 v = texture2D(sampler_fluid, uv).xz; | |
if(length(mouseV) > 0.) | |
v = mix(v, mouseV*2., mouseFilter(uv)*0.85); | |
gl_FragColor.xz = v; | |
} | |
</script> | |
<script id="shader-fs-advect" type="x-shader/x-fragment"> | |
uniform vec2 texSize; | |
uniform vec2 pixelSize; | |
uniform sampler2D sampler_fluid; | |
const float dt = .0005; | |
void main(void){ | |
vec2 v = texture2D(sampler_fluid, uv).xz; | |
vec2 D = -texSize*vec2(v.x, v.y)*dt; | |
vec2 Df = floor(D), Dd = D - Df; | |
vec2 uv = uv + Df*pixelSize; | |
vec2 uv0, uv1, uv2, uv3; | |
uv0 = uv + pixelSize*vec2(0.,0.); | |
uv1 = uv + pixelSize*vec2(1.,0.); | |
uv2 = uv + pixelSize*vec2(0.,1.); | |
uv3 = uv + pixelSize*vec2(1.,1.); | |
vec2 v0 = texture2D(sampler_fluid, uv0).xz; | |
vec2 v1 = texture2D(sampler_fluid, uv1).xz; | |
vec2 v2 = texture2D(sampler_fluid, uv2).xz; | |
vec2 v3 = texture2D(sampler_fluid, uv3).xz; | |
v = mix( mix( v0, v1, Dd.x), mix( v2, v3, Dd.x), Dd.y); | |
gl_FragColor.xz = v*(1.-border(uv, 1., texSize)); | |
} | |
</script> | |
<script id="shader-fs-p" type="x-shader/x-fragment"> | |
uniform vec2 pixelSize; | |
uniform vec2 texSize; | |
uniform sampler2D sampler_v; | |
uniform sampler2D sampler_p; | |
const float h = 1./1024.; | |
void main(void){ | |
vec2 v = texture2D(sampler_v, uv).xz; | |
float v_x = texture2D(sampler_v, uv - vec2(1.,0.)*pixelSize).r; | |
float v_y = texture2D(sampler_v, uv - vec2(0.,1.)*pixelSize).b; | |
float n = texture2D(sampler_p, uv- pixelSize*vec2(0.,1.)).r; | |
float w = texture2D(sampler_p, uv + pixelSize*vec2(1.,0.)).r; | |
float s = texture2D(sampler_p, uv + pixelSize*vec2(0.,1.)).r; | |
float e = texture2D(sampler_p, uv - pixelSize*vec2(1.,0.)).r; | |
float p = ( n + w + s + e - (v.x - v_x + v.y - v_y)*h ) * .25; | |
gl_FragColor.r = p; | |
gl_FragColor.ba = vec2(0.); // unused | |
} | |
</script> | |
<script id="shader-fs-div" type="x-shader/x-fragment"> | |
uniform vec2 texSize; | |
uniform vec2 pixelSize; | |
uniform sampler2D sampler_v; | |
uniform sampler2D sampler_p; | |
void main(void){ | |
float p = texture2D(sampler_p, uv).r; | |
vec2 v = texture2D(sampler_v, uv).xz; | |
float p_x = texture2D(sampler_p, uv + vec2(1.,0.)*pixelSize).r; | |
float p_y = texture2D(sampler_p, uv + vec2(0.,1.)*pixelSize).r; | |
v -= (vec2(p_x, p_y)-p)*512.; | |
gl_FragColor.xz = v; | |
} | |
</script> | |
<script type="x-shader/x-vertex" id="shader-particle-renderer-vs"> | |
attribute vec2 uv; // particle position lookup vector | |
uniform sampler2D sampler_particles; // particle positions in a float texture | |
uniform vec2 mouse; | |
void main() { | |
gl_Position = (texture2D(sampler_particles, uv) - 0.5)*2.; // pass em flat | |
gl_PointSize = 1.; | |
} | |
</script> | |
<script type="x-shader/x-fragment" id="shader-particle-renderer-fs"> | |
void main() { | |
gl_FragColor = vec4(1.0, 0.5, 0.166, 0.33); | |
} | |
</script> | |
<script type="text/javascript"> | |
function getShader(gl, id) { | |
var shaderScript = document.getElementById(id); | |
var str = ""; | |
var k = shaderScript.firstChild; | |
while (k) { | |
if (k.nodeType == 3) | |
str += k.textContent; | |
k = k.nextSibling; | |
} | |
var fsIncScript = document.getElementById("shader-fs-inc"); | |
var incStr = ""; | |
k = fsIncScript.firstChild; | |
while (k) { | |
if (k.nodeType == 3) | |
incStr += k.textContent; | |
k = k.nextSibling; | |
} | |
var shader; | |
if (shaderScript.type == "x-shader/x-fragment") { | |
str = incStr + str; | |
shader = gl.createShader(gl.FRAGMENT_SHADER); | |
} else if (shaderScript.type == "x-shader/x-vertex") | |
shader = gl.createShader(gl.VERTEX_SHADER); | |
else | |
return null; | |
gl.shaderSource(shader, str); | |
gl.compileShader(shader); | |
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) == 0) | |
alert("error compiling shader '" + id + "'\n\n" + gl.getShaderInfoLog(shader)); | |
return shader; | |
} | |
window.requestAnimFrame = (function() { | |
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame | |
|| window.msRequestAnimationFrame || function(callback) { | |
window.setTimeout(callback, 1000 / desiredFramerate); | |
}; | |
})(); | |
var gl; | |
var ext; | |
var prog_copy; | |
var prog_advance; | |
var prog_composite; | |
var prog_blur_horizontal; | |
var prog_blur_vertical; | |
var prog_fluid_init; | |
var prog_fluid_add_mouse_motion; | |
var prog_fluid_advect; | |
var prog_fluid_p; | |
var prog_fluid_div; | |
var prog_move_particles; | |
var prog_render_particles; | |
var prog_sat; | |
var FBO_main; | |
var FBO_main2; | |
var FBO_noise; | |
var FBO_sat; | |
var FBO_blur; | |
var FBO_blur2; | |
var FBO_blur3; | |
var FBO_blur4; | |
var FBO_blur5; | |
var FBO_blur6; | |
var FBO_helper; | |
var FBO_helper2; | |
var FBO_helper3; | |
var FBO_helper4; | |
var FBO_helper5; | |
var FBO_helper6; | |
var FBO_fluid_v; | |
var FBO_fluid_p; | |
var FBO_fluid_store; | |
var FBO_fluid_backbuffer; | |
var FBO_particles; // particle positions in a texture | |
var FBO_particles2; // double buffer | |
var FBO_particle_projection; // particle render target for projection feedback effects | |
var texture_main_n; // main, nearest pixel | |
var texture_main_l; // main, linear interpolated access on the same buffer | |
var texture_main2_n; // main double buffer, nearest | |
var texture_main2_l; // main double buffer, linear | |
var texture_sat; // summed area table / integral image | |
var texture_blur; // full resolution blur result | |
var texture_blur2; // double blur | |
var texture_blur3; // quad blur | |
var texture_blur4; // use low resolutions wisely ;) | |
var texture_blur5; | |
var texture_blur6; | |
var texture_helper; // needed for multi-pass shader programs (2-pass Gaussian blur) | |
var texture_helper2; // (1/4 resolution ) | |
var texture_helper3; // (1/16 resolution ) | |
var texture_helper4; // (1/256 resolution ) | |
var texture_helper5; | |
var texture_helper6; | |
var texture_noise_n; // nearest pixel access | |
var texture_noise_l; // linear interpolated | |
var texture_fluid_v; // velocities | |
var texture_fluid_p; // pressure | |
var texture_fluid_store; | |
var texture_fluid_backbuffer; | |
var texture_particles; | |
var texture_particles2; | |
var texture_particle_projection; | |
// main texture loop dimensions | |
var sizeX = 512; // must be powers of 2 | |
var sizeY = 512; | |
var viewX = sizeX; // viewport size (ideally exactly the texture size) | |
var viewY = sizeY; | |
// particle positions will be stored in a texture of that size | |
var particlesWidth = 512; | |
var particlesHeight = 512; | |
var particleCount = particlesWidth * particlesHeight; // can also be set to lower than particlesWidth * particlesHeight | |
var useParticles = false; | |
var useProjectionFeedback = false; // rendering half a million points can slow things down significantly, don't render to texture if not needed | |
var useFluidSimulation = true; // the textures will be initialized anyway | |
var simScale = 8; // for better performance, the fluid simulation will be calculated for cells this times bigger than the main texture's pixels (powers of 2) | |
var useSummedAreaTable = false; // Useful for superfast multiscale boxblur. The linearized integral image calculation is the most expensive filter here, you've been warned | |
var maxGaussianBlurLevelUsed = 4; // not yet implemented, but doesn't cost much either. ;) | |
var desiredFramerate = 30; | |
var startFullpage = false; | |
var renderParticlesOnly = false; | |
var alwaysUseFlush = true; // experimental setting to toggle finite time execution forces (false was ok on Win7 here, but glitches on MacOS X) | |
// don't change vars below | |
var frame = 0; // frame counter to be resetted every 1000ms | |
var framecount = 0; // not resetted | |
var mainBufferToggle = 1; | |
var halted = false; | |
var fps, fpsDisplayUpdateTimer; | |
var time, starttime = new Date().getTime(); | |
var canvasMouseX = 0; | |
var canvasMouseY = 0; | |
var mouseX = 0.5; | |
var mouseY = 0.5; | |
var oldMouseX = 0; | |
var oldMouseY = 0; | |
var mouseDx = 0; | |
var mouseDy = 0; | |
// geometry | |
var particleBuffer, squareBuffer, hLineBuffer, vLineBuffer; | |
function load() { | |
clearInterval(fpsDisplayUpdateTimer); | |
var c = document.getElementById("c"); | |
try { | |
gl = c.getContext("experimental-webgl", { | |
depth : false, | |
preserveDrawingBuffer: true | |
}); | |
} catch (e) { | |
} | |
if (!gl) { | |
alert("Meh! Y u no support experimental WebGL !?!"); | |
return; | |
} | |
[ "OES_texture_float", "OES_standard_derivatives", "OES_texture_float_linear" ].forEach(function(name) { | |
console.log("check " + name); | |
try { | |
ext = gl.getExtension(name); | |
} catch (e) { | |
alert(e); | |
} | |
if (!ext) { | |
alert("Meh! Y u no support " + name + " !?!\n(Chrome 29 or Firefox 24 will do fine)"); | |
return; | |
} | |
ext = false; | |
}); | |
if (gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) == 0) { | |
alert("Meh! Y u no support vertex shader textures !?!"); | |
return; | |
} | |
document.onmousemove = function(evt) { | |
mouseX = evt.pageX / viewX; | |
mouseY = 1 - evt.pageY / viewY; | |
canvasMouseX = Math.min(evt.layerX, c.width); | |
canvasMouseY = c.height - evt.layerY; | |
}; | |
if (startFullpage) { | |
viewX = window.innerWidth; | |
viewY = window.innerHeight; | |
} | |
c.width = viewX; | |
c.height = viewY; | |
prog_copy = createAndLinkProgram("shader-fs-copy"); | |
prog_advance = createAndLinkProgram("shader-fs-advance"); | |
prog_composite = createAndLinkProgram("shader-fs-composite"); | |
prog_blur_horizontal = createAndLinkProgram("shader-fs-blur-horizontal"); | |
prog_blur_vertical = createAndLinkProgram("shader-fs-blur-vertical"); | |
prog_sat = createAndLinkProgram("shader-fs-sat"); | |
prog_fluid_init = createAndLinkProgram("shader-fs-init"); | |
prog_fluid_add_mouse_motion = createAndLinkProgram("shader-fs-add-mouse-motion"); | |
prog_fluid_advect = createAndLinkProgram("shader-fs-advect"); | |
prog_fluid_p = createAndLinkProgram("shader-fs-p"); | |
prog_fluid_div = createAndLinkProgram("shader-fs-div"); | |
prog_move_particles = createAndLinkProgram("shader-fs-move-particles"); | |
triangleStripGeometry = { | |
vertices : new Float32Array([ -1, -1, 0, 1, -1, 0, -1, 1, 0, 1, 1, 0 ]), | |
texCoords : new Float32Array([ 0, 0, 1, 0, 0, 1, 1, 1 ]), | |
vertexSize : 3, | |
vertexCount : 4, | |
type : gl.TRIANGLE_STRIP | |
}; | |
createTexturedGeometryBuffer(triangleStripGeometry); | |
hLineVertices = []; | |
hLineTexCoords = []; | |
for ( var y = 0; y < sizeY; y++) { | |
hLineVertices.push(-1, -1 + 2 * y / sizeY, 0, 1, -1 + 2 * y / sizeY, 0); | |
hLineTexCoords.push(0. / sizeX, (y - 0.5) / sizeY, (sizeX + 0.) / sizeX, (y - 0.5) / sizeY); | |
} | |
hLineGeometry = { | |
vertices : new Float32Array(hLineVertices), | |
texCoords : new Float32Array(hLineTexCoords), | |
vertexSize : 3, | |
vertexCount : sizeY * 2, | |
type : gl.LINES | |
}; | |
vLineVertices = []; | |
vLineTexCoords = []; | |
for ( var x = 0; x < sizeX; x++) { | |
vLineVertices.push(-1 + 2 * x / sizeX, -1, 0, -1 + 2 * x / sizeX, 1, 0); | |
vLineTexCoords.push((x - 0.5) / sizeX, 0. / sizeY, (x - 0.5) / sizeX, (sizeY + 0.) / sizeY); | |
} | |
vLineGeometry = { | |
vertices : new Float32Array(vLineVertices), | |
texCoords : new Float32Array(vLineTexCoords), | |
vertexSize : 3, | |
vertexCount : sizeX * 2, | |
type : gl.LINES | |
}; | |
createTexturedGeometryBuffer(hLineGeometry); | |
createTexturedGeometryBuffer(vLineGeometry); | |
squareBuffer = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, squareBuffer); | |
var aPosLoc = gl.getAttribLocation(prog_advance, "aPos"); | |
var aTexLoc = gl.getAttribLocation(prog_advance, "aTexCoord"); | |
gl.enableVertexAttribArray(aPosLoc); | |
gl.enableVertexAttribArray(aTexLoc); | |
var verticesAndTexCoords = new Float32Array([ -1, -1, 1, -1, -1, 1, 1, 1, // one square of a quad! | |
0, 0, 1, 0, 0, 1, 1, 1 ] // hello texture, you be full | |
); | |
gl.bufferData(gl.ARRAY_BUFFER, verticesAndTexCoords, gl.STATIC_DRAW); | |
gl.vertexAttribPointer(aPosLoc, 2, gl.FLOAT, gl.FALSE, 8, 0); | |
gl.vertexAttribPointer(aTexLoc, 2, gl.FLOAT, gl.FALSE, 8, 32); | |
var noisePixels = [], pixels = [], simpixels = [], pixels2 = [], pixels3 = [], pixels4 = [], pixels5 = [], pixels6 = [], particles = [], particlesIdx = []; | |
var dX = 1 / particlesWidth; | |
var dY = 1 / particlesHeight; | |
for ( var j = 0; j < sizeY; j++) { | |
for ( var i = 0; i < sizeX; i++) { | |
noisePixels.push(Math.random(), Math.random(), Math.random(), 1); | |
pixels.push(0, 0, 0, 1); | |
if (i < sizeX / simScale && j < sizeY / simScale) | |
simpixels.push(0, 0, 0, 1); | |
if (i < sizeX / 2 && j < sizeY / 2) | |
pixels2.push(0, 0, 0, 1); | |
if (i < sizeX / 4 && j < sizeY / 4) | |
pixels3.push(0, 0, 0, 1); | |
if (i < sizeX / 8 && j < sizeY / 8) | |
pixels4.push(0, 0, 0, 1); | |
if (i < sizeX / 16 && j < sizeY / 16) | |
pixels5.push(0, 0, 0, 1); | |
if (i < sizeX / 32 && j < sizeY / 32) | |
pixels6.push(0, 0, 0, 1); | |
if (i < particlesWidth && j < particlesHeight) { | |
particles.push(dX / 2 + i * dX, dY / 2 + j * dY, 0); // initial particle positions, here: uniform distribution | |
} | |
} | |
} | |
for ( var i = 0; i < particlesHeight; i++) { | |
for ( var j = 0; j < particlesWidth; j++) { | |
particlesIdx.push(dX / 2 + j * dX, dY / 2 + i * dY); // coordinate lookup vectors (center of pixels) | |
} | |
} | |
FBO_main = gl.createFramebuffer(); | |
FBO_main2 = gl.createFramebuffer(); | |
var glPixels; | |
glPixels = new Float32Array(noisePixels); | |
texture_main_n = createAndBindTexture(glPixels, 1, FBO_main, gl.NEAREST); | |
texture_main2_n = createAndBindTexture(glPixels, 1, FBO_main2, gl.NEAREST); | |
glPixels = new Float32Array(noisePixels); | |
texture_main_l = createAndBindTexture(glPixels, 1, FBO_main, gl.LINEAR); | |
texture_main2_l = createAndBindTexture(glPixels, 1, FBO_main2, gl.LINEAR); | |
FBO_fluid_p = gl.createFramebuffer(); | |
FBO_fluid_v = gl.createFramebuffer(); | |
FBO_fluid_store = gl.createFramebuffer(); | |
FBO_fluid_backbuffer = gl.createFramebuffer(); | |
texture_fluid_v = createAndBindSimulationTexture(new Float32Array(simpixels), FBO_fluid_v); | |
texture_fluid_p = createAndBindSimulationTexture(new Float32Array(simpixels), FBO_fluid_p); | |
texture_fluid_store = createAndBindSimulationTexture(new Float32Array(simpixels), FBO_fluid_store); | |
texture_fluid_backbuffer = createAndBindSimulationTexture(new Float32Array(simpixels), FBO_fluid_backbuffer); | |
FBO_particle_projection = gl.createFramebuffer(); | |
texture_particle_projection = createAndBindTexture(new Float32Array(pixels), 1, FBO_particle_projection, gl.LINEAR); | |
FBO_helper = gl.createFramebuffer(); | |
FBO_helper2 = gl.createFramebuffer(); | |
FBO_helper3 = gl.createFramebuffer(); | |
FBO_helper4 = gl.createFramebuffer(); | |
FBO_helper5 = gl.createFramebuffer(); | |
FBO_helper6 = gl.createFramebuffer(); | |
texture_helper = createAndBindTexture(new Float32Array(pixels), 1, FBO_helper, gl.NEAREST); // helper buffers for the two-pass Gaussian blur calculation basically | |
texture_helper2 = createAndBindTexture(new Float32Array(pixels2), 2, FBO_helper2, gl.NEAREST); | |
texture_helper3 = createAndBindTexture(new Float32Array(pixels3), 4, FBO_helper3, gl.NEAREST); | |
texture_helper4 = createAndBindTexture(new Float32Array(pixels4), 8, FBO_helper4, gl.NEAREST); | |
texture_helper5 = createAndBindTexture(new Float32Array(pixels5), 16, FBO_helper5, gl.NEAREST); | |
texture_helper6 = createAndBindTexture(new Float32Array(pixels6), 32, FBO_helper6, gl.NEAREST); | |
FBO_blur = gl.createFramebuffer(); | |
FBO_blur2 = gl.createFramebuffer(); | |
FBO_blur3 = gl.createFramebuffer(); | |
FBO_blur4 = gl.createFramebuffer(); | |
FBO_blur5 = gl.createFramebuffer(); | |
FBO_blur6 = gl.createFramebuffer(); | |
texture_blur = createAndBindTexture(new Float32Array(pixels), 1, FBO_blur, gl.LINEAR); | |
texture_blur2 = createAndBindTexture(new Float32Array(pixels2), 2, FBO_blur2, gl.LINEAR); | |
texture_blur3 = createAndBindTexture(new Float32Array(pixels3), 4, FBO_blur3, gl.LINEAR); | |
texture_blur4 = createAndBindTexture(new Float32Array(pixels4), 8, FBO_blur4, gl.LINEAR); | |
texture_blur5 = createAndBindTexture(new Float32Array(pixels5), 16, FBO_blur5, gl.LINEAR); | |
texture_blur6 = createAndBindTexture(new Float32Array(pixels6), 32, FBO_blur6, gl.LINEAR); | |
FBO_sat = gl.createFramebuffer(); | |
texture_sat = createAndBindTexture(new Float32Array(pixels), 1, FBO_sat, gl.NEAREST); | |
FBO_noise = gl.createFramebuffer(); | |
glPixels = new Float32Array(noisePixels); | |
texture_noise_n = createAndBindTexture(glPixels, 1, FBO_noise, gl.NEAREST); | |
texture_noise_l = createAndBindTexture(glPixels, 1, FBO_noise, gl.LINEAR); | |
FBO_particles = gl.createFramebuffer(); | |
texture_particles = createAndBindParticleTexture(new Float32Array(particles), FBO_particles); | |
FBO_particles2 = gl.createFramebuffer(); | |
texture_particles2 = createAndBindParticleTexture(new Float32Array(particles), FBO_particles2); | |
// lesson learned: the (frame) buffer location that we pass to the vertex shader has to be bound to the program before linking! | |
var aParticleLoc = 2; // no getAttributeLoc | |
prog_render_particles = createAndLinkParticleRenderer(aParticleLoc); | |
gl.useProgram(prog_render_particles); | |
gl.uniform1i(gl.getUniformLocation(prog_render_particles, "sampler_particles"), 0); | |
gl.enableVertexAttribArray(aParticleLoc); | |
particleBuffer = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, particleBuffer); | |
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(particlesIdx), gl.STATIC_DRAW); | |
gl.vertexAttribPointer(aParticleLoc, 2, gl.FLOAT, false, 0, 0); | |
gl.activeTexture(gl.TEXTURE2); | |
gl.bindTexture(gl.TEXTURE_2D, texture_blur); | |
gl.activeTexture(gl.TEXTURE3); | |
gl.bindTexture(gl.TEXTURE_2D, texture_blur2); | |
gl.activeTexture(gl.TEXTURE4); | |
gl.bindTexture(gl.TEXTURE_2D, texture_blur3); | |
gl.activeTexture(gl.TEXTURE5); | |
gl.bindTexture(gl.TEXTURE_2D, texture_blur4); | |
gl.activeTexture(gl.TEXTURE6); | |
gl.bindTexture(gl.TEXTURE_2D, texture_blur5); | |
gl.activeTexture(gl.TEXTURE7); | |
gl.bindTexture(gl.TEXTURE_2D, texture_blur6); | |
gl.activeTexture(gl.TEXTURE8); | |
gl.bindTexture(gl.TEXTURE_2D, texture_noise_l); | |
gl.activeTexture(gl.TEXTURE9); | |
gl.bindTexture(gl.TEXTURE_2D, texture_noise_n); | |
gl.activeTexture(gl.TEXTURE10); | |
gl.bindTexture(gl.TEXTURE_2D, texture_fluid_v); | |
gl.activeTexture(gl.TEXTURE11); | |
gl.bindTexture(gl.TEXTURE_2D, texture_fluid_p); | |
gl.activeTexture(gl.TEXTURE12); | |
gl.bindTexture(gl.TEXTURE_2D, texture_particles); // to be swapped anyways | |
gl.activeTexture(gl.TEXTURE13); | |
gl.bindTexture(gl.TEXTURE_2D, texture_particle_projection); | |
gl.activeTexture(gl.TEXTURE14); | |
gl.bindTexture(gl.TEXTURE_2D, texture_sat); | |
calculateBlurTexture(); | |
fluidInit(FBO_fluid_v); | |
fluidInit(FBO_fluid_p); | |
fluidInit(FBO_fluid_store); | |
fluidInit(FBO_fluid_backbuffer); | |
fpsDisplayUpdateTimer = setInterval(fr, 1000); | |
time = new Date().getTime() - starttime; | |
gl.blendFunc(gl.SRC_ALPHA, gl.ONE); | |
gl.clearColor(0, 0, 0, 1); | |
anim(); | |
startAudio(); | |
} | |
function createTexturedGeometryBuffer(geometry) { | |
geometry.buffer = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, geometry.buffer); | |
geometry.aPosLoc = gl.getAttribLocation(prog_advance, "aPos"); // we could take any program here, they all use the same vertex shader | |
gl.enableVertexAttribArray(geometry.aPosLoc); | |
geometry.aTexLoc = gl.getAttribLocation(prog_advance, "aTexCoord"); | |
gl.enableVertexAttribArray(geometry.aTexLoc); | |
geometry.texCoordOffset = geometry.vertices.byteLength; | |
gl.bufferData(gl.ARRAY_BUFFER, geometry.texCoordOffset + geometry.texCoords.byteLength, gl.STATIC_DRAW); | |
gl.bufferSubData(gl.ARRAY_BUFFER, 0, geometry.vertices); | |
gl.bufferSubData(gl.ARRAY_BUFFER, geometry.texCoordOffset, geometry.texCoords); | |
setGeometryVertexAttribPointers(geometry); | |
} | |
function setGeometryVertexAttribPointers(geometry) { | |
gl.vertexAttribPointer(geometry.aPosLoc, geometry.vertexSize, gl.FLOAT, gl.FALSE, 0, 0); | |
gl.vertexAttribPointer(geometry.aTexLoc, 2, gl.FLOAT, gl.FALSE, 0, geometry.texCoordOffset); | |
} | |
function createAndLinkProgram(fsId) { | |
var program = gl.createProgram(); | |
gl.attachShader(program, getShader(gl, "shader-vs")); | |
gl.attachShader(program, getShader(gl, fsId)); | |
gl.linkProgram(program); | |
return program; | |
} | |
function createAndLinkParticleRenderer(aParticleLoc) { | |
var program = gl.createProgram(); | |
gl.attachShader(program, getShader(gl, "shader-particle-renderer-vs")); | |
gl.attachShader(program, getShader(gl, "shader-particle-renderer-fs")); | |
gl.bindAttribLocation(program, aParticleLoc, "uv"); // can't use getAttribLocation later so we must bind before linking | |
gl.linkProgram(program); | |
return program; | |
} | |
function createAndBindTexture(glPixels, scale, fbo, filter) { | |
var texture = gl.createTexture(); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); | |
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, sizeX / scale, sizeY / scale, 0, gl.RGBA, gl.FLOAT, glPixels); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); | |
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); | |
return texture; | |
} | |
function createAndBindParticleTexture(glPixels, fbo) { | |
var texture = gl.createTexture(); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); | |
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, particlesWidth, particlesHeight, 0, gl.RGB, gl.FLOAT, glPixels); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); | |
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); | |
return texture; | |
} | |
function createAndBindSimulationTexture(glPixels, fbo) { | |
var texture = gl.createTexture(); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); | |
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, sizeX / simScale, sizeY / simScale, 0, gl.RGBA, gl.FLOAT, glPixels); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); | |
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); | |
return texture; | |
} | |
function fluidInit(fbo) { | |
gl.viewport(0, 0, sizeX / simScale, sizeY / simScale); | |
gl.useProgram(prog_fluid_init); | |
renderAsTriangleStrip(fbo); | |
} | |
function setUniforms(program) { | |
gl.uniform4f(gl.getUniformLocation(program, "rnd"), Math.random(), Math.random(), Math.random(), Math.random()); | |
gl.uniform4f(gl.getUniformLocation(program, "rainbow"), rainbowR, rainbowG, rainbowB, 1); | |
gl.uniform2f(gl.getUniformLocation(program, "texSize"), sizeX, sizeY); | |
gl.uniform2f(gl.getUniformLocation(program, "pixelSize"), 1. / sizeX, 1. / sizeY); | |
gl.uniform2f(gl.getUniformLocation(program, "aspect"), Math.max(1, viewX / viewY), Math.max(1, viewY / viewX)); | |
gl.uniform2f(gl.getUniformLocation(program, "mouse"), mouseX, mouseY); | |
gl.uniform2f(gl.getUniformLocation(program, "mouseV"), mouseDx, mouseDy); | |
gl.uniform1f(gl.getUniformLocation(program, "fps"), fps); | |
gl.uniform1f(gl.getUniformLocation(program, "time"), time); | |
gl.uniform1f(gl.getUniformLocation(program, "frame"), framecount); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_prev"), 0); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_prev_n"), 1); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_blur"), 2); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_blur2"), 3); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_blur3"), 4); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_blur4"), 5); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_blur5"), 6); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_blur6"), 7); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_noise"), 8); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_noise_n"), 9); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_fluid"), 10); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_fluid_p"), 11); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_particles"), 12); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_particle_projection"), 13); | |
gl.uniform1i(gl.getUniformLocation(program, "sampler_sat"), 14); | |
} | |
function useGeometry(geometry) { | |
gl.bindBuffer(gl.ARRAY_BUFFER, geometry.buffer); | |
setGeometryVertexAttribPointers(geometry); | |
} | |
function renderGeometry(geometry, targetFBO) { | |
useGeometry(geometry); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, targetFBO); | |
gl.drawArrays(geometry.type, 0, geometry.vertexCount); | |
if (alwaysUseFlush) | |
gl.flush(); | |
} | |
function renderAsTriangleStrip(targetFBO) { | |
renderGeometry(triangleStripGeometry, targetFBO); | |
} | |
function renderParticles(targetFBO) { | |
gl.bindBuffer(gl.ARRAY_BUFFER, particleBuffer); | |
if (targetFBO == null) | |
gl.viewport(0, 0, viewX, viewY); | |
else | |
gl.viewport(0, 0, sizeX, sizeY); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, targetFBO); | |
gl.useProgram(prog_render_particles); | |
gl.activeTexture(gl.TEXTURE12); | |
if (mainBufferToggle < 0) { | |
gl.bindTexture(gl.TEXTURE_2D, texture_particles2); | |
} else { | |
gl.bindTexture(gl.TEXTURE_2D, texture_particles); | |
} | |
gl.uniform1i(gl.getUniformLocation(prog_render_particles, "sampler_particles"), 12); // input for the vertex shader | |
gl.uniform2f(gl.getUniformLocation(prog_render_particles, "mouse"), mouseX, mouseY); | |
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); | |
gl.enable(gl.BLEND); | |
gl.drawArrays(gl.POINTS, 0, particleCount); | |
gl.disable(gl.BLEND); | |
if (alwaysUseFlush) | |
gl.flush(); | |
} | |
function renderAsHLines(targetFBO) { | |
useGeometry(hLineGeometry); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, targetFBO); | |
for ( var y = 1; y < sizeY; y++) { | |
gl.drawArrays(gl.LINES, y * 2, 2); | |
if (alwaysUseFlush) | |
gl.flush(); | |
} | |
} | |
function renderAsVLines(targetFBO) { | |
useGeometry(vLineGeometry); | |
gl.bindFramebuffer(gl.FRAMEBUFFER, targetFBO); | |
for ( var x = 1; x < sizeX; x++) { | |
gl.drawArrays(gl.LINES, x * 2, 2); | |
if (alwaysUseFlush) | |
gl.flush(); | |
} | |
} | |
function calculateSummedAreaTable(sourceTex) { | |
gl.viewport(0, 0, particlesWidth, particlesHeight); | |
gl.useProgram(prog_copy); | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, sourceTex); | |
renderAsTriangleStrip(FBO_sat); | |
gl.useProgram(prog_sat); | |
gl.bindTexture(gl.TEXTURE_2D, texture_sat); | |
gl.uniform1i(gl.getUniformLocation(prog_sat, "sampler_sat"), 0); | |
gl.uniform2f(gl.getUniformLocation(prog_sat, "offset"), 0, -1 / sizeY); | |
renderAsHLines(FBO_sat); | |
gl.uniform2f(gl.getUniformLocation(prog_sat, "offset"), -1 / sizeX, 0); | |
renderAsVLines(FBO_sat); | |
} | |
function calculateBlurTextures(texture_source) { | |
calculateBlurTexture(texture_source, texture_blur, FBO_blur, texture_helper, FBO_helper, 1); | |
calculateBlurTexture(texture_blur, texture_blur2, FBO_blur2, texture_helper2, FBO_helper2, 2); | |
calculateBlurTexture(texture_blur2, texture_blur3, FBO_blur3, texture_helper3, FBO_helper3, 4); | |
calculateBlurTexture(texture_blur3, texture_blur4, FBO_blur4, texture_helper4, FBO_helper4, 8); | |
calculateBlurTexture(texture_blur4, texture_blur5, FBO_blur5, texture_helper5, FBO_helper5, 16); | |
calculateBlurTexture(texture_blur5, texture_blur6, FBO_blur6, texture_helper6, FBO_helper6, 32); | |
} | |
function calculateBlurTexture(sourceTex, targetTex, targetFBO, helperTex, helperFBO, scale) { | |
// copy source | |
gl.viewport(0, 0, sizeX / scale, sizeY / scale); | |
gl.useProgram(prog_copy); | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, sourceTex); | |
renderAsTriangleStrip(targetFBO); | |
// blur vertically | |
gl.viewport(0, 0, sizeX / scale, sizeY / scale); | |
gl.useProgram(prog_blur_vertical); | |
gl.uniform2f(gl.getUniformLocation(prog_blur_vertical, "pixelSize"), scale / sizeX, scale / sizeY); | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, targetTex); | |
renderAsTriangleStrip(helperFBO); | |
// blur horizontally | |
gl.viewport(0, 0, sizeX / scale, sizeY / scale); | |
gl.useProgram(prog_blur_horizontal); | |
gl.uniform2f(gl.getUniformLocation(prog_blur_horizontal, "pixelSize"), scale / sizeX, scale / sizeY); | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, helperTex); | |
renderAsTriangleStrip(targetFBO); | |
} | |
function stepParticles() { | |
gl.viewport(0, 0, particlesWidth, particlesHeight); | |
gl.useProgram(prog_move_particles); | |
gl.uniform4f(gl.getUniformLocation(prog_move_particles, "rnd"), Math.random(), Math.random(), Math.random(), Math.random()); | |
gl.uniform1f(gl.getUniformLocation(prog_move_particles, "frame"), framecount); | |
gl.uniform2f(gl.getUniformLocation(prog_move_particles, "pixelSize"), 1. / sizeX, 1. / sizeY); | |
gl.uniform2f(gl.getUniformLocation(prog_move_particles, "scale"), 2. / simScale / particlesWidth, 2. / simScale / particlesHeight); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_prev"), 0); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_prev_n"), 1); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_blur"), 2); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_blur2"), 3); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_blur3"), 4); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_blur4"), 5); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_blur5"), 6); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_blur6"), 7); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_noise"), 8); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_noise_n"), 9); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_fluid"), 10); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_fluid_p"), 11); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_particles"), 12); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_particle_projection"), 13); | |
gl.uniform1i(gl.getUniformLocation(prog_move_particles, "sampler_sat"), 14); | |
if (mainBufferToggle > 0) { | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, texture_fluid_v); | |
gl.activeTexture(gl.TEXTURE12); | |
gl.bindTexture(gl.TEXTURE_2D, texture_particles); | |
renderAsTriangleStrip(FBO_particles2) | |
} else { | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, texture_fluid_v); | |
gl.activeTexture(gl.TEXTURE12); | |
gl.bindTexture(gl.TEXTURE_2D, texture_particles2); | |
renderAsTriangleStrip(FBO_particles); | |
} | |
} | |
function fluidSimulationStep() { | |
addMouseMotion(); | |
advect(); | |
diffuse(); | |
} | |
function addMouseMotion() { | |
gl.viewport(0, 0, (sizeX / simScale), (sizeY / simScale)); | |
gl.useProgram(prog_fluid_add_mouse_motion); | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, texture_fluid_v); | |
gl.uniform2f(gl.getUniformLocation(prog_fluid_add_mouse_motion, "aspect"), Math.max(1, viewX / viewY), Math.max(1, viewY / viewX)); | |
gl.uniform2f(gl.getUniformLocation(prog_fluid_add_mouse_motion, "mouse"), mouseX, mouseY); | |
gl.uniform2f(gl.getUniformLocation(prog_fluid_add_mouse_motion, "mouseV"), mouseDx, mouseDy); | |
gl.uniform2f(gl.getUniformLocation(prog_fluid_add_mouse_motion, "pixelSize"), 1. / (sizeX / simScale), 1. / (sizeY / simScale)); | |
gl.uniform2f(gl.getUniformLocation(prog_fluid_add_mouse_motion, "texSize"), (sizeX / simScale), (sizeY / simScale)); | |
renderAsTriangleStrip(FBO_fluid_backbuffer); | |
} | |
function advect() { | |
gl.viewport(0, 0, (sizeX / simScale), (sizeY / simScale)); | |
gl.useProgram(prog_fluid_advect); | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, texture_fluid_backbuffer); | |
gl.uniform2f(gl.getUniformLocation(prog_fluid_advect, "pixelSize"), 1. / (sizeX / simScale), 1. / (sizeY / simScale)); | |
gl.uniform2f(gl.getUniformLocation(prog_fluid_advect, "texSize"), (sizeX / simScale), (sizeY / simScale)); | |
renderAsTriangleStrip(FBO_fluid_v); | |
} | |
function diffuse() { | |
for ( var i = 0; i < 8; i++) { | |
gl.viewport(0, 0, (sizeX / simScale), (sizeY / simScale)); | |
gl.useProgram(prog_fluid_p); | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, texture_fluid_v); | |
gl.activeTexture(gl.TEXTURE1); | |
gl.bindTexture(gl.TEXTURE_2D, texture_fluid_p); | |
gl.uniform2f(gl.getUniformLocation(prog_fluid_p, "texSize"), (sizeX / simScale), (sizeY / simScale)); | |
gl.uniform2f(gl.getUniformLocation(prog_fluid_p, "pixelSize"), 1. / (sizeX / simScale), 1. / (sizeY / simScale)); | |
gl.uniform1i(gl.getUniformLocation(prog_fluid_p, "sampler_v"), 0); | |
gl.uniform1i(gl.getUniformLocation(prog_fluid_p, "sampler_p"), 1); | |
renderAsTriangleStrip(FBO_fluid_backbuffer); | |
gl.viewport(0, 0, (sizeX / simScale), (sizeY / simScale)); | |
gl.useProgram(prog_fluid_p); | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, texture_fluid_v); | |
gl.activeTexture(gl.TEXTURE1); | |
gl.bindTexture(gl.TEXTURE_2D, texture_fluid_backbuffer); | |
gl.uniform2f(gl.getUniformLocation(prog_fluid_p, "texSize"), (sizeX / simScale), (sizeY / simScale)); | |
gl.uniform2f(gl.getUniformLocation(prog_fluid_p, "pixelSize"), 1. / (sizeX / simScale), 1. / (sizeY / simScale)); | |
gl.uniform1i(gl.getUniformLocation(prog_fluid_p, "sampler_v"), 0); | |
gl.uniform1i(gl.getUniformLocation(prog_fluid_p, "sampler_p"), 1); | |
renderAsTriangleStrip(FBO_fluid_p); | |
} | |
gl.viewport(0, 0, (sizeX / simScale), (sizeY / simScale)); | |
gl.useProgram(prog_fluid_div); | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, texture_fluid_v); | |
gl.activeTexture(gl.TEXTURE1); | |
gl.bindTexture(gl.TEXTURE_2D, texture_fluid_p); | |
gl.uniform2f(gl.getUniformLocation(prog_fluid_div, "texSize"), (sizeX / simScale), (sizeY / simScale)); | |
gl.uniform2f(gl.getUniformLocation(prog_fluid_div, "pixelSize"), 1. / (sizeX / simScale), 1. / (sizeY / simScale)); | |
gl.uniform1i(gl.getUniformLocation(prog_fluid_div, "sampler_v"), 0); | |
gl.uniform1i(gl.getUniformLocation(prog_fluid_div, "sampler_p"), 1); | |
renderAsTriangleStrip(FBO_fluid_v); | |
} | |
// main texture feedback warp | |
function advance() { | |
gl.viewport(0, 0, sizeX, sizeY); | |
gl.useProgram(prog_advance); | |
setUniforms(prog_advance); | |
if (mainBufferToggle > 0) { | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, texture_main_l); // interpolated input | |
gl.activeTexture(gl.TEXTURE1); | |
gl.bindTexture(gl.TEXTURE_2D, texture_main_n); // "nearest" input | |
renderAsTriangleStrip(FBO_main2); | |
} else { | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, texture_main2_l); // interpolated | |
gl.activeTexture(gl.TEXTURE1); | |
gl.bindTexture(gl.TEXTURE_2D, texture_main2_n); // "nearest" | |
renderAsTriangleStrip(FBO_main); | |
} | |
mainBufferToggle = -mainBufferToggle; | |
} | |
function composite() { | |
gl.viewport(0, 0, viewX, viewY); | |
gl.useProgram(prog_composite); | |
setUniforms(prog_composite); | |
if (mainBufferToggle < 0) { | |
gl.activeTexture(gl.TEXTURE12); | |
gl.bindTexture(gl.TEXTURE_2D, texture_particles); | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, texture_main_l); | |
gl.activeTexture(gl.TEXTURE1); | |
gl.bindTexture(gl.TEXTURE_2D, texture_main_n); | |
} else { | |
gl.activeTexture(gl.TEXTURE12); | |
gl.bindTexture(gl.TEXTURE_2D, texture_particles2); | |
gl.activeTexture(gl.TEXTURE0); | |
gl.bindTexture(gl.TEXTURE_2D, texture_main2_l); | |
gl.activeTexture(gl.TEXTURE1); | |
gl.bindTexture(gl.TEXTURE_2D, texture_main2_n); | |
} | |
renderAsTriangleStrip(null); | |
} | |
var rainbowR, rainbowG, rainbowB, w = Math.PI * 2 / 3; | |
function anim() { | |
setTimeout("requestAnimationFrame(anim)", 1000 / desiredFramerate); | |
time = new Date().getTime() - starttime; | |
var t = time / 150; | |
rainbowR = 0.5 + 0.5 * Math.sin(t); | |
rainbowG = 0.5 + 0.5 * Math.sin(t + w); | |
rainbowB = 0.5 + 0.5 * Math.sin(t - w); | |
if (oldMouseX != 0 && oldMouseY != 0) { | |
mouseDx = (mouseX - oldMouseX) * viewX; | |
mouseDy = (mouseY - oldMouseY) * viewY; | |
} | |
if (!halted) { | |
if (useProjectionFeedback) | |
renderParticles(FBO_particle_projection); | |
if (useFluidSimulation) | |
fluidSimulationStep(); | |
if (useParticles) | |
stepParticles(); | |
advance(); | |
var srcTex = (mainBufferToggle < 0) ? texture_main2_l : texture_main_l; | |
calculateBlurTextures(srcTex); | |
if(useSummedAreaTable) | |
calculateSummedAreaTable(srcTex); | |
frame++; | |
framecount++; | |
} | |
if (renderParticlesOnly) | |
renderParticles(null); | |
else | |
composite(); | |
frames++; | |
oldMouseX = mouseX; | |
oldMouseY = mouseY; | |
} | |
function fr() { // updates every second | |
document.getElementById("fps").textContent = frame; | |
frame = 0; // reset the frame counter | |
} | |
var hidden = false; | |
function hide() { | |
hidden = !hidden; | |
document.getElementById("desc").style.setProperty('visibility', hidden ? 'hidden' : 'visible'); | |
} | |
function goFull(cb) { | |
if (cb.checked) { | |
viewX = window.innerWidth; | |
viewY = window.innerHeight; | |
} else { | |
viewX = sizeX; | |
viewY = sizeY; | |
} | |
c.width = viewX; | |
c.height = viewY; | |
} | |
function setDesiredFps(tb) { | |
desiredFramerate = tb.value; | |
if (desiredFramerate < 1) | |
desiredFPS = 1; | |
} | |
function switchRenderer(particlesOnly) { | |
renderParticlesOnly = particlesOnly; | |
} | |
</script> | |
<script type="text/javascript"> | |
function spiralIndex(w,h) { | |
var a = []; | |
var dh = [0,h] | |
var dw = [0,w] | |
function ddw() { | |
return dw[1]-dw[0]; | |
} | |
function ddh() { | |
return dh[1]-dh[0]; | |
} | |
while(ddw() > 0 & ddh() > 0) { | |
if(ddw() > 0) { | |
for(var i=dw[0]; i<dw[1]; i++) { | |
a.push(i+dh[0]*w); | |
} | |
dh[0]+=1; | |
} | |
dw[1]-=1; | |
if(ddh() > 0) { | |
for(var i=dh[0]; i<dh[1]; i++) { | |
a.push(i*w+dw[1]); | |
} | |
} | |
dh[1]-=1; | |
if(ddw() > 0) { | |
for(var i=dw[1]; i>dw[0]; i--) { | |
a.push(i+(dh[1])*w-1); | |
} | |
} | |
if(ddh() > 0) { | |
for(var i=dh[1]; i>dh[0]; i--) { | |
a.push((i*w-dw[1]-1)); | |
} | |
dw[0]+=1; | |
} | |
} | |
return a; | |
} | |
var drawWebGLonAudioBufferFill = true; | |
var audioBufferSize = 2048; | |
var width = 32; | |
var height = audioBufferSize/width; | |
var bufferSpiralIndex = spiralIndex(width,height); | |
var pixels = new Uint8Array(4 * audioBufferSize); | |
var p = new Int32Array(pixels.buffer) | |
window.AudioContext = window.AudioContext || window.webkitAudioContext; | |
var context = new AudioContext(); | |
var filter = context.createBiquadFilter(); | |
filter.type = filter.HIGHSHELF; | |
filter.frequency.value = 5200; | |
filter.Q.value = 1; | |
filter.connect(context.destination); | |
var redGain = context.createGain(); | |
redGain.gain.value = 0.3; | |
var red = context.createOscillator(); | |
red.connect(redGain); | |
redGain.connect(filter); | |
red.frequency.value = 20; | |
red.type = 'sine'; | |
red.connect(redGain); | |
red.start(0); | |
var greenGain = context.createGain(); | |
greenGain.gain.value = 0.3; | |
var green = context.createOscillator(); | |
green.connect(greenGain); | |
greenGain.connect(filter); | |
green.frequency.value = 30; | |
green.type = 'sine'; | |
green.connect(greenGain); | |
green.start(0); | |
var blueGain = context.createGain(); | |
blueGain.gain.value = 0.3; | |
var blue = context.createOscillator(); | |
blue.connect(blueGain); | |
blueGain.connect(filter); | |
blue.frequency.value = 50; | |
blue.type = 'sine'; | |
blue.start(0); | |
function startAudio(){ | |
// Create a ScriptProcessorNode with a bufferSize of audioBufferSize and a single output channel | |
var redModulator = context.createScriptProcessor(audioBufferSize, 0, 1); | |
// Give the node a function to process audio events | |
fillbuffer = function(audioProcessingEvent) { | |
if(drawWebGLonAudioBufferFill && false) anim(); | |
gl.readPixels(canvasMouseX,canvasMouseY,width,height,gl.RGBA, gl.UNSIGNED_BYTE,pixels); | |
// The output buffer contains the samples that will be modified and played | |
var outputBuffer = audioProcessingEvent.outputBuffer; | |
// Loop through the output channels (in this case there is only one) | |
for (var channel = 0; channel < outputBuffer.numberOfChannels; channel++) { | |
var outData = outputBuffer.getChannelData(channel); | |
// Loop through the audioBufferSize samples | |
for (var i = 0; i < outputBuffer.length; i++) { | |
sample = bufferSpiralIndex[i]; | |
var r = (p[sample] >> (0)) & 0xff; | |
outData[i] = Math.pow(r,1+0.05*audioBufferSize/4096+Math.random()*0.1); | |
} | |
} | |
bufferSpiralIndex = bufferSpiralIndex.reverse(); | |
} | |
redModulator.onaudioprocess = fillbuffer; | |
var greenModulator = context.createScriptProcessor(audioBufferSize, 0, 1); | |
updateGreen = function(evt) { | |
var outputBuffer = evt.outputBuffer; | |
for (var channel = 0; channel < outputBuffer.numberOfChannels; channel++) { | |
var outData = outputBuffer.getChannelData(channel); | |
// Loop through the audioBufferSize samples | |
for (var i = 0; i < outputBuffer.length; i++) { | |
sample = bufferSpiralIndex[i]; | |
p.buffer = pixels.buffer; | |
var g = (p[sample] >> (8)) & 0xff; | |
outData[i] = Math.pow(g,1+0.1*audioBufferSize/4096+Math.random()*0.1); | |
} | |
} | |
} | |
greenModulator.onaudioprocess = updateGreen; | |
var blueModulator = context.createScriptProcessor(audioBufferSize, 0, 1); | |
updateBlue = function(evt) { | |
var outputBuffer = evt.outputBuffer; | |
for (var channel = 0; channel < outputBuffer.numberOfChannels; channel++) { | |
var outData = outputBuffer.getChannelData(channel); | |
// Loop through the audioBufferSize samples | |
for (var i = 0; i < outputBuffer.length; i++) { | |
sample = bufferSpiralIndex[i]; | |
p.buffer = pixels.buffer; | |
var b = (p[sample] >> (16)) & 0xff; | |
outData[i] = Math.pow(b,1+0.2*audioBufferSize/4096+Math.random()*0.1)+b; | |
} | |
} | |
} | |
blueModulator.onaudioprocess = updateBlue; | |
redModulator.connect(red.frequency); | |
greenModulator.connect(green.frequency); | |
blueModulator.connect(blue.frequency); | |
} | |
</script> | |
<style type="text/css"> | |
body { | |
background-color: #000000; | |
color: #FFFFFF; | |
} | |
#c { | |
position: absolute; | |
top: 0; | |
left: 0; | |
z-index: -1; | |
} | |
a { | |
color: #FFFFFF; | |
font-weight: bold; | |
} | |
#desc { | |
background-color: rgba(0, 0, 0, 0.2); | |
width: 1024; | |
} | |
</style> | |
</head> | |
<body onload="load()" ondblclick="hide()"> | |
<div id="desc"> | |
<!-- | |
WebGL implemention of summed area tables / integral images | |
<p> | |
<a href="http://www.aishack.in/2010/07/integral-images/" | |
target="aishack">http://www.aishack.in/2010/07/integral-images/</a><br> | |
<a | |
href="http://www.nvidia.com/docs/IO/8227/GDC2003_SummedAreaTables.pdf" | |
target="nvidia">http://www.nvidia.com/docs/IO/8227/GDC2003_SummedAreaTables.pdf</a> | |
<p> | |
<form> | |
desired fps <input type="text" name="desiredFPS" size="3" value="30" | |
onKeyUp="setDesiredFps(this)"></input> (actual: <span id="fps"></span>) | |
<br> <input type="radio" name="render" | |
onclick="switchRenderer(false)" checked="true">raw </input> <br> | |
<input type="radio" name="render" onclick="switchRenderer(true)">points | |
</input> <br> <input type="checkbox" onclick="goFull(this)">full | |
</input> | |
</form> | |
--> | |
3 self-inflating layers (by gradient flow) coupled over a rock paper scissor mechanism,<br> | |
afterward routed into three WebAudio script processing nodes that each modulate a sine wave.<br> | |
(modified from shader demo found at <a href="http://www.cake23.de/fmx/rock-paper-scissor-smearfinger.html">cake23.de</a>)<br> | |
fps: <span id="fps">11</span> | |
</div> | |
<canvas height="512" width="512" id="c"></canvas> | |
</body></html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment