var vsSource = [
"attribute vec2 pos;",
"void main()",
"{",
"gl_Position = vec4(pos.x,pos.y,0.0,1.0);",
"}"
].join("\n");
var fsSource = [
"void main()",
"{",
"gl_FragColor = vec4(0.0,0.0,0.0,1.0);",
"}"
].join("\n");
//--------------------------------------
function createGLTexture( ctx, image, format, texture )
{
if( ctx==null ) return;
ctx.bindTexture( ctx.TEXTURE_2D, texture);
ctx.pixelStorei( ctx.UNPACK_FLIP_Y_WEBGL, false );
ctx.texImage2D( ctx.TEXTURE_2D, 0, format, ctx.RGBA, ctx.UNSIGNED_BYTE, image);
ctx.texParameteri( ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR);
ctx.texParameteri( ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR_MIPMAP_LINEAR);
ctx.texParameteri( ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.REPEAT);
ctx.texParameteri( ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.REPEAT);
ctx.generateMipmap(ctx.TEXTURE_2D);
ctx.bindTexture(ctx.TEXTURE_2D, null);
}
function createGLTextureLinear( ctx, image, texture )
{
if( ctx==null ) return;
ctx.bindTexture( ctx.TEXTURE_2D, texture);
ctx.pixelStorei( ctx.UNPACK_FLIP_Y_WEBGL, false );
ctx.texImage2D( ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, image);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE);
ctx.bindTexture(ctx.TEXTURE_2D, null);
}
function createGLTextureNearestRepeat( ctx, image, texture )
{
if( ctx==null ) return;
ctx.bindTexture(ctx.TEXTURE_2D, texture);
ctx.pixelStorei( ctx.UNPACK_FLIP_Y_WEBGL, false );
ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, image);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.NEAREST);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.NEAREST);
ctx.bindTexture(ctx.TEXTURE_2D, null);
}
function createGLTextureNearest( ctx, image, texture )
{
if( ctx==null ) return;
ctx.bindTexture(ctx.TEXTURE_2D, texture);
ctx.pixelStorei( ctx.UNPACK_FLIP_Y_WEBGL, false );
ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, image);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.NEAREST);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.NEAREST);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE);
ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE);
ctx.bindTexture(ctx.TEXTURE_2D, null);
}
function createAudioTexture( ctx, texture )
{
if( ctx==null ) return;
ctx.bindTexture( ctx.TEXTURE_2D, texture );
ctx.texParameteri( ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR );
ctx.texParameteri( ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR );
ctx.texParameteri( ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE );
ctx.texParameteri( ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE) ;
ctx.texImage2D( ctx.TEXTURE_2D, 0, ctx.LUMINANCE, 512, 2, 0, ctx.LUMINANCE, ctx.UNSIGNED_BYTE, null);
ctx.bindTexture( ctx.TEXTURE_2D, null);
}
function createKeyboardTexture( ctx, texture )
{
if( ctx==null ) return;
ctx.bindTexture( ctx.TEXTURE_2D, texture );
ctx.texParameteri( ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.NEAREST );
ctx.texParameteri( ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.NEAREST );
ctx.texParameteri( ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE );
ctx.texParameteri( ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE) ;
ctx.texImage2D( ctx.TEXTURE_2D, 0, ctx.LUMINANCE, 256, 2, 0, ctx.LUMINANCE, ctx.UNSIGNED_BYTE, null);
ctx.bindTexture( ctx.TEXTURE_2D, null);
}
function Effect( ac, gl, xres, yres, callback, obj, forceMuted, forcePaused)
{
this.mAudioContext = ac;
this.mNoAudioMessageShowed = false;
this.mGLContext = gl;
this.mQuadVBO = null;
this.mProgram = null;
this.mXres = xres;
this.mYres = yres;
this.mInputs = new Array(4);
this.mInputs[0] = null;
this.mInputs[1] = null;
this.mInputs[2] = null;
this.mInputs[3] = null;
this.mTextureCallbackFun = callback;
this.mTextureCallbackObj = obj;
this.mSource = null;
this.mForceMuted = forceMuted;
this.mForcePaused = forcePaused;
this.mSupportsDerivatives = false;
//-------------
if( gl==null ) return;
var ext = gl.getExtension('OES_standard_derivatives');
this.mSupportsDerivatives = (ext != null);
if( this.mSupportsDerivatives )
{
gl.hint( ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES, gl.NICEST );
}
var ext2 = gl.getExtension('OES_texture_float');
this.mSupportTextureFloat = (ext2 != null );
var vertices = new Float32Array( [ -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0] );
this.mQuadVBO = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.mQuadVBO);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
var res = this.NewShader(fsSource);
this.DetermineShaderPrecission();
this.MakeHeader();
}
Effect.prototype.DestroyInput = function( id )
{
if( this.mInputs[id]==null ) return;
if( this.mGLContext== null ) return;
var gl = this.mGLContext;
if( this.mInputs[id].mInfo.mType=="texture" )
{
gl.deleteTexture( this.mInputs[id].globject );
}
else if( this.mInputs[id].mInfo.mType=="slideshow" )
{
gl.deleteTexture( this.mInputs[id].globject );
}
else if( this.mInputs[id].mInfo.mType=="webcam" )
{
gl.deleteTexture( this.mInputs[id].globject );
}
else if( this.mInputs[id].mInfo.mType=="video" )
{
this.mInputs[id].video.pause();
this.mInputs[id].video = null;
gl.deleteTexture( this.mInputs[id].globject );
}
else if( this.mInputs[id].mInfo.mType=="music" )
{
this.mInputs[id].audio.pause();
this.mInputs[id].audio = null;
gl.deleteTexture( this.mInputs[id].globject );
}
else if( this.mInputs[id].mInfo.mType=="cubemap" )
{
gl.deleteTexture( this.mInputs[id].globject );
}
else if( this.mInputs[id].mInfo.mType=="keyboard" )
{
gl.deleteTexture( this.mInputs[id].globject );
}
this.mInputs[id] = null;
}
Effect.prototype.NewTexture = function( slot, url )
{
var me = this;
var gl = this.mGLContext;
var texture = null;
if (url!=null && url.mType=="webcam" && this.mForceMuted)
{
url.mType = "texture";
}
if( url==null )
{
if( me.mTextureCallbackFun!=null )
me.mTextureCallbackFun( this.mTextureCallbackObj, slot, null, false, true, 0, -1.0 );
return false;
}
else if( url.mType=="texture" )
{
texture = {};
texture.mInfo = url;
texture.globject = (gl!=null) ? gl.createTexture() : null;
texture.loaded = false;
texture.image = new Image();
texture.image.crossOrigin = '';
texture.image.onload = function()
{
var format = gl.RGBA;
if( url.mSrc=="/presets/tex15.png" || url.mSrc=="/presets/tex17.png" )
format = gl.LUMINANCE;
if( url.mSrc=="/presets/tex14.png" )
createGLTextureNearest( gl, texture.image, texture.globject );
else if( url.mSrc=="/presets/tex15.png" )
createGLTextureNearestRepeat( gl, texture.image, texture.globject );
else
createGLTexture( gl, texture.image, format, texture.globject );
texture.loaded = true;
if( me.mTextureCallbackFun!=null )
me.mTextureCallbackFun( me.mTextureCallbackObj, slot, texture.image, true, true, 0, -1.0 );
}
texture.image.src = url.mSrc;
}
else if( url.mType=="slideshow" )
{
texture = {};
texture.mInfo = url;
texture.globject = (gl!=null) ? gl.createTexture() : null;
texture.loaded = false;
texture.image = new Image();
texture.image.crossOrigin = '';
texture.image.onload = function()
{
createGLTexture( gl, texture.image, gl.RGBA, texture.globject );
texture.loaded = true;
if( me.mTextureCallbackFun!=null )
me.mTextureCallbackFun( me.mTextureCallbackObj, slot, texture.image, true, true, 3, -1.0 );
}
texture.slideshow = {};
texture.slideshow.mCurrentSlide = 0;
texture.slideshow.mNewTextureReady = false;
var urlSlide = url.mSrc.replace("??","00");
texture.image.src = urlSlide;
}
else if( url.mType=="cubemap" )
{
texture = {};
texture.mInfo = url;
texture.globject = (gl!=null) ? gl.createTexture() : null;
texture.loaded = false;
texture.image = [ new Image(), new Image(), new Image(), new Image(), new Image(), new Image() ];
gl.bindTexture( gl.TEXTURE_CUBE_MAP, texture.globject );
gl.texParameteri( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
gl.texParameteri( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR );
gl.bindTexture( gl.TEXTURE_CUBE_MAP, null );
texture.loaded = true;
for( var i=0; i<6; i++ )
{
texture.image[i].mId = i;
texture.image[i].crossOrigin = '';
texture.image[i].onload = function()
{
var id = this.mId;
gl.bindTexture( gl.TEXTURE_CUBE_MAP, texture.globject );
gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, false );
gl.texImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + id, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image[id] );
gl.bindTexture( gl.TEXTURE_CUBE_MAP, null );
if( me.mTextureCallbackFun!=null )
me.mTextureCallbackFun( me.mTextureCallbackObj, slot, texture.image[0], true, true, 0, -1.0 );
}
texture.image[i].src = url.mSrc.replace( "_0.", "_" + i + "." );
}
}
else if( url.mType=="webcam" )
{
texture = {};
texture.mInfo = url;
texture.globject = null;
texture.loaded = false;
texture.video = document.createElement('video');
texture.video.width = 320;
texture.video.height = 240;
texture.video.autoplay = true;
texture.video.loop = true;
texture.video.paused = true;
navigator.getUserMedia( { "video": true, "audio": false },
function(stream)
{
texture.video.src = window.URL.createObjectURL(stream);
texture.globject = gl.createTexture();
try
{
createGLTextureLinear( gl, texture.video, texture.globject );
texture.loaded = true;
}
catch(e)
{
alert( 'Your browser can not transfer webcam data to the GPU.');
}
},
function(error)
{
alert( 'Unable to capture WebCam. Please reload the page.' );
} );
}
else if( url.mType=="video" )
{
texture = {};
texture.mInfo = url;
texture.globject = null;
texture.loaded = false;
texture.video = document.createElement('video');
texture.video.width = 256;
texture.video.height = 256;
texture.video.loop = true;
texture.video.paused = true;//this.mForcePaused;
texture.video.mPaused = true;//this.mForcePaused;
texture.video.mMuted = this.mForceMuted;
texture.video.muted = this.mForceMuted;
if( this.mForceMuted==true )
texture.video.volume = 0;
texture.video.autoplay = false;
texture.video.hasFalled = false;
texture.video.addEventListener( "canplay", function(e)
{
texture.video.play();
texture.video.paused = false;
texture.video.mPaused = false;
texture.globject = gl.createTexture();
createGLTextureLinear( gl, texture.video, texture.globject );
texture.loaded = true;
if( me.mTextureCallbackFun!=null )
me.mTextureCallbackFun( me.mTextureCallbackObj, slot, texture.video, true, true, 1, -1.0 );
} );
texture.video.addEventListener( "error", function(e)
{
if( texture.video.hasFalled==true ) { alert("Error: cannot load video" ); return; }
var str = texture.video.src;
str = str.substr(0,str.lastIndexOf('.') ) + ".mp4";
texture.video.src = str;
texture.video.hasFalled = true;
} );
texture.video.src = url.mSrc;
}
else if( url.mType=="music" )
{
texture = {};
texture.mInfo = url;
texture.globject = null;
texture.loaded = false;
texture.audio = document.createElement('audio');
texture.audio.loop = true;
texture.audio.mMuted = this.mForceMuted;
texture.audio.mForceMuted = this.mForceMuted;
texture.audio.muted = this.mForceMuted;
if( this.mForceMuted==true )
texture.audio.volume = 0;
texture.audio.autoplay = true;
texture.audio.hasFalled = false;
texture.audio.paused = true;
texture.audio.mPaused = true;
texture.audio.mSound = {};
if( this.mForceMuted )
{
texture.globject = gl.createTexture();
createAudioTexture( gl, texture.globject );
var num = 512;
texture.audio.mSound.mFreqData = new Uint8Array( num );
texture.audio.mSound.mWaveData = new Uint8Array( num );
texture.loaded = true;
texture.audio.paused = false;
texture.audio.mPaused = false;
}
texture.audio.addEventListener( "canplay", function()
{
if( this.mForceMuted ) return;
texture.globject = gl.createTexture();
createAudioTexture( gl, texture.globject );
if( me.mAudioContext != null )
{
var ctx = me.mAudioContext;
texture.audio.mSound.mSource = ctx.createMediaElementSource( texture.audio );
texture.audio.mSound.mAnalyser = ctx.createAnalyser();
texture.audio.mSound.mGain = ctx.createGain();
texture.audio.mSound.mSource.connect( texture.audio.mSound.mAnalyser );
texture.audio.mSound.mAnalyser.connect( texture.audio.mSound.mGain );
texture.audio.mSound.mGain.connect( ctx.destination );
texture.audio.mSound.mFreqData = new Uint8Array( texture.audio.mSound.mAnalyser.frequencyBinCount );
texture.audio.mSound.mWaveData = new Uint8Array( texture.audio.mSound.mAnalyser.frequencyBinCount );
texture.loaded = true;
texture.audio.paused = false;
texture.audio.mPaused = false;
}
else
{
if( me.mNoAudioMessageShowed==false )
{
var ve = document.getElementById( "centerScreen" );
doAlert( getCoords(ve), {mX:420,mY:160}, "Error", "Your browser does not support WebAudio.
This shader will not work as the author intended. Please consider using a WebAudio-friendly browser (Chrome).", false, null );
me.mNoAudioMessageShowed = true;
}
}
} );
texture.audio.addEventListener( "error", function(e)
{
if( this.mForceMuted ) return;
if( texture.audio.hasFalled==true ) { /*alert("Error: cannot load music" ); */return; }
var str = texture.audio.src;
str = str.substr(0,str.lastIndexOf('.') ) + ".ogg";
texture.audio.src = str;
texture.audio.hasFalled = true;
} );
if( !this.mForceMuted )
{
texture.audio.src = url.mSrc;
}
if( me.mTextureCallbackFun!=null )
me.mTextureCallbackFun( me.mTextureCallbackObj, slot, null, false, true, 2, -1.0 );
}
else if( url.mType=="keyboard" )
{
texture = {};
texture.mInfo = url;
texture.globject = gl.createTexture();
texture.loaded = true;
texture.keyboard = {};
texture.keyboard.mImage = new Image();
texture.keyboard.mImage.onload = function()
{
texture.loaded = true;
if( me.mTextureCallbackFun!=null )
me.mTextureCallbackFun( me.mTextureCallbackObj, slot, {mImage:texture.keyboard.mImage,mData:texture.keyboard.mData}, false, false, 4, -1.0 );
}
texture.keyboard.mImage.src = "/img/keyboard.png";
texture.keyboard.mNewTextureReady = true;
texture.keyboard.mData = new Uint8Array( 256*2 );
createKeyboardTexture( gl, texture.globject );
for( var j=0; j<(256*2); j++ )
{
texture.keyboard.mData[j] = 0;
}
if( me.mTextureCallbackFun!=null )
me.mTextureCallbackFun( me.mTextureCallbackObj, slot, {mImage:texture.keyboard.mImage,mData:texture.keyboard.mData}, false, false, 4, -1.0 );
}
else if( url.mType==null )
{
if( me.mTextureCallbackFun!=null )
me.mTextureCallbackFun( this.mTextureCallbackObj, slot, null, false, true, 0, -1.0 );
}
else
{
alert( "texture type error" );
return;
}
this.DestroyInput( slot );
this.mInputs[slot] = texture;
this.MakeHeader();
}
Effect.prototype.SetKeyDown = function( k )
{
for( var i=0; i1 )
{
return { mFailed : true, mError : "ShaderToy only supports one-pass shaders at this momment", mShader:null };
}
for( var j=0; j