├── README.md ├── demo_webgl.js ├── index.html ├── pxshaders.js └── pxslabs.js /README.md: -------------------------------------------------------------------------------- 1 | # WebGL Feedback Demo 2 | WebGL setup for rendering multi-pass shader networks for image processes. 3 | 4 | This demo shows my basic setup for developing WebGL pages with image processing effects. It doesn't require any WebGL frameworks, as my intention was to create a minimal setup that is simple for me to develop for. This style of working with textures, shaders, and FBOs was inspired by my work with Jitter (jit.gl.slab) and is informed by the way ofShader is implemented in OpenFrameworks. I haven't come across any other WebGL libraries that make it this straightforward to experiment and develop texture processing effects. It was also developed with an eye toward making it easy for me to port Jitter work over to webpages, since I prefer the nonlinear workflow there. 5 | 6 | ## pxslabs.js 7 | My small library for setting up framebuffers and fragment shaders in a way that lends itself to image processing chains. This is super useful for any kind of full-frame FX or if you want to play with video feedback style experiments. 8 | 9 | ## shaders.js 10 | I prefer to load my shaders as a JS Object full of strings that I can load directly into WebGL. This eliminates the need for special text parsing routines, but introduces a little overhead making sure line endings in the GLSL are marked properly. A typical project for me, like melter.club might have as many as 15 different shaders in this file. 11 | 12 | ## demo_webgl.js 13 | This file shows a typical setup and usage of the pxslabs library. I do everything with raw library calls to WebGL, so it's not as tidy as running THREE.js, but it's about as optimal as you can get if you just want to do texture processing. I also like to do all the WebGL initialization stuff directly because I find it simpler than hunting down parameters inside of a library. Using raw WebGL means it's also very simple to build graphics drawing routines on top of the texture processing. I may add in one of my simple polygon animation things if I can clean it up. 14 | 15 | Simplicity means different things to different people. 16 | 17 | This works for me, but it might be too bare-metal for most usages. It's a good starting point though if you want to understand framebuffers and fragment shaders in WebGL, so there's probably some educational value here. 18 | -------------------------------------------------------------------------------- /demo_webgl.js: -------------------------------------------------------------------------------- 1 | var c = document.getElementById('c'); 2 | c.width = $(document).width(); 3 | c.height = $(document).height(); 4 | c.aspect = $(document).width()/$(document).height(); 5 | 6 | var gl; 7 | 8 | //set up globals 9 | var basevs,basefs,feedback,heightfs,heightflow,testfs,tester; 10 | var camtex,ppgm,noisetex; 11 | var webcam=document.createElement('video'); 12 | 13 | //set the initial state of the effect 14 | var FLOWSTATE = { 15 | warp: 0.05, 16 | mixin: 0.1, 17 | aspect: c.width/c.height 18 | } 19 | //method to translate stored settings into shader uniforms 20 | FLOWSTATE.calc = function(){ 21 | gl.useProgram(heightflow.pgm); 22 | gl.uniform1f(gl.getUniformLocation(heightflow.pgm,"warp"),this.warp); 23 | gl.uniform1f(gl.getUniformLocation(heightflow.pgm,"mixin"),this.mixin); 24 | gl.uniform1f(gl.getUniformLocation(heightflow.pgm,"aspect"),this.aspect); 25 | } 26 | 27 | //initialize the important stuff 28 | initGL(); 29 | initSlabs(); 30 | resizeCanvas(); 31 | 32 | function initGL(){ 33 | gl = c.getContext('webgl'); 34 | gl.blendFunc(gl.ONE,gl.ONE_MINUS_SRC_ALPHA); 35 | gl.disable(gl.BLEND); 36 | gl.disable(gl.DEPTH_TEST); 37 | gl.clearColor(0.,0.,0.,1.); 38 | gl.viewport(0,0,c.width,c.height); 39 | } 40 | 41 | function initSlabs(){ 42 | // generate the shaders 43 | basevs = pxShader(shades.basevs,gl.VERTEX_SHADER); 44 | basefs = pxShader(shades.basefs,gl.FRAGMENT_SHADER); 45 | heightfs = pxShader(shades.basic,gl.FRAGMENT_SHADER); 46 | ppgm = pxProgram(basevs,basefs); 47 | 48 | // create Slab 49 | heightflow = new pxSlab(basevs,heightfs); 50 | heightflow.allocate(c.width,c.height); 51 | 52 | // create the FBO 53 | feedback = new pxFBO(); 54 | feedback.allocate2(c.width,c.height); 55 | 56 | FLOWSTATE.calc(); 57 | } 58 | 59 | function initImages(){ 60 | camtex = gl.createTexture(); 61 | camtex.image = webcam; 62 | gl.bindTexture(gl.TEXTURE_2D, camtex); 63 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 64 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 65 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 66 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 67 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 68 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, webcam); 69 | } 70 | 71 | //the "animate" function is where the draw loop happens 72 | function animate() { 73 | //set up the next frame 74 | window.requestAnimFrame( animate ); 75 | //recalculate the settings 76 | FLOWSTATE.aspect = c.height/c.width; 77 | FLOWSTATE.calc(); 78 | 79 | //clear the frame 80 | gl.clear(gl.COLOR_BUFFER_BIT); 81 | 82 | //bind the "heightflow" slab 83 | heightflow.start(); 84 | //clear the color buffer 85 | gl.clear(gl.COLOR_BUFFER_BIT); 86 | //draw the "feedback" image (along with camera) into the heightflow buffer, using the heightflow shader 87 | feedback.draw2(heightflow.pgm,camtex); 88 | 89 | //bind the feedback slab 90 | feedback.start(); 91 | //clear the color buffer 92 | gl.clear(gl.COLOR_BUFFER_BIT); 93 | //draw the heightflow buffer image into the feedback buffer, using a very simple, passthru shader 94 | heightflow.draw(ppgm); 95 | 96 | //unbind the framebuffer so that we draw to screen 97 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 98 | //draw the heightflow buffer to screen 99 | heightflow.draw(ppgm); 100 | 101 | //important note about feedback with framebuffers: 102 | //You can't draw a framebuffer texture into itself (unlike an HTML5 canvas), so it's always going to be 103 | //necessary to have at least one otherframebuffer in the drawing chain. 104 | 105 | //update camera texture 106 | gl.bindTexture(gl.TEXTURE_2D, camtex); 107 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, webcam); 108 | } 109 | 110 | //The current best practice for camera input. This seems to change regularly so could break. 111 | function startvideo() { 112 | webcam.style.width = document.width + 'px'; 113 | webcam.style.height = document.height + 'px'; 114 | webcam.setAttribute('autoplay', ''); 115 | webcam.setAttribute('muted', ''); 116 | webcam.setAttribute('playsinline', ''); 117 | 118 | var constraints = { 119 | audio: false, 120 | video: { 121 | facingMode: 'user' 122 | } 123 | } 124 | navigator.mediaDevices.getUserMedia(constraints).then(function success(stream) { 125 | webcam.srcObject = stream; 126 | initImages(); 127 | animate(); 128 | }); 129 | } 130 | 131 | //You need to bind a user interaction in order to start video input on Safari and iOS 132 | $(document).ready (function(){ 133 | $('body').bind("click touchstart",function(){ 134 | startvideo(); 135 | $('#startmessage').empty(); 136 | $('body').unbind("click touchstart"); 137 | }); 138 | }); 139 | 140 | //This makes sure everything is the right size 141 | function resizeCanvas() { 142 | c.width = c.clientWidth; 143 | c.height = c.clientHeight; 144 | c.aspect = c.width/c.height; 145 | gl.viewport(0,0,c.width,c.height); 146 | feedback.allocate2(c.width,c.height); 147 | heightflow.allocate(c.width,c.height); 148 | } 149 | 150 | //who knows if this polyfill is still necessary 151 | window.requestAnimFrame = (function() { 152 | return window.requestAnimationFrame || 153 | window.webkitRequestAnimationFrame || 154 | window.mozRequestAnimationFrame || 155 | window.oRequestAnimationFrame || 156 | window.msRequestAnimationFrame || 157 | function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) { 158 | return window.setTimeout(callback, 1000/60); 159 | }; 160 | })(); 161 | 162 | 163 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My Shader Demo 5 | 6 | 7 | 15 | 18 | 19 |
click anywhere to start
(and click "Allow" to enable camera)
20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /pxshaders.js: -------------------------------------------------------------------------------- 1 | var shades ={ 2 | simplefs: 3 | "precision mediump float;\n\ 4 | varying vec4 vColor;\n\ 5 | varying vec2 tc;\n\ 6 | uniform sampler2D tex0;\n\ 7 | uniform sampler2D tex1;\n\ 8 | uniform float usetex;\n\ 9 | \n\ 10 | void main(void) {\n\ 11 | vec4 tt = texture2D(tex0,tc);\n\ 12 | gl_FragColor = vec4(mix(vec3(1.),tt.rgb,usetex)*vColor.rgb*vec3(tt.a*vColor.a),vColor.a*tt.a);\n\ 13 | }", 14 | simplevs: 15 | "attribute vec3 pos;\n\ 16 | attribute vec4 color;\n\ 17 | attribute vec2 texcoord;\n\ 18 | \n\ 19 | uniform vec2 tscale;\n\ 20 | uniform vec2 toffset;\n\ 21 | uniform vec2 pscale;\n\ 22 | uniform vec2 ptranslate;\n\ 23 | uniform vec4 pcolor;\n\ 24 | \n\ 25 | varying vec4 vColor;\n\ 26 | varying vec2 tc;\n\ 27 | \n\ 28 | void main(void) {\n\ 29 | gl_Position = vec4(pos*vec3(pscale.xy,1.)+vec3(ptranslate.xy,0.),1.);\n\ 30 | vColor = color*pcolor;\n\ 31 | vec2 ttt = ((texcoord*2.-vec2(1.))*tscale+toffset)*0.5+vec2(0.5);\n\ 32 | tc= ttt;\n\ 33 | }", 34 | basevs: 35 | "attribute vec3 pos;\n\ 36 | attribute vec4 color;\n\ 37 | attribute vec2 texcoord;\n\ 38 | varying vec4 vColor;\n\ 39 | varying vec2 tc;\n\ 40 | \n\ 41 | void main(void){\n\ 42 | gl_Position = vec4(pos,1);\n\ 43 | tc = texcoord;\n\ 44 | vColor = color;\n\ 45 | }", 46 | basefs: 47 | "precision mediump float;\n\ 48 | varying vec4 vColor;\n\ 49 | varying vec2 tc;\n\ 50 | uniform sampler2D tex0;\n\ 51 | uniform sampler2D tex1;\n\ 52 | \n\ 53 | void main(void) {\n\ 54 | gl_FragColor = texture2D(tex0,tc);\n\ 55 | }", 56 | testfs: 57 | "precision mediump float;\n\ 58 | varying vec4 vColor;\n\ 59 | varying vec2 tc;\n\ 60 | //uniform sampler2D tex0;\n\ 61 | //uniform sampler2D tex1;\n\ 62 | \n\ 63 | void main(void) {\n\ 64 | gl_FragColor = vColor;\n\ 65 | }", 66 | basic: 67 | "precision highp float;\n\ 68 | varying vec2 tc;\n\ 69 | uniform sampler2D tex0;\n\ 70 | uniform sampler2D tex1;\n\ 71 | uniform float warp;\n\ 72 | uniform float mixin;\n\ 73 | uniform float aspect;\n\ 74 | \n\ 75 | void main()\n\ 76 | { \n\ 77 | vec2 asp = vec2(aspect,1.);\n\ 78 | vec2 wack = tc + (tc-vec2(0.5))*clamp(length((tc-vec2(0.5))*asp),0.,1.)*-warp;\n\ 79 | vec4 a = texture2D(tex0, wack)*1.06-vec4(0.025);\n\ 80 | vec4 b = texture2D(tex1, tc);\n\ 81 | gl_FragColor = mix(a,b,mixin);\n\ 82 | }" 83 | }; 84 | -------------------------------------------------------------------------------- /pxslabs.js: -------------------------------------------------------------------------------- 1 | /* 2 | This implementation was created to mimic the "jit.gl.slab" object in Jitter (Cycling '74). I've never 3 | looked at the code for that object, but the idea is similar - bundling an FBO with 4 | a fragment shader program and a billboard mesh. A similar technique is used in Open Frameworks. 5 | Since I do a lot of my shader prototyping in Max 6 | it made sense to me to have a way to migrate the complex shader chains into webpages. 7 | */ 8 | 9 | function pxSlab(vs,fs){ 10 | //the "start" method is borrowed from the OpenFrameworks implementation of texture processing 11 | this.start = function(){ 12 | gl.bindFramebuffer(gl.FRAMEBUFFER,this.fbo); 13 | gl.useProgram(this.pgm); 14 | }; 15 | this.allocate = function(w,h){ 16 | //make the framebuffer and texture output of the shader rendering 17 | this.fbo = gl.createFramebuffer(); 18 | this.texture = gl.createTexture(); 19 | //set properties for the texture 20 | gl.bindTexture(gl.TEXTURE_2D, this.texture); 21 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 22 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 23 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 24 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 25 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w,h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); 26 | //bind framebuffer to texture 27 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo); 28 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0); 29 | }; 30 | this.allocate2 = function(w,h){ 31 | //make the framebuffer and texture output 32 | this.fbo = gl.createFramebuffer(); 33 | this.texture = gl.createTexture(); 34 | //set properties for the texture 35 | gl.bindTexture(gl.TEXTURE_2D, this.texture); 36 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 37 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 38 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 39 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 40 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w,h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); 41 | //bind framebuffer to texture 42 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo); 43 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0); 44 | }; 45 | //the different draw functions are there to accomodate different numbers of textures 46 | this.draw = function(pgm){ 47 | this.bb.draw(pgm,this.texture); 48 | }; 49 | this.draw2 = function(pgm,texture2){ 50 | this.bb.draw2(pgm,this.texture,texture2); 51 | }; 52 | this.draw3 = function(pgm,texture2,texture3){ 53 | this.bb.draw3(pgm,this.texture,texture2,texture3); 54 | }; 55 | this.draw4 = function(pgm,texture2,texture3,texture4){ 56 | this.bb.draw2(pgm,this.texture,texture2,texture3,texture4); 57 | }; 58 | this.createPGM = function(v,f) { 59 | //assumes individual shaders are already compiled by the running code 60 | var program = gl.createProgram(); 61 | gl.attachShader(program, v); 62 | gl.attachShader(program, f); 63 | gl.linkProgram(program); 64 | return program; 65 | }; 66 | this.pgm = this.createPGM(vs,fs); 67 | this.fbo; 68 | this.texture; 69 | this.bb = new pxBB(gl); 70 | } 71 | 72 | //this is for when you need a framebuffer to draw into, but don't need another shader 73 | function pxFBO(){ 74 | this.start = function(pgm){ 75 | //get things ready for 76 | gl.bindFramebuffer(gl.FRAMEBUFFER,this.fbo); 77 | }; 78 | this.allocate = function(w,h){ 79 | //make the framebuffer and texture output 80 | this.fbo = gl.createFramebuffer(); 81 | this.texture = gl.createTexture(); 82 | //set properties for the texture 83 | gl.bindTexture(gl.TEXTURE_2D, this.texture); 84 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 85 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 86 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 87 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 88 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w,h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); 89 | //bind framebuffer to texture 90 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo); 91 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0); 92 | }; 93 | this.allocate2 = function(w,h){ 94 | //make the framebuffer and texture output 95 | this.fbo = gl.createFramebuffer(); 96 | this.texture = gl.createTexture(); 97 | //set properties for the texture 98 | gl.bindTexture(gl.TEXTURE_2D, this.texture); 99 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 100 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 101 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 102 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 103 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w,h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); 104 | //bind framebuffer to texture 105 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo); 106 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0); 107 | }; 108 | this.draw = function(pgm){ 109 | this.bb.draw(pgm,this.texture); 110 | }; 111 | this.draw2 = function(pgm,texture2){ 112 | this.bb.draw2(pgm,this.texture,texture2); 113 | }; 114 | this.draw2 = function(pgm,texture2){ 115 | this.bb.draw2(pgm,this.texture,texture2); 116 | }; 117 | this.draw3 = function(pgm,texture2,texture3){ 118 | this.bb.draw3(pgm,this.texture,texture2,texture3); 119 | }; 120 | this.draw4 = function(pgm,texture2,texture3,texture4){ 121 | this.bb.draw2(pgm,this.texture,texture2,texture3,texture4); 122 | }; 123 | this.fbo; 124 | this.texture; 125 | this.bb = new pxBB(gl); 126 | } 127 | 128 | function pxShader(source,type){ 129 | var shader = gl.createShader(type); 130 | gl.shaderSource(shader, source); 131 | gl.compileShader(shader); 132 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 133 | console.log(gl.getShaderInfoLog(shader)); 134 | return null; 135 | } 136 | return shader; 137 | } 138 | 139 | function pxProgram(vid, fid) { 140 | var program = gl.createProgram(); 141 | gl.attachShader(program, vid); 142 | gl.attachShader(program, fid); 143 | gl.linkProgram(program); 144 | if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { 145 | throw gl.getProgramInfoLog(program); 146 | } 147 | return program; 148 | } 149 | 150 | 151 | function pxBB(){ 152 | //put together a basic billboard geometry to fill the screen 153 | this.vert = gl.createBuffer(); 154 | initBuffer(this.vert,[ 155 | -1.0, 1.0, 0.0, 156 | -1.0, -1.0,0.0, 157 | 1.0, 1.0,0.0, 158 | 1.0, -1.0,0.0 159 | ]); 160 | 161 | this.tex = gl.createBuffer(); 162 | initBuffer(this.tex,[ 163 | 0,1, 164 | 0,0, 165 | 1,1, 166 | 1,0]); 167 | 168 | this.color = gl.createBuffer(); 169 | initBuffer(this.color,[ 170 | 1,1,1,1, 171 | 1,1,1,1, 172 | 1,1,1,1, 173 | 1,1,1,1]); 174 | } 175 | pxBB.prototype.predraw = function(pgm){ 176 | //hook up the vertex attributes 177 | //assumes the vertex shader used will have pos,color, texcoord inputs 178 | gl.useProgram(pgm); 179 | pgm.vertexPosAttrib = gl.getAttribLocation(pgm, 'pos'); 180 | gl.enableVertexAttribArray(pgm.vertexPosAttrib); 181 | 182 | pgm.vertexColorAttrib = gl.getAttribLocation(pgm, 'color'); 183 | gl.enableVertexAttribArray(pgm.vertexColorAttrib); 184 | 185 | pgm.vertexTexAttrib = gl.getAttribLocation(pgm, 'texcoord'); 186 | gl.enableVertexAttribArray(pgm.vertexTexAttrib); 187 | gl.bindBuffer(gl.ARRAY_BUFFER, this.color); 188 | gl.vertexAttribPointer(pgm.vertexColorAttrib, 4, gl.FLOAT, false, 0, 0); 189 | gl.bindBuffer(gl.ARRAY_BUFFER, this.vert); 190 | gl.vertexAttribPointer(pgm.vertexPosAttrib, 3, gl.FLOAT, false, 0, 0); 191 | gl.bindBuffer(gl.ARRAY_BUFFER, this.tex); 192 | gl.vertexAttribPointer(pgm.vertexTexAttrib, 2, gl.FLOAT, false, 0, 0); 193 | } 194 | pxBB.prototype.draw = function(pgm,texture){ 195 | this.predraw(pgm); 196 | gl.uniform1i(gl.getUniformLocation(pgm,"tex0"), 0); 197 | gl.activeTexture(gl.TEXTURE0); 198 | gl.bindTexture(gl.TEXTURE_2D, texture); 199 | gl.drawArrays(gl.TRIANGLE_STRIP, 0,4); 200 | } 201 | 202 | pxBB.prototype.draw2 = function(pgm,texture1,texture2){ 203 | this.predraw(pgm); 204 | gl.uniform1i(gl.getUniformLocation(pgm,"tex0"), 0); 205 | gl.uniform1i(gl.getUniformLocation(pgm,"tex1"), 1); 206 | gl.activeTexture(gl.TEXTURE0); 207 | gl.bindTexture(gl.TEXTURE_2D, texture1); 208 | gl.activeTexture(gl.TEXTURE1); 209 | gl.bindTexture(gl.TEXTURE_2D, texture2); 210 | gl.drawArrays(gl.TRIANGLE_STRIP, 0,4); 211 | } 212 | 213 | pxBB.prototype.draw3 = function(pgm,texture1,texture2,texture3){ 214 | this.predraw(pgm); 215 | gl.uniform1i(gl.getUniformLocation(pgm,"tex0"), 0); 216 | gl.uniform1i(gl.getUniformLocation(pgm,"tex1"), 1); 217 | gl.uniform1i(gl.getUniformLocation(pgm,"tex2"), 2); 218 | gl.activeTexture(gl.TEXTURE0); 219 | gl.bindTexture(gl.TEXTURE_2D, texture1); 220 | gl.activeTexture(gl.TEXTURE1); 221 | gl.bindTexture(gl.TEXTURE_2D, texture2); 222 | gl.activeTexture(gl.TEXTURE2); 223 | gl.bindTexture(gl.TEXTURE_2D, texture3); 224 | gl.drawArrays(gl.TRIANGLE_STRIP, 0,4); 225 | } 226 | 227 | pxBB.prototype.draw4 = function(pgm,texture1,texture2,texture3,texture4){ 228 | this.predraw(pgm); 229 | gl.uniform1i(gl.getUniformLocation(pgm,"tex0"), 0); 230 | gl.uniform1i(gl.getUniformLocation(pgm,"tex1"), 1); 231 | gl.uniform1i(gl.getUniformLocation(pgm,"tex2"), 2); 232 | gl.uniform1i(gl.getUniformLocation(pgm,"tex3"), 3); 233 | gl.activeTexture(gl.TEXTURE0); 234 | gl.bindTexture(gl.TEXTURE_2D, texture1); 235 | gl.activeTexture(gl.TEXTURE1); 236 | gl.bindTexture(gl.TEXTURE_2D, texture2); 237 | gl.activeTexture(gl.TEXTURE2); 238 | gl.bindTexture(gl.TEXTURE_2D, texture3); 239 | gl.activeTexture(gl.TEXTURE3); 240 | gl.bindTexture(gl.TEXTURE_2D, texture4); 241 | gl.drawArrays(gl.TRIANGLE_STRIP, 0,4); 242 | } 243 | 244 | function initBuffer(buf,dataset){ 245 | gl.bindBuffer(gl.ARRAY_BUFFER, buf); 246 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(dataset), gl.STATIC_DRAW); 247 | } 248 | --------------------------------------------------------------------------------