HTML 3D 画布的图形过滤器





5.00/5 (2投票s)
解释了 HTML 3D 画布的图形滤镜,以及它们在我公共领域的 HTML 3D 库中的工作方式。
- 在 HTML 3D 库的发布页面下载最新版本的库
引言
此页面描述了什么是图形滤镜以及它们在我公共领域的 HTML 3D 库中是如何工作的。它还描述了几个图形滤镜的例子。
图形滤镜
在 HTML 3D 库中,图形滤镜是用于在每一帧之后修改屏幕外观的函数。它们使用一种名为 GLSL(GL 着色语言)的语言实现。GLSL 程序被称为“着色器”,它们被编译成在 GPU(图形处理单元)上运行的代码。
图形滤镜被认为是“片段着色器”,即一次处理一个像素的着色器。GPU 可以非常快速地运行着色器,因为片段着色器可以并行处理每个像素,而不会影响其他像素,而且 GPU 通常比 CPU 更适合并行处理。
为了使图形滤镜工作,3D 场景必须渲染到一个名为帧缓冲区的离屏缓冲区。帧缓冲区就像一个 3D 纹理,它将在图形滤镜的着色器程序的帮助下渲染回屏幕。
编写图形滤镜
在 HTML 3D 库中,使用 ShaderProgram
类的 makeEffect
方法创建图形滤镜
ShaderProgram
类保存着色器程序的数据。每个着色器程序由一个顶点着色器和一个片段着色器组成。图形滤镜本质上是片段着色器的一部分,因此处理像素。(顶点着色器,它处理三角形、线条和点的顶点,在此页面中不讨论。)makeEffect
方法创建一个着色器程序并编译它,使用图形滤镜作为程序片段着色器的一部分。由于着色器程序也必须有一个顶点着色器,因此该方法还为图形滤镜添加了一个非常基本的顶点着色器。
以下是一个图形滤镜的示例。
return ShaderProgram.makeEffect(context,[
"vec4 textureEffect(sampler2D sampler, vec2 uvCoord, vec2 textureSize){",
// Read the current color from the sampler texture
" vec4 color=texture2D(sampler,uvCoord);",
// Convert the color to a shade of gray. It gets
// the current color's red, green, and blue components,
// adds them, and divides by 3. Thus, the gray color
// will be an average of the red/green/blue components.
" float gray=(color.r+color.g+color.b)/3.0;",
// Return the gray color (using the color's original alpha)
" return vec4(gray,gray,gray,color.a);",
"}"].join("\n"));
每个图形滤镜都必须有一个名为 textureEffect()
的 GLSL 函数,如上面的示例所示。textureEffect
函数接受这些参数
sampler2D sampler
:指向表示当前帧屏幕截图的纹理。要从纹理读取,请使用texture2D
函数,如上面的示例所示。vec2 uvCoord
:当前像素的纹理坐标。uvCoord.x
从左侧的 0 到右侧的 1。uvCoord.y
从底部的 0 到顶部的 1。(请注意,纹理坐标从左下角开始,而不是左上角,也就是说,纹理是“自下而上”的,而不是“自上而下”的)。vec2 textureSize
:由sampler
指向的屏幕截图的大小(以像素为单位)。textureSize.x
是宽度,textureSize.y
是高度。
textureEffect
函数返回一个 vec4
(4 元素向量),给出当前像素应有的颜色。上面的示例读取当前像素的颜色,将其转换为灰色调,并返回带有该灰色调的新颜色。因此,滤镜将把屏幕转换为灰度色调。
着色器还可以定义名为“uniforms
”的自定义参数。它们通过在着色器顶部使用 uniform [type] [name];
这样的行来声明。例如:uniform float time;
一旦声明,Uniforms 就可以在 textureEffect
函数中使用。
GLSL 的详细处理超出了本页的范围。可以通过搜索 Web 找到有关 GLSL 的更多信息;请注意,GLSL 有许多版本,用于 HTML 应用程序的版本在如今相对基础。另外,请参阅下面的更多图形滤镜示例。
使用图形滤镜
滤镜创建后,使用起来非常简单;只需使用 Scene3D
的 useFilter
方法进行设置。滤镜设置后,其工作方式如下
- 当调用
useFilter
方法设置新的图形滤镜时,HTML 3D 库会创建一个帧缓冲区。帧缓冲区由与 3D 画布大小相同的纹理和一个用作深度缓冲区的renderbuffer
组成。 - 当每帧调用
render()
方法时- 库确保帧缓冲区与 3D 画布大小相同。
- 3D 库将绘图切换为使用帧缓冲区而不是 3D 画布,然后将着色器切换为用于绘制 3D 场景的常规着色器。
- 当前帧渲染到帧缓冲区。帧缓冲区的纹理现在将包含帧的“快照”,可以由图形滤镜修改。
- 3D 库将绘图切换回 3D 画布,然后将着色器切换到图形滤镜的着色器。
- 绘制一个占据整个 3D 画布的矩形。这是为了让纹理的每个像素都传递给图形滤镜,并且为每个像素调用滤镜的
textureEffect
方法。在绘图之前,将设置提供给图形滤镜的任何自定义参数或“uniforms
”。图形滤镜可以使用当前像素的颜色或更改每个像素的颜色。因此,将绘制当前帧的“过滤后
”版本。
目前,HTML 3D 库一次只支持一个滤镜,因此如果 HTML 应用程序需要使用多个效果,例如灰度化和模糊,则两个效果都需要组合在同一个图形滤镜着色器中。
示例
- squares.html - 演示图形滤镜。
演示 squares.html 包含了许多以着色器形式实现的图形滤镜。
以下是它包含的滤镜的更多详细信息。
灰度
上面已经给出了将屏幕转换为黑白的灰度滤镜。
反色
反色滤镜内置于 HTML 3D 库中。它反转屏幕的颜色,使效果看起来像胶片底片。
此滤镜在方法 ShaderProgram.getInvertEffect()
中实现
ShaderProgram.getInvertEffect=function(context){ return ShaderProgram.makeEffect(context, [ "vec4 textureEffect(sampler2D sampler, vec2 uvCoord, vec2 textureSize){", " vec4 color=texture2D(sampler,uvCoord);", " vec4 ret; ret.xyz=vec3(1.0,1.0,1.0)-color.xyz; ret.w=color.w; return ret;", "}"].join("\n")); }
红色色调
红色色调滤镜为图像添加一丝红色。
function makeRedTint(context){ return ShaderProgram.makeEffect(context,[ "vec4 textureEffect(sampler2D sampler, vec2 uvCoord, vec2 textureSize){", " vec4 color=texture2D(sampler,uvCoord);", " return vec4(color.r+0.3,color.g,color.b,color.a);", "}"].join("\n")); }
镜像滤镜
此滤镜对其像素进行水平翻转。请注意,下面给出的滤镜不是从当前像素读取,而是从与当前像素相对的另一侧的像素读取(它采用 1 减去当前 X 坐标)。
function makeMirror(context){ return ShaderProgram.makeEffect(context,[ "vec4 textureEffect(sampler2D sampler, vec2 uvCoord, vec2 textureSize){", " vec4 color=texture2D(sampler,vec2(1.0-uvCoord.x,uvCoord.y));", " return color;", "}"].join("\n")); }
通过简单的更改,此滤镜可以修改为垂直翻转(1.0-uvCoord.y
)甚至同时进行水平和垂直翻转。
矩阵滤镜
此滤镜启用了一系列图像处理滤镜,例如模糊、锐化、边缘检测和浮雕,它们处理每个像素及其邻居。此滤镜采用一个 3x3 矩阵,称为卷积核,它给出每个像素颜色对最终颜色的贡献。矩阵中的所有数字必须加起来为 1。
请注意,下面给出的 uniform
是 mat3
,表示一个 3x3 矩阵。
卷积核的一个例子
[ 0, 1/8, 0, 1/8, 1/2, 1/8, 0, 1/8, 0 ]
此滤镜意味着目标像素将具有原始像素颜色的一半,以及其 4 个相邻像素颜色的八分之一。请注意,此示例加起来为 1。
此滤镜在演示中的 makeKernelMatrix
函数中实现。它用于“模糊”和“边缘检测”效果。该滤镜展示了滤镜在实现其效果时如何读取相邻像素,而不仅仅是当前像素。
像素化滤镜
此滤镜通过缩小然后放大来像素化屏幕。此滤镜采用一个名为 coarseness
的 uniform
,表示每个“像素化”像素占据多少个正常像素。
function makePixelate(context){ return ShaderProgram.makeEffect(context,[ "uniform float coarseness;", // coarseness in pixels; 1 means normal "vec4 textureEffect(sampler2D sampler, vec2 uvCoord, vec2 textureSize){", " float g=max(coarseness,1.0);", " float gridSizeX=textureSize.x/g;", " float gridSizeY=textureSize.y/g;", " float uv0=floor(uvCoord.x*gridSizeX)/gridSizeX;", " float uv1=floor(uvCoord.y*gridSizeY)/gridSizeY;", " vec4 c=texture2D(sampler,vec2(uv0,uv1));", " return c;", "}"].join("\n")); }
演示随着时间改变“coarseness
”参数以实现像素化效果的动画。
波浪滤镜
此滤镜水平移动每行中的像素,导致屏幕垂直波动。此滤镜采用一个名为 time
的 uniform
,表示波动效果的动画帧。
此滤镜在演示中的 makeWave
函数中实现。
水彩滤镜
这是一种水彩效果,基于一个名为“Themaister
”的公共领域着色器。
此滤镜在演示中的 makeWaterpaint
函数中实现。
其他页面
我在 CodeProject 上的以下页面也讨论了此库