Sketch with procedural texture generation and drawing a graphical primitive using shader
A Pen by Mika Chernov on CodePen.
| <script id="effectShader" type="x-shader/x-fragment"> | |
| #ifdef GL_ES | |
| precision highp float; | |
| #endif | |
| uniform vec2 resolution; | |
| uniform float time; | |
| uniform sampler2D tex0; | |
| uniform vec2 mouse; | |
| //uniform vec4 color; | |
| //Function to draw a line, taken from the watch shader | |
| float line(vec2 p, vec2 a, vec2 b, float thickness ) | |
| { | |
| vec2 pa = p - a; | |
| vec2 ba = b - a; | |
| float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); | |
| return 1.0 - smoothstep(thickness*0.2, thickness * 1.0, length(pa - ba * h)); | |
| } | |
| void main(void) | |
| { | |
| vec2 uv = (gl_FragCoord.xy / resolution.xy) * 2.0 - 1.0; | |
| float gt = floor(time * 60.0) * 0.05; | |
| //Calculate the next two points | |
| vec2 point1 = vec2(sin(gt * 0.19), cos(gt * 0.29)); | |
| vec2 point2 = vec2(cos(gt * 0.39), sin(gt * 0.45)); | |
| vec2 delta = vec2(1.5,1.5); | |
| vec4 col = texture2D(tex0,uv); | |
| float aspect = resolution.x/resolution.y; | |
| float dx = 0.008; | |
| float dy = dx * aspect; | |
| vec4 v0 = texture2D( tex0, uv ); | |
| vec4 v1 = texture2D( tex0, uv + vec2( 0.0, dy ) ); | |
| vec4 v2 = texture2D( tex0, uv + vec2( dx, 0.0 ) ); | |
| vec4 v3 = texture2D( tex0, uv + vec2( 0.0, -dy ) ); | |
| vec4 v4 = texture2D( tex0, uv + vec2( -dx, 0.0 ) ); | |
| vec4 v5 = texture2D( tex0, uv + vec2( dx, dy ) ); | |
| vec4 v6 = texture2D( tex0, uv + vec2( -dx, -dy ) ); | |
| vec4 v7 = texture2D( tex0, uv + vec2( dx, -dy ) ); | |
| vec4 v8 = texture2D( tex0, uv + vec2( -dx, dy ) ); | |
| vec4 s = v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8; | |
| if ( v0.x >= 0.0 ) { | |
| //if ( s.x < 1.0 || s.x > 2.0 ) | |
| //col = vec4( 1.0, 0.0, 0.0, 1 ); | |
| //else | |
| //col = vec4( 0.0, 1.0, 1.0, 1 ); | |
| } | |
| //else { | |
| // if ( s.x == 3.0 ) | |
| // col = vec4( 0.5, 0.5, 1.0, 1.0 ); } | |
| col.x *= 0.0+0.5*sin(1.9*time); | |
| col.y *= 0.0+0.5*cos(1.7*time); | |
| col.z *= 0.0+0.5*sin(1.5*time); | |
| col.w *= 0.0+0.5*sin(1.3*time); | |
| col.xyz += line(uv, vec2(-0.0,-0.0) + col.xy * 4.0 , vec2(-0.0,-0.0) + col.zw * 4.0, 0.2); | |
| //col.rgb += line(uv, point1, point2, 0.3); | |
| //col.rgb -= line(uv, point2, point1, 0.05); | |
| //gl_FragColor = clamp(color, 0.0, 1.0); | |
| gl_FragColor = vec4(col.xyz, 1.0); | |
| } | |
| </script> |
Sketch with procedural texture generation and drawing a graphical primitive using shader
A Pen by Mika Chernov on CodePen.
| "use strict"; | |
| var mouse = { | |
| down: false, | |
| button: 1, | |
| x: 0, y: 0, px: 0, py: 0 | |
| }; | |
| // Define an Effect class for handling interaction with OpenGL and drawing to a quad. | |
| function Effect(gl,xres,yres) | |
| { | |
| this.mGLContext = gl; | |
| this.mQuadVBO = null; | |
| this.mProgram = null; | |
| this.mTexture = null; | |
| var vertices = new Float32Array([ -1., -1., 1., -1., -1., 1., 1., -1., 1., 1., -1., 1.]); | |
| this.mQuadVBO = gl.createBuffer(); | |
| gl.bindBuffer(gl.ARRAY_BUFFER, this.mQuadVBO); | |
| gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); | |
| } | |
| // Compiles GLSL code and sends it to the GPU. | |
| Effect.prototype.NewShader = function(shaderCode) | |
| { | |
| var gl = this.mGLContext; | |
| var tmpProgram = gl.createProgram(); | |
| var vs = gl.createShader(gl.VERTEX_SHADER); | |
| var fs = gl.createShader(gl.FRAGMENT_SHADER); | |
| gl.shaderSource(vs, "attribute vec2 pos;void main(){gl_Position=vec4(pos.x,pos.y,0.0,1.0);}"); | |
| gl.shaderSource(fs, shaderCode); | |
| gl.compileShader(vs); | |
| gl.compileShader(fs); | |
| if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) | |
| { var infoLog = gl.getShaderInfoLog(vs); | |
| gl.deleteProgram( tmpProgram ); | |
| return "VS ERROR: " + infoLog;; | |
| } | |
| if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) | |
| { var infoLog = gl.getShaderInfoLog(fs); | |
| gl.deleteProgram( tmpProgram ); | |
| return "FS ERROR: " + infoLog; | |
| } | |
| gl.attachShader(tmpProgram, vs); | |
| gl.attachShader(tmpProgram, fs); | |
| gl.deleteShader(vs); | |
| gl.deleteShader(fs); | |
| gl.linkProgram(tmpProgram); | |
| if( this.mProgram != null ) | |
| gl.deleteProgram( this.mProgram ); | |
| this.mProgram = tmpProgram; | |
| } | |
| // Resize the effect window. | |
| Effect.prototype.SetSize = function(xres,yres) | |
| { | |
| this.mXres = xres; | |
| this.mYres = yres; | |
| } | |
| // This method creates the XOR texture and binds it to the GL context associated with the Effect. | |
| Effect.prototype.BindTexture = function() | |
| { | |
| var textureCanvas = document.createElement('canvas'); | |
| textureCanvas.width = 256; | |
| textureCanvas.height = 256; | |
| var ctx = textureCanvas.getContext("2d"); | |
| var idata = ctx.getImageData(0, 0, 256, 256); | |
| var i = 0; | |
| for( var x = 0 ; x < 256 ; ++x ) { | |
| for( var y = 0 ; y < 256 ; ++y ) { | |
| idata.data[i++]=( Math.sin(x|y)*300 ); | |
| idata.data[i++]=( Math.cos(x|y)*300 ); | |
| idata.data[i++]=( Math.sin(x|y)*255 ); | |
| idata.data[i++]=( Math.sin(x|y)*255 ); | |
| } | |
| } | |
| //var myimage = new Image(); | |
| //myimage.onload = function() { | |
| // ctx.drawImage(myimage, 0, 0); | |
| // } | |
| //myimage.src = 'http://i647.photobucket.com/albums/uu200/dasheekyplus/texture.png'; | |
| ctx.putImageData( idata, 0, 0 ); | |
| var gl = this.mGLContext; | |
| var texture = gl.createTexture(); | |
| gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); | |
| gl.bindTexture(gl.TEXTURE_2D, texture); | |
| gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureCanvas); // This is the important line! | |
| gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); | |
| gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); | |
| gl.generateMipmap(gl.TEXTURE_2D); | |
| gl.bindTexture(gl.TEXTURE_2D, null); | |
| this.mTexture = texture; | |
| } | |
| // Draws a single quad using the fragment shader we specified by calling NewShader. | |
| Effect.prototype.Paint = function(time) | |
| { | |
| var gl = this.mGLContext; | |
| gl.viewport( 0, 0, this.mXres, this.mYres ); | |
| gl.useProgram(this.mProgram); | |
| var l1 = gl.getAttribLocation(this.mProgram, "pos"); | |
| var l2 = gl.getUniformLocation(this.mProgram, "time"); | |
| var l3 = gl.getUniformLocation(this.mProgram, "resolution"); | |
| var l4 = gl.getUniformLocation(this.mProgram, "color"); | |
| var l5 = gl.getUniformLocation(this.mProgram, "mouse"); | |
| var t0 = gl.getUniformLocation(this.mProgram, "tex0"); | |
| gl.bindBuffer(gl.ARRAY_BUFFER, this.mQuadVBO); | |
| if( l2!=null ) gl.uniform1f(l2, time); | |
| if( l3!=null ) gl.uniform2f(l3, this.mXres, this.mYres); | |
| //var color = new Float32Array([0, 1, 0, 1]); | |
| gl.uniform4f(l4, mouse.x/1000.0, mouse.y/1000, 1 - mouse.x/1000.0 , 1.0); | |
| gl.uniform2f(l5, -1+mouse.x/100.0, 1-mouse.y/100.0); | |
| gl.vertexAttribPointer(l1, 2, gl.FLOAT, false, 0, 0); | |
| gl.enableVertexAttribArray(l1); | |
| if( t0!=null ) { gl.uniform1i(t0, 0 ); | |
| gl.activeTexture(gl.TEXTURE0); | |
| gl.bindTexture(gl.TEXTURE_2D, this.mTexture); } | |
| gl.drawArrays(gl.TRIANGLES, 0, 6); | |
| gl.disableVertexAttribArray(l1); | |
| } | |
| // Request animation frame browser-specific bindings and fallback. | |
| if ( !window.requestAnimationFrame ) { | |
| window.requestAnimationFrame = ( function() { | |
| return window.webkitRequestAnimationFrame || | |
| window.mozRequestAnimationFrame || | |
| window.oRequestAnimationFrame || | |
| window.msRequestAnimationFrame || | |
| function ( callback, element ) { | |
| window.setTimeout( callback, 1000 / 100 ); | |
| }; | |
| } )(); | |
| } | |
| // Create the canvas element, compile the shader code, and bind the texture to the context. | |
| document.write("<canvas id='x'></canvas>"); | |
| var glCanvas = document.getElementById("x"); | |
| var glContext = glCanvas.getContext("experimental-webgl"); | |
| var effect = new Effect( glContext, glCanvas.width, glCanvas.height ); | |
| effect.NewShader( document.getElementById( 'effectShader' ).textContent ); | |
| effect.BindTexture(); | |
| // Specify a canvas scale and handle window resize. | |
| var scaleDown = 1; | |
| function onWindowResize() { | |
| //glCanvas.width = window.innerWidth / scaleDown; | |
| //glCanvas.height = window.innerHeight / scaleDown; | |
| //glCanvas.style.width = window.innerWidth + 'px'; | |
| //glCanvas.style.height = window.innerHeight + 'px'; | |
| var width = 800, height = 800; | |
| glCanvas.width = width / scaleDown; | |
| glCanvas.height = height / scaleDown; | |
| glCanvas.style.width = width + 'px'; | |
| glCanvas.style.height = height + 'px'; | |
| glContext.viewport( 0, 0, glCanvas.width, glCanvas.height ); | |
| effect.SetSize( glCanvas.width, glCanvas.height ); | |
| } | |
| onWindowResize(); | |
| window.addEventListener( 'resize', onWindowResize, false ); | |
| // Main loop. | |
| var t0 = (new Date()).getTime(); | |
| function animate() { | |
| effect.Paint( ((new Date()).getTime()-t0)/1000 ); | |
| requestAnimationFrame( animate ); | |
| } | |
| animate(); | |
| window.onload = function() { | |
| x.onmousedown = function(e) { | |
| if (e.which == 1) { | |
| } else if (e.which == 3) { | |
| mouse.down = true; } | |
| e.preventDefault(); }; | |
| x.onmouseup = function(e) { | |
| if (e.which == 3) { mouse.down = false; } | |
| e.preventDefault(); }; | |
| x.onmousemove = function(e) { | |
| var rect = this.getBoundingClientRect(); | |
| mouse.x = e.clientX - rect.left; | |
| mouse.y = e.clientY - rect.top; | |
| }; | |
| }; |
| html,body{ margin:0px; overflow:hidden; } |