├── 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 |
--------------------------------------------------------------------------------