Html5 canvas 片段着色器-确定整个(单色)图像的最小/最大值,并将其用于进一步的像素操作

Html5 canvas 片段着色器-确定整个(单色)图像的最小/最大值,并将其用于进一步的像素操作,html5-canvas,webgl,Html5 Canvas,Webgl,我想用这种方式规范化单色图像像素,最小值为黑色,最大值为白色,中间的值按比例分布。 目前我在canvas中分两步完成,但我相信在WebGL中应该更快 我可以想象通过片段着色器操纵颜色,但我找不到任何有效的方法(1)确定图像的实际范围,也找不到(2)将此信息传递给另一个片段着色器的方法,然后可以执行灰度标准化。似乎可以在片段着色器中生成越来越小的纹理,并在每个纹理中写出最小值和最大值。因此,例如,如果您有一个16x16纹理,则每2x2像素写出一个表示最大值的像素 vec4 c00 = textu

我想用这种方式规范化单色图像像素,最小值为黑色,最大值为白色,中间的值按比例分布。 目前我在canvas中分两步完成,但我相信在WebGL中应该更快


我可以想象通过片段着色器操纵颜色,但我找不到任何有效的方法(1)确定图像的实际范围,也找不到(2)将此信息传递给另一个片段着色器的方法,然后可以执行灰度标准化。

似乎可以在片段着色器中生成越来越小的纹理,并在每个纹理中写出最小值和最大值。因此,例如,如果您有一个16x16纹理,则每2x2像素写出一个表示最大值的像素

 vec4 c00 = texture2D(sampler, uv);
 vec4 c10 = texture2D(sampler, uv + vec2(onePixelRight, 0));
 vec4 c01 = texture2D(sampler, uv + vec2(0, onePixelUp));
 vec4 c11 = texture2D(sampler, uv + vec2(onePixelRight, onePixelUp);
 gl_FragColor = max(max(c00, c10), max(c01, c11));
重复此操作,直到达到1x1像素。对min执行相同操作。完成后,您将拥有2个1x1像素纹理。使用readPixels读取它们,或者将它们作为范围传递给另一个着色器

使用较大的块可能会更快,而不是2x2-do-8x8或16x16区域,但在达到1x1像素之前,请继续缩小

在伪代码中

// setup
textures = [];
framebuffers = [];
cellSize = 16
maxDimension = max(width, height)
w = width 
h = height
while w > 1 || h > 1
   w = max(1, w / cellSize)
   h = max(1, h / cellSize)
   textures.push(create Texture of size w, h)
   framebuffers.push(create framebuffer and attach texture)
}
  
// computation
bind original image as input texture
foreach(framebuffer)
   bind framebuffer
   render to framebuffer with max GLSL shader above
   bind texture of current framebuffer as input to next iteration
}
现在,将最后一个帧缓冲区设置为1x1像素纹理,其中包含最大值

“严格使用”;
var-cellSize=2;
//制作一个纹理作为我们的源
var ctx=document.createElement(“canvas”).getContext(“2d”);
ctx.fillStyle=“rgb(12,34,56)”;
ctx.fillRect(20,30,1,1);
ctx.fillStyle=“rgb(254243232)”;
ctx.fillRect(270,140,1,1);
var canvas=document.createElement(“canvas”);
var m4=twgl.m4;
var gl=canvas.getContext(“webgl”);
var fsSrc=document.getElementById(“max fs”).text.replace(“$(cellSize)s”,cellSize);
var programInfo=twgl.createProgramInfo(gl,[“vs”,fsSrc]);
var unitQuadBufferInfo=twgl.primitives.createXYQuadBufferInfo(gl);
var framebufferInfo=twgl.createFramebufferInfo(gl);
var srcTex=twgl.createTexture(gl,{
src:ctx.canvas,
最小值:gl最近值,
杂志:德国劳埃德船级社,
包裹:总包夹至边缘,
});
var framebuffers=[];
var w=ctx.canvas.width;
var h=ctx.canvas.height;
而(w>1 | | h>1){
w=数学最大值(1,(w+cellSize-1)/cellSize | 0);
h=数学最大值(1,(h+cellSize-1)/cellSize | 0);
//创建帧缓冲区,创建并附着RGBA/未签名纹理
var fb=twgl.createFramebufferInfo(gl[
{min:gl.NEAREST,max:gl.NEAREST,wrap:gl.CLAMP_TO_EDGE},
],w,h);
帧缓冲区推送(fb);
}
变量={
分辨率:[ctx.canvas.width,ctx.canvas.height],
u_纹理:srcTex,
};
总账使用程序(programInfo.program);
twgl.setBuffersAndAttributes(总账、programInfo、unitQuadBufferInfo);
var w=ctx.canvas.width;
var h=ctx.canvas.height;
framebuffers.forEach(函数(fbi、ndx){
w=数学最大值(1,(w+cellSize-1)/cellSize | 0);
h=数学最大值(1,(h+cellSize-1)/cellSize | 0);
1.udstu分辨率=[w,h];
twgl.bindFramebufferInfo(德国劳埃德船级社,联邦调查局);
twgl.设置制服(程序信息、制服);
twgl.drawBufferInfo(总账,unitQuadBufferInfo);
uniforms.u_texture=fbi.attachments[0];
uniforms.u_src分辨率=[w,h];
});
var p=新的UINT8阵列(4);
gl.readPixels(0,0,1,1,gl.RGBA,gl.UNSIGNED_字节,p);
log(“max:,p[0],p[1],p[2]);
函数日志(){
var elem=document.createElement(“pre”);
elem.appendChild(document.createTextNode(Array.prototype.join.call(arguments)(“”));
文件.正文.附件(elem);
}

属性向量4位置;
void main(){
gl_位置=位置;
}
精密中泵浮子;
#定义单元大小$(单元大小)s
均匀的二维u_纹理;
均匀vec2u_分辨率;
均匀vec2udstu分辨率;
void main(){
//计算源单元格的第一个像素
vec2 srcPixel=地板(gl_FragCoord.xy)*浮点数(单元大小);
//源中的一个像素
vec2单像素=vec2(1)/u_分辨率;
//单元中第一个像素的uv。像素中心+0.5
vec2 uv=(srcPixel+0.5)*一个像素;
vec4 maxColor=vec4(0);
对于(int y=0;y
另请注意:从顶点着色器将最终UV作为
varyings
传递允许GPU进行预取,从而显著提高性能。手动生成的mipmap。。。它们真的可以在片段着色器中生成吗?到目前为止,我在这里发现了一些伪代码(注释8)。你是说这样的事吗?根据该描述,mipmap生成似乎是从着色器外部触发的。任何指向工作示例的链接在这里都会有所帮助;-)对不起,我不是说生成真正的mips。我的意思是逐步渲染纹理,每个都比前一个小,直到得到1x1像素的纹理,这就是你的答案。我更新了答案,没有提到mips,因为那是confusing@LJ,将最终UV传递为变光,GPU如何知道如何使用变光以及用于哪些纹理?假设
vUv
是一个变量。我可以使用
texture2D(someTex,mod(uVu,0.1))
或者
texture2D(someTex,uVu*40.0)
或者
texture2D(someTex,somethercomplexfunc(uVu))
,所以我很想知道通过varying是如何帮助提高速度的。对于简单的情况,是否有一些复杂的依赖项检查?@JanTosovsky您已经复制了包含javascript下方着色器的脚本标记,因此在执行脚本时它们不在DOM中。。。