本文介绍了在WebGL v.s.模拟基于调色板的图形画布2D的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 目前,我使用2D画布上下文来绘制一个生成的图像(从像素到像素,但在生成的帧之后一次刷新为整个缓冲区),大约是25fps。生成的图像总是每个像素一个字节(整数/类型数组),固定调色板用于生成RGB最终结果。缩放还需要采用画布的大小(即:全屏)和/或使用请求(放大/缩小按钮)。 2D上下文的canvas是可以为此目的,但我很好奇,如果WebGL可以提供更好的结果和/或更好的性能。请注意:我不想通过webGL放像素,我想把像素放入我的缓冲区(这基本上是Uint8Array),并使用该缓冲区(一次)刷新上下文。我不太了解WebGL,但使用所需的生成的图像作为某种形式的纹理会以某种方式工作的例子?然后我需要以大约25fps的速度刷新纹理。 如果WebGL支持颜色空间转换,这将是真的太棒了。使用2D上下文,我需要将JavaScript中的每个像素的pixelata转换1字节/像素缓冲区为RGBA ...缩放(2D上下文)现在是通过改变画布的高度/宽度样式,所以浏览器缩放图像然后。但是我想它可以慢于WebGL可以做的hw支持,还(我希望)WebGL可以给予更大的灵活性来控制缩放,例如与2D上下文,浏览器将做抗锯齿,即使我不想 我已经尝试学习几个WebGL教程,但所有的从对象,形状,3D立方体等开始,我不需要任何 - classic - 对象来渲染只有什么2D上下文可以做到 - 希望WebGL可以为同一个任务更快的解决方案!当然,如果在这里没有赢得所有的WebGL,我会继续使用2D上下文。 要清楚:这是一种计算机硬件模拟器做JavaScript和它的输出(将在连接到它的PAL电视上看到)通过画布上下文呈现。该机器有256个元素的固定调色板,在内部它只需要一个字节的像素来定义它的颜色。解决方案使用纹理作为您的调色板和不同的纹理作为您的图像。然后从图像纹理中获取值,并使用它从调色板纹理中查找颜色。 调色板纹理是256x1 RGBA像素。你的图像纹理是任何你想要的尺寸,但只是一个单通道ALPHA纹理。然后,您可以从映像查找值 float index = texture2D(u_image,v_texcoord).a * 255.0; 并使用该值在调色板中查找颜色 gl_FragColor = texture2D(u_palette,vec2((index + 0.5)/ 256.0,0.5)); 您的着色器可能是这样的 Vertex Shader 属性vec4 a_position; varying vec2 v_texcoord; void main(){ gl_Position = a_position; //假设一个单位四边形的位置we //可以使用texcoords。翻转Y虽然所以我们得到的顶部在0 v_texcoord = a_position.xy * vec2(0.5,-0.5)+0.5; } 片段着色器 precision mediump float; varying vec2 v_texcoord; uniform sampler2D u_image; uniform sampler2D u_palette; void main(){ float index = texture2D(u_image,v_texcoord).a * 255.0; gl_FragColor = texture2D(u_palette,vec2((index + 0.5)/ 256.0,0.5)); } 然后你只需要一个调色板纹理。 //设置调色板。 var palette = new Uint8Array(256 * 4); //我很懒,所以只是在调色板中设置4种颜色 function setPalette(index,r,g,b,a){ palette [index * 4 + 0 ] = r; palette [index * 4 + 1] = g; palette [index * 4 + 2] = b; palette [index * 4 + 3] = a; } setPalette(1,255,0,0,255); // red setPalette(2,0,255,0,255); // green setPalette(3,0,0,255,255); // blue //上传调色板 ... gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,256,1,0,gl.RGBA, gl.UNSIGNED_BYTE,palette); 和你的形象。 //制作图片。只是要做一些东西8x8 var image = new Uint8Array([ 0,0,1,1,1,1,0,0, 0,1,0,0,0 ,0,1,0, 1,0,0,0,0,0,0,1, 1,0,2,0,0,2,0,1, 1,0,0,0,0,0,0,1, 1,0,3,3,3,3,0,1, 0,1,0,0,0 ,0,1,0, 0,0,1,1,1,1,0,0,]); //上传图片 .... gl.texImage2D(gl.TEXTURE_2D,0,gl.ALPHA,8,8,0,gl.ALPHA, gl.UNSIGNED_BYTE,image); 您还需要确保两个纹理都使用 gl.NEAREST 过滤,因为一个表示索引,另一个表示调色板,在这些情况下,值之间的过滤是没有意义的。 gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST); 这里有一个工作示例: div class =snippetdata-lang =jsdata-hide =true> var canvas = document.getElementById(c); var gl = canvas.getContext(webgl); //注意:createProgramFromScripts将调用bindAttribLocation //基于索引attibute名称我们传递给it.var program = twgl.createProgramFromScripts(gl,[vshader,fshader],[a_position,a_textureIndex]); gl.useProgram(program); var imageLoc = gl.getUniformLocation (program,u_image); var paletteLoc = gl.getUniformLocation(program,u_palette); //告诉它使用纹理单元0和1作为图像和palettegl.uniform1i(imageLoc,0); gl.uniform1i paletteLoc,1); //设置单位quadvar positions = [1,1,-1,1,-1,-1,1,1,-1,-1,1,-1,]; var vertBuffer = gl .createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,vertBuffer); gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(positions),gl.STATIC_DRAW); gl.enableVertexAttribArray(0); gl.vertexAttribPointer(0, gl.FLOAT,false,0,0); //设置palette.var palette = new Uint8Array(256 * 4); //我很懒,所以只是在palettefunction中设置4种颜色setPalette(index,r,g,b ,a){palette [index * 4 + 0] = r; palette [index * 4 + 1] = g; palette [index * 4 + 2] = b; palette [index * 4 + 3] = a;} setPalette(1,255,0,0,255); // redsetPalette(2,0,255,0,255); // greensetPalette(3,0,0,255,255); // blue // make palette texture and upload palettegl.activeTexture(gl.TEXTURE1); var paletteTex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D,paletteTex); 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.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D,gl。 TEXTURE_MAG_FILTER,gl.NEAREST); gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,256,1,0,gl.RGBA,gl.UNSIGNED_BYTE,palette); //创建图像。只是要做一些东西8x8var image = new Uint8Array([0,0,1,1,1,1,0,0,0,1,0,0,0,0,1,0,1,0,0, 0,0,0,0,1,1,0,2,0,0,2,0,1,1,0,0,0,0,0,0,1,1,0,3,3, 3,3,0,1,0,1,0,0,0,0,1,0,0,0,1,1,1,1,0,0,]); // make image textures and upload imagegl.activeTexture(gl.TEXTURE0); var imageTex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D,imageTex); 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.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl .NEAREST); gl.texImage2D(gl.TEXTURE_2D,0,gl.ALPHA,8,8,0,gl.ALPHA,gl.UNSIGNED_BYTE,image); gl.drawArrays(gl.TRIANGLES,0,positions.length / 2); canvas {border:1px solid black; } < script src =https:// twgljs .org / dist / twgl.min.js>< / script>< script id =vshadertype =whatever>属性vec4 a_position; vec2 v_texcoord; void main(){gl_Position = a_position; //假设一个单位四边形的位置,我们可以只使用texcoords。翻转Y虽然所以我们得到的顶部在0 v_texcoord = a_position.xy * vec2(0.5,-0.5)+0.5; }< / script>< script id =fshadertype =whatever> precision mediump float; varying vec2 v_texcoord; uniform sampler2D u_image; uniform sampler2D u_palette; void main(){float index = texture2D(u_image,v_texcoord).a * 255.0; gl_FragColor = texture2D(u_palette,vec2((index + 0.5)/ 256.0,0.5));}< / script>< canvas id =cwidth =256height =256>< / canvas> ; 要制作动画才能更新图片,然后重新上传到纹理中 gl.texImage2D(gl.TEXTURE_2D,0,gl.ALPHA,8,8,0,gl.ALPHA , gl.UNSIGNED_BYTE,image); 示例: var canvas = document.getElementById(c); var gl = canvas.getContext(webgl); //注意:createProgramFromScripts将根据attibute名称的索引调用bindAttribLocation //传递给it.var program = twgl.createProgramFromScripts(gl,[vshader,fshader],[a_position,a_textureIndex]); gl.useProgram(program); var imageLoc = gl.getUniformLocation u_image); var paletteLoc = gl.getUniformLocation(program,u_palette); //告诉它使用纹理单元0和1作为图像和palettegl.uniform1i(imageLoc,0); gl.uniform1i(paletteLoc,1 ); //设置单位quadvar positions = [1,1,-1,1,-1,-1,1,1,-1,-1,1,-1,]; var vertBuffer = gl.createBuffer ); gl.bindBuffer(gl.ARRAY_BUFFER,vertBuffer); gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(positions),gl.STATIC_DRAW); gl.enableVertexAttribArray(0); gl.vertexAttribPointer(0,2,gl.FLOAT ,false,0,0); //设置palette.var palette = new Uint8Array(256 * 4); //我很懒,所以只是在palettefunction中设置4种颜色setPalette(index,r,g,b, {palette [index * 4 + 0] = r; palette [index * 4 + 1] = g; palette [index * 4 + 2] = b; palette [index * 4 + 3] = a;} setPalette(1,255,0,0,255); // redsetPalette(2,0,255,0,255); // greensetPalette(3,0,0,255,255); // blue // make palette texture and upload palettegl.activeTexture(gl.TEXTURE1); var paletteTex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D,paletteTex); 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.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D,gl。 TEXTURE_MAG_FILTER,gl.NEAREST); gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,256,1,0,gl.RGBA,gl.UNSIGNED_BYTE,palette); //创建图像。只是要做一些8x8var width = 8; var height = 8; var image = new Uint8Array([0,0,1,1,1,1,0,0,0,1,0,0,0,0, 1,0,1,0,0,0,0,0,0,1,1,0,2,0,0,2,0,1,1,0,0,0,0,0,0, 1,1,0,3,3,3,3,0,1,0,1,0,0,0,0,1,0,0,0,1,1,1,1,0,0, ]); // make image textures and upload imagegl.activeTexture(gl.TEXTURE0); var imageTex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D,imageTex); 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.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl .NEAREST); gl.texImage2D(gl.TEXTURE_2D,0,gl.ALPHA,width,height,0,gl.ALPHA,gl.UNSIGNED_BYTE,image); var frameCounter = 0; function render(){++ frameCounter; // skip 3 of 4 frames so the animation is not too if if((frameCounter& 3)== 0){//将图像左旋转(var y = 0; y < script src =https:// twgljs .org / dist / twgl.min.js>< / script>< script id =vshadertype =whatever>属性vec4 a_position; vec2 v_texcoord; void main(){gl_Position = a_position; //假设一个单位四边形的位置,我们可以只使用texcoords。翻转Y虽然所以我们得到的顶部在0 v_texcoord = a_position.xy * vec2(0.5,-0.5)+0.5; }< / script>< script id =fshadertype =whatever> precision mediump float; varying vec2 v_texcoord; uniform sampler2D u_image; uniform sampler2D u_palette; void main(){float index = texture2D(u_image,v_texcoord).a * 255.0; gl_FragColor = texture2D(u_palette,vec2((index + 0.5)/ 256.0,0.5));}< / script>< canvas id =cwidth =256height =256>< / canvas> ; 当然假设你的目标是CPU通过操作像素。否则,你可以使用任何正常的webgl技术来处理纹理坐标或任何东西。 你也可以为调色板动画更新调色板。只需修改调色板并重新上传即可。 gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,256,1 ,0,gl.RGBA, gl.UNSIGNED_BYTE,palette); 示例: var canvas = document.getElementById(c); var gl = canvas.getContext(webgl); //注意:createProgramFromScripts将根据attibute名称的索引调用bindAttribLocation //传递给it.var program = twgl.createProgramFromScripts(gl,[vshader,fshader],[a_position,a_textureIndex]); gl.useProgram(program); var imageLoc = gl.getUniformLocation u_image); var paletteLoc = gl.getUniformLocation(program,u_palette); //告诉它使用纹理单元0和1作为图像和palettegl.uniform1i(imageLoc,0); gl.uniform1i(paletteLoc,1 ); //设置单位quadvar positions = [1,1,-1,1,-1,-1,1,1,-1,-1,1,-1,]; var vertBuffer = gl.createBuffer ); gl.bindBuffer(gl.ARRAY_BUFFER,vertBuffer); gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(positions),gl.STATIC_DRAW); gl.enableVertexAttribArray(0); gl.vertexAttribPointer(0,2,gl.FLOAT ,false,0,0); //设置palette.var palette = new Uint8Array(256 * 4); //我很懒,所以只是在palettefunction中设置4种颜色setPalette(index,r,g,b, {palette [index * 4 + 0] = r; palette [index * 4 + 1] = g; palette [index * 4 + 2] = b; palette [index * 4 + 3] = a;} setPalette(1,255,0,0,255); // redsetPalette(2,0,255,0,255); // greensetPalette(3,0,0,255,255); // blue // make palette texture and upload palettegl.activeTexture(gl.TEXTURE1); var paletteTex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D,paletteTex); 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.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D,gl。 TEXTURE_MAG_FILTER,gl.NEAREST); gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,256,1,0,gl.RGBA,gl.UNSIGNED_BYTE,palette); //创建图像。只是要做一些8x8var width = 8; var height = 8; var image = new Uint8Array([0,0,1,1,1,1,0,0,0,1,0,0,0,0, 1,0,1,0,0,0,0,0,0,1,1,0,2,0,0,2,0,1,1,0,0,0,0,0,0, 1,1,0,3,3,3,3,0,1,0,1,0,0,0,0,1,0,0,0,1,1,1,1,0,0, ]); // make image textures and upload imagegl.activeTexture(gl.TEXTURE0); var imageTex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D,imageTex); 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.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl .NEAREST); gl.texImage2D(gl.TEXTURE_2D,0,gl.ALPHA,width,height,0,gl.ALPHA,gl.UNSIGNED_BYTE,image); var frameCounter = 0; function render(){++ frameCounter; // skip 3 of 4 frames so the animation does not too fast if((frameCounter& 3)== 0){// rotate the 3 palette colors var tempR = palette [4 + 0]; var tempG = palette [4 + 1]; var tempB = palette [4 + 2]; var tempA = palette [4 + 3]; setPalette(1,palette [2 * 4 + 0],palette [2 * 4 + 1],palette [2 * 4 + 2],palette [2 * 4 + 3]); setPalette(2,palette [3 * 4 + 0],palette [3 * 4 + 1],palette [3 * 4 + 2],palette [3 * 4 + 3]); setPalette(3,tempR,tempG,tempB,tempA); //重新上传调色板gl.activeTexture(gl.TEXTURE1); gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,256,1,0,gl.RGBA,gl.UNSIGNED_BYTE,palette); gl.drawArrays(gl.TRIANGLES,0,positions.length / 2); } requestAnimationFrame(render);} render(); < script src =https:// twgljs .org / dist / twgl.min.js>< / script>< script id =vshadertype =whatever>属性vec4 a_position; vec2 v_texcoord; void main(){gl_Position = a_position; //假设一个单位四边形的位置,我们可以只使用texcoords。翻转Y虽然所以我们得到的顶部在0 v_texcoord = a_position.xy * vec2(0.5,-0.5)+0.5; }< / script>< script id =fshadertype =whatever> precision mediump float; vec2 v_texcoord; uniform sampler2D u_image; uniform sampler2D u_palette; void main(){float index = texture2D(u_image,v_texcoord).a * 255.0; gl_FragColor = texture2D(u_palette,vec2((index + 0.5)/ 256.0,0.5));}< / script>< canvas id =cwidth =256height =256>< / canvas> ; 略微相关的是这个tile shader示例 http://blog.tojicode.com/2012/07/ sprite-tile-maps-on-gpu.html Currently, I'm using 2D canvas context to draw an image generated (from pixel to pixel, but refreshed as a whole buffer in once after a generated frame) from JavaScript at about a 25fps rate. The generated image is always one byte (integer / typed array) per pixel and a fixed palette is used to generate RGB final result. Scaling is also needed to adopt to the size of the canvas (ie: going to fullscreen) and/or use request (zoom in/out buttons).The 2D context of canvas is OK for this purpose, however I'm curious if WebGL can provide better result and/or better performance. Please note: I don't want to put pixels via webGL, I want to put pixels into my buffer (which is basically Uint8Array), and use that buffer (in once) to refresh the context. I don't know too much about WebGL, but using the needed generated image as some kind of texture would work somehow for example? Then I would need to refresh the texture at about 25fps rate, I guess.It would be really fantastic, if WebGL support the colour space conversion somehow. With 2D context, I need to convert 1 byte / pixel buffer into RGBA for the imagedata in JavaScript for every pixel ... Scaling (for 2D context) is done now by altering the hight/width style of the canvas, so browsers scales the image then. However I guess it can be slower than what WebGL can do with hw support, and also (I hope) WebGL can give greater flexibility to control the scaling, eg with the 2D context, browsers will do antialiasing even if I don't want to do (eg: integer zooming factor), and maybe that's a reason it can be quite slow sometimes.I've already tried to learn several WebGL tutorials but all of them starts with objects, shapes, 3D cubes, etc, I don't need any - classical - object to render only what 2D context can do as well - in the hope that WebGL can be a faster solution for the very same task! Of course if there is no win here with WebGL at all, I would continue to use 2D context.To be clear: this is some kind of computer hardware emulator done in JavaScript, and its output (what would be seen on a PAL TV connectected to it) is rendered via a canvas context. The machine has fixed palette with 256 elements, internally it only needs one byte for a pixel to define its colour. 解决方案 You can use a texture as your palette and a different texture as your image. You then get a value from the image texture and use it too look up a color from the palette texture.The palette texture is 256x1 RGBA pixels. Your image texture is any size you want but just a single channel ALPHA texture. You can then look up a value from the image float index = texture2D(u_image, v_texcoord).a * 255.0;And use that value to look up a color in the palette gl_FragColor = texture2D(u_palette, vec2((index + 0.5) / 256.0, 0.5));Your shaders might be something like thisVertex Shaderattribute vec4 a_position;varying vec2 v_texcoord;void main() { gl_Position = a_position; // assuming a unit quad for position we // can just use that for texcoords. Flip Y though so we get the top at 0 v_texcoord = a_position.xy * vec2(0.5, -0.5) + 0.5;}Fragment shaderprecision mediump float;varying vec2 v_texcoord;uniform sampler2D u_image;uniform sampler2D u_palette;void main() { float index = texture2D(u_image, v_texcoord).a * 255.0; gl_FragColor = texture2D(u_palette, vec2((index + 0.5) / 256.0, 0.5));}Then you just need a palette texture. // Setup a palette. var palette = new Uint8Array(256 * 4); // I'm lazy so just setting 4 colors in palette function setPalette(index, r, g, b, a) { palette[index * 4 + 0] = r; palette[index * 4 + 1] = g; palette[index * 4 + 2] = b; palette[index * 4 + 3] = a; } setPalette(1, 255, 0, 0, 255); // red setPalette(2, 0, 255, 0, 255); // green setPalette(3, 0, 0, 255, 255); // blue // upload palette ... gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 256, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, palette);And your image. It's an alpha only image so just 1 channel. // Make image. Just going to make something 8x8 var image = new Uint8Array([ 0,0,1,1,1,1,0,0, 0,1,0,0,0,0,1,0, 1,0,0,0,0,0,0,1, 1,0,2,0,0,2,0,1, 1,0,0,0,0,0,0,1, 1,0,3,3,3,3,0,1, 0,1,0,0,0,0,1,0, 0,0,1,1,1,1,0,0, ]); // upload image .... gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, 8, 8, 0, gl.ALPHA, gl.UNSIGNED_BYTE, image);You also need to make sure both textures are using gl.NEAREST for filtering since one represents indices and the other a palette and filtering between values in those cases makes no sense.gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);Here's a working example:var canvas = document.getElementById("c");var gl = canvas.getContext("webgl");// Note: createProgramFromScripts will call bindAttribLocation// based on the index of the attibute names we pass to it.var program = twgl.createProgramFromScripts( gl, ["vshader", "fshader"], ["a_position", "a_textureIndex"]);gl.useProgram(program);var imageLoc = gl.getUniformLocation(program, "u_image");var paletteLoc = gl.getUniformLocation(program, "u_palette");// tell it to use texture units 0 and 1 for the image and palettegl.uniform1i(imageLoc, 0);gl.uniform1i(paletteLoc, 1);// Setup a unit quadvar positions = [ 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1,];var vertBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);gl.enableVertexAttribArray(0);gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);// Setup a palette.var palette = new Uint8Array(256 * 4);// I'm lazy so just setting 4 colors in palettefunction setPalette(index, r, g, b, a) { palette[index * 4 + 0] = r; palette[index * 4 + 1] = g; palette[index * 4 + 2] = b; palette[index * 4 + 3] = a;}setPalette(1, 255, 0, 0, 255); // redsetPalette(2, 0, 255, 0, 255); // greensetPalette(3, 0, 0, 255, 255); // blue// make palette texture and upload palettegl.activeTexture(gl.TEXTURE1);var paletteTex = gl.createTexture();gl.bindTexture(gl.TEXTURE_2D, paletteTex);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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 256, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, palette);// Make image. Just going to make something 8x8var image = new Uint8Array([ 0,0,1,1,1,1,0,0, 0,1,0,0,0,0,1,0, 1,0,0,0,0,0,0,1, 1,0,2,0,0,2,0,1, 1,0,0,0,0,0,0,1, 1,0,3,3,3,3,0,1, 0,1,0,0,0,0,1,0, 0,0,1,1,1,1,0,0,]);// make image textures and upload imagegl.activeTexture(gl.TEXTURE0);var imageTex = gl.createTexture();gl.bindTexture(gl.TEXTURE_2D, imageTex);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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, 8, 8, 0, gl.ALPHA, gl.UNSIGNED_BYTE, image);gl.drawArrays(gl.TRIANGLES, 0, positions.length / 2);canvas { border: 1px solid black; }<script src="https://twgljs.org/dist/twgl.min.js"></script><script id="vshader" type="whatever"> attribute vec4 a_position; varying vec2 v_texcoord; void main() { gl_Position = a_position; // assuming a unit quad for position we // can just use that for texcoords. Flip Y though so we get the top at 0 v_texcoord = a_position.xy * vec2(0.5, -0.5) + 0.5; }</script><script id="fshader" type="whatever">precision mediump float;varying vec2 v_texcoord;uniform sampler2D u_image;uniform sampler2D u_palette;void main() { float index = texture2D(u_image, v_texcoord).a * 255.0; gl_FragColor = texture2D(u_palette, vec2((index + 0.5) / 256.0, 0.5));}</script><canvas id="c" width="256" height="256"></canvas>To animate just update the image and then re-upload it into the texture gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, 8, 8, 0, gl.ALPHA, gl.UNSIGNED_BYTE, image);Example:var canvas = document.getElementById("c");var gl = canvas.getContext("webgl");// Note: createProgramFromScripts will call bindAttribLocation// based on the index of the attibute names we pass to it.var program = twgl.createProgramFromScripts( gl, ["vshader", "fshader"], ["a_position", "a_textureIndex"]);gl.useProgram(program);var imageLoc = gl.getUniformLocation(program, "u_image");var paletteLoc = gl.getUniformLocation(program, "u_palette");// tell it to use texture units 0 and 1 for the image and palettegl.uniform1i(imageLoc, 0);gl.uniform1i(paletteLoc, 1);// Setup a unit quadvar positions = [ 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1,];var vertBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);gl.enableVertexAttribArray(0);gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);// Setup a palette.var palette = new Uint8Array(256 * 4);// I'm lazy so just setting 4 colors in palettefunction setPalette(index, r, g, b, a) { palette[index * 4 + 0] = r; palette[index * 4 + 1] = g; palette[index * 4 + 2] = b; palette[index * 4 + 3] = a;}setPalette(1, 255, 0, 0, 255); // redsetPalette(2, 0, 255, 0, 255); // greensetPalette(3, 0, 0, 255, 255); // blue// make palette texture and upload palettegl.activeTexture(gl.TEXTURE1);var paletteTex = gl.createTexture();gl.bindTexture(gl.TEXTURE_2D, paletteTex);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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 256, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, palette);// Make image. Just going to make something 8x8var width = 8;var height = 8;var image = new Uint8Array([ 0,0,1,1,1,1,0,0, 0,1,0,0,0,0,1,0, 1,0,0,0,0,0,0,1, 1,0,2,0,0,2,0,1, 1,0,0,0,0,0,0,1, 1,0,3,3,3,3,0,1, 0,1,0,0,0,0,1,0, 0,0,1,1,1,1,0,0,]);// make image textures and upload imagegl.activeTexture(gl.TEXTURE0);var imageTex = gl.createTexture();gl.bindTexture(gl.TEXTURE_2D, imageTex);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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, width, height, 0, gl.ALPHA, gl.UNSIGNED_BYTE, image);var frameCounter = 0;function render() { ++frameCounter; // skip 3 of 4 frames so the animation is not too fast if ((frameCounter & 3) == 0) { // rotate the image left for (var y = 0; y < height; ++y) { var temp = image[y * width]; for (var x = 0; x < width - 1; ++x) { image[y * width + x] = image[y * width + x + 1]; } image[y * width + width - 1] = temp; } // re-upload image gl.activeTexture(gl.TEXTURE0); gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, width, height, 0, gl.ALPHA, gl.UNSIGNED_BYTE, image); gl.drawArrays(gl.TRIANGLES, 0, positions.length / 2); } requestAnimationFrame(render);}render();canvas { border: 1px solid black; }<script src="https://twgljs.org/dist/twgl.min.js"></script><script id="vshader" type="whatever"> attribute vec4 a_position; varying vec2 v_texcoord; void main() { gl_Position = a_position; // assuming a unit quad for position we // can just use that for texcoords. Flip Y though so we get the top at 0 v_texcoord = a_position.xy * vec2(0.5, -0.5) + 0.5; }</script><script id="fshader" type="whatever">precision mediump float;varying vec2 v_texcoord;uniform sampler2D u_image;uniform sampler2D u_palette;void main() { float index = texture2D(u_image, v_texcoord).a * 255.0; gl_FragColor = texture2D(u_palette, vec2((index + 0.5) / 256.0, 0.5));}</script><canvas id="c" width="256" height="256"></canvas>Of course that assumes your goal is to do the animation on the CPU by manipulating pixels. Otherwise you can use any normal webgl techniques to manipulate texture coordinates or whatever.You can also update the palette similarly for palette animation. Just modify the palette and re-upload it gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 256, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, palette);Example:var canvas = document.getElementById("c");var gl = canvas.getContext("webgl");// Note: createProgramFromScripts will call bindAttribLocation// based on the index of the attibute names we pass to it.var program = twgl.createProgramFromScripts( gl, ["vshader", "fshader"], ["a_position", "a_textureIndex"]);gl.useProgram(program);var imageLoc = gl.getUniformLocation(program, "u_image");var paletteLoc = gl.getUniformLocation(program, "u_palette");// tell it to use texture units 0 and 1 for the image and palettegl.uniform1i(imageLoc, 0);gl.uniform1i(paletteLoc, 1);// Setup a unit quadvar positions = [ 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1,];var vertBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);gl.enableVertexAttribArray(0);gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);// Setup a palette.var palette = new Uint8Array(256 * 4);// I'm lazy so just setting 4 colors in palettefunction setPalette(index, r, g, b, a) { palette[index * 4 + 0] = r; palette[index * 4 + 1] = g; palette[index * 4 + 2] = b; palette[index * 4 + 3] = a;}setPalette(1, 255, 0, 0, 255); // redsetPalette(2, 0, 255, 0, 255); // greensetPalette(3, 0, 0, 255, 255); // blue// make palette texture and upload palettegl.activeTexture(gl.TEXTURE1);var paletteTex = gl.createTexture();gl.bindTexture(gl.TEXTURE_2D, paletteTex);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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 256, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, palette);// Make image. Just going to make something 8x8var width = 8;var height = 8;var image = new Uint8Array([ 0,0,1,1,1,1,0,0, 0,1,0,0,0,0,1,0, 1,0,0,0,0,0,0,1, 1,0,2,0,0,2,0,1, 1,0,0,0,0,0,0,1, 1,0,3,3,3,3,0,1, 0,1,0,0,0,0,1,0, 0,0,1,1,1,1,0,0,]);// make image textures and upload imagegl.activeTexture(gl.TEXTURE0);var imageTex = gl.createTexture();gl.bindTexture(gl.TEXTURE_2D, imageTex);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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, width, height, 0, gl.ALPHA, gl.UNSIGNED_BYTE, image);var frameCounter = 0;function render() { ++frameCounter; // skip 3 of 4 frames so the animation is not too fast if ((frameCounter & 3) == 0) { // rotate the 3 palette colors var tempR = palette[4 + 0]; var tempG = palette[4 + 1]; var tempB = palette[4 + 2]; var tempA = palette[4 + 3]; setPalette(1, palette[2 * 4 + 0], palette[2 * 4 + 1], palette[2 * 4 + 2], palette[2 * 4 + 3]); setPalette(2, palette[3 * 4 + 0], palette[3 * 4 + 1], palette[3 * 4 + 2], palette[3 * 4 + 3]); setPalette(3, tempR, tempG, tempB, tempA); // re-upload palette gl.activeTexture(gl.TEXTURE1); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 256, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, palette); gl.drawArrays(gl.TRIANGLES, 0, positions.length / 2); } requestAnimationFrame(render);}render();canvas { border: 1px solid black; }<script src="https://twgljs.org/dist/twgl.min.js"></script><script id="vshader" type="whatever"> attribute vec4 a_position; varying vec2 v_texcoord; void main() { gl_Position = a_position; // assuming a unit quad for position we // can just use that for texcoords. Flip Y though so we get the top at 0 v_texcoord = a_position.xy * vec2(0.5, -0.5) + 0.5; }</script><script id="fshader" type="whatever">precision mediump float;varying vec2 v_texcoord;uniform sampler2D u_image;uniform sampler2D u_palette;void main() { float index = texture2D(u_image, v_texcoord).a * 255.0; gl_FragColor = texture2D(u_palette, vec2((index + 0.5) / 256.0, 0.5));}</script><canvas id="c" width="256" height="256"></canvas>Slightly related is this tile shader examplehttp://blog.tojicode.com/2012/07/sprite-tile-maps-on-gpu.html 这篇关于在WebGL v.s.模拟基于调色板的图形画布2D的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
09-09 18:27