在webgl的使用过程中,我们通常会想对texture进行多级处理并对其贴在表面显示

如对较精准的边缘检测,要先后使用灰度shader、模糊shader、边缘shader来进行处理,而每次的处理对象则是上一次处理后的texture,这就要对处理后的结果进行覆盖保存。

这是我在做Polyer使用到的:http://zhiyishou.github.io/Polyer

在众多webgl库中,直接有选项rederToTarget来实现将shader处理后的texture渲染并覆盖原texture,其是怎么完成这个步骤的呢?

这就要引出本文的主角——FrameBuffer

FrameBuffer是什么


FBO(Frame Buffer Object)是被推荐用于将数据渲染到纹理对象的扩展。

FrameBuffer就像是一个webgl显示容器一样,平时我们使用gl.drawArrays或者gl.drawElements都是将对象绘制在了默认的窗口中,而当我们指定一个FrameBuffer为当前窗口时,则用这两个方法去绘制,则会将对象绘制于指定的FrameBuffer中。

FrameBuffer的使用


internalformat, int x, int y, sizei width,
sizei height, int border);
target: TEXTURE_2D, TEXTURE_

FBO的创建:

//创建一个Framebuffer
var fb = gl.createFramebuffer();
//将fb绑定为目前的窗口
gl.bindFramebuffer(gl.FRAMEBUFFER,fb);

这样,我们则创建了一个新的可以绘制的buffer了,且其并不会被显示出来

但是,这样就可以了吗?我们想到的是将经过shader渲染后的texture渲染出来并交给下一个shader,这时则引入方法framebufferTexture2D

Reference from《OpenGL ES Reference Pages about FramebufferTexture2D》:

To render directly into a texture image, a specified image from a texture object can be attached as one of the logical buffers of the currently bound framebuffer object by calling the command

为了直接渲染至纹理图片中,一个纹理对象中指定的图片可用下面的方法绑定在当前使用的FBO上一个逻辑缓存中

void FramebufferTexture2D( enum target, enum attachment, enum textarget, uint texture, int level );

 

target:

• FRAMEBUFFER

attachment:

• If attachment is COLOR_ATTACHMENT0, then image must have a colorrenderable internal format.(色彩)

• If attachment is DEPTH_ATTACHMENT, then image must have a depthrenderable internal format.(深度)

• If attachment is STENCIL_ATTACHMENT, then image must have a stencilrenderable internal format.(模板)

textarget:

• TEXTURE_2D    (two-dimensional texture)

• TEXTURE_CUBE_MAP_POSITIVE_X  (three-dimensional +x texture)

• TEXTURE_CUBE_MAP_POSITIVE_Y  (three-dimensional +y texture)

• TEXTURE_CUBE_MAP_POSITIVE_Z  (three-dimensional +z texture)

• TEXTURE_CUBE_MAP_NEGATIVE_X  (three-dimensional -x texture)

• TEXTURE_CUBE_MAP_NEGATIVE_Y  (three-dimensional -y texture)

• TEXTURE_CUBE_MAP_NEGATIVE_Z  (three-dimensional -z texture)

texture:

  texture object

level:

  specifies the mipmap level of the texture image to be attached to the framebuffer and must be .

我们使用这个方法来进行绑定(本文只介绍色彩的绑定,尝试和模板类似,但是有不同之处,不在此讨论)

//创建一个纹理对象
var texture = gl.createTexture();
//使用如下的设置来创建texture,这样对texture的设置可以使我们对任何尺寸的图片进行处理
gl.bindTexture(gl.TEXTURE_2D, texture);
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);

var fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER,fb);
//使用该方法将texture的颜色值与FBO进行绑定
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);

绑定后,当我们执行gl.drawArrays或gl.drawElements方法时,则会将直接渲染至目前绑定的FBO上,而FBO又与texture的色彩进行了绑定,所以绘制时则也将色彩渲染至了texture中

这样,我们则可用两个FBO来进行队列加工:

OriginalImage --> texture1

texture1 --> gray --> texture2

texture2 --> blur --> texture1

texture1 --> edge --> texture2

下面是具体实现过程

var FBOs = [],
textures = []; for(var i = 0; i < 2; i++){
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
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); var fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER,fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); //store corresponding texture and fb
textures.push(texture);
FBOs.push(fb);
} gl.bindTexture(gl.TEXTURE_2D, originalImageTexture); for(var i = 0; i < 3; i++){
switch(i){case 0:
//set gray shader to current shader program
//handle arguments to vs shader and fs shader
break;
case 1:
//set blur shader to current shader program
//handle arguments to vs shader and fs shader
break;
case 2:
//set edge shader to current shader program
//handle arguments to vs shader and fs shader
break;
} gl.bindFramebuffer(gl.FRAMEBUFFER, FBOs[i%2]);
//set the viewport fits the images size
gl.viewport(0, 0, imgWidth, imgHeight);
gl.drawArrays(....); //or gl.drawElements(....); //set the rendered texture to current texture for next frambuffer using
gl.bindTexture(gl.TEXTURE_2D, texture[i%2]);
}

完整的过程为:

originalTexture --> gray program --> set FBO1 --> draw --> FBO1 --> set texture1

texture1 --> blur program --> set FBO2 --> draw --> FBO2 --> set texture2

texture2 --> edge program --> set FBO1 --> draw --> FBO1 --> set texture1

该过程中,FBO1与texture1是进行色彩渲染绑定的,所以set FBO1后进行渲染则会直接渲染至texture1

当我们完成了整个绘制的时候,要正常显示处理后的图片,则要从FBO中跳出来:

//set FBO to null to use default framebuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, null);

FrameBuffer的其它用处


gl.readPixels

从FrameBuffer中读取像素颜色数据

Reference from 《webgl_2.0_reference_card》/OpenGL ES Reference Pages about readPixels:

Pixels in the current framebuffercan be read back into an ArrayBufferView object.

void readPixels(int x, int y, long width, long height,enum format, enum type, Object pixels)

x,y

• Specify the window coordinates of the first pixel that is read from the frame buffer. This location is the lower left corner of a rectangular block of pixels.

width,height

• Specify the dimensions of the pixel rectangle. width and height of one correspond to a single pixel.

format

• Specifies the format of the pixel data. The following symbolic values are accepted  RGBA in WebGL

type

• Specifies the data type of the pixel data. Must be UNSIGNED_BYTEin WebGL

pixels

  • Returns the pixel data.

在使用过程中,我们要先创建pixels对象来储存数据

//using ArrayBufferView to store pixels data only, Unit8Array is the best because each color data is a byte
var pixels = new Uint8Array(ImageWidth * ImageHeight * 4); gl.readPixels(0, 0, ImageWidth, ImageHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

这样,我们则可以得到整个FBO中的色彩数据

gl.CopyTexImage2D

gl.CopyTexSubImage2D

这两个函数都是用来从FBO中将数据复制至当前绑定的texture中的

CopyTexImage2D方法:

Reference from OpenGL ES Reference Pages about CopyTexImage2D:

 

copy pixels into a 2D texture image

void CopyTexImage2D(enum target, int level,enum internalformat, int x, int y, sizei width,sizei height, int border);

target:

• TEXTURE_2D

• TEXTURE_CUBE_MAP_POSITIVE_{X, Y, Z},

• TEXTURE_CUBE_MAP_NEGATIVE_{X, Y, Z}

internalformat:

• ALPHA

• LUMINANCE

• LUMINANCE_ALPHA

• RGB

• RGBA

x,y

Specify the window coordinates of the lower left corner of the rectangular region of pixels to be copied.

width

Specifies the width of the texture image. Must be 0 or 2 n + 2 ⁡ border for some integer n.

height

Specifies the height of the texture image. Must be 0 or 2 m + 2 ⁡ border for some integer m.

border

Specifies the width of the border. Must be either 0 or 1.

CopyTexSubImage2D方法:

Reference from 《OpenGL ES Reference Pages about CopyTexSubImage2D:

 

copy a two-dimensional texture subimage

void CopyTexSubImage2D(enum target, int level, int xoffset,int yoffset, int x, int y, sizei width, sizei height);

 

target:

• TEXTURE_2D

• TEXTURE_CUBE_MAP_POSITIVE_{X, Y, Z},

• TEXTURE_CUBE_MAP_NEGATIVE_{X, Y, Z}

level:

Specifies the level-of-detail number. Level 0 is the base image level. Level n is the nth mipmap reduction image.

xoffset:

Specifies a texel offset in the x direction within the texture array.

yoffset:

Specifies a texel offset in the y direction within the texture array.

x,y:

Specify the window coordinates of the lower left corner of the rectangular region of pixels to be copied.

width:

Specifies the width of the texture subimage.

height:

Specifies the height of the texture subimage.

这两个方法的不同之处相信大家已经看得出来了

CopyTexSubImage2D相对CopyTexImage2D增加了offset来改变复制区域

其最终复制区域为:[x, xoffset + width - 1]与[y, yoffset + height -1]。

而CopyTexImage2D则是比CopyTexSubImage2D多了internelformat参数来控制对像素数据复制的种类。

结语:

有了对texture灵活的操作,则我们才能做出更有趣的东西出来,而framebuffer在里面也是相当重要的一个角色。

附:

WebGL-1.0参考卡片:http://files.cnblogs.com/files/zhiyishou/webgl-reference-card-1_0.pdf

OpenGL-ES-2.0参考卡片:http://files.cnblogs.com/files/zhiyishou/OpenGL-ES-2_0-Reference-card.pdf

OpenGL-ES-2.0参考手册:https://www.khronos.org/opengles/sdk/docs/man/

The end.

WebGL中图片多级处理(FrameBuffer)的更多相关文章

  1. Html5 中获取镜像图像 - 解决 WebGL 中纹理倒置问题

    Html5 中获取镜像图像 - 解决 WebGL 中纹理倒置问题 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致& ...

  2. iOS 解决LaunchScreen中图片加载黑屏问题

    iOS 解决LaunchScreen中图片加载黑屏问题 原文: http://blog.csdn.net/chengkaizone/article/details/50478045 iOS 解决Lau ...

  3. div+css:div中图片垂直居中

    div中图片垂直居中 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> &l ...

  4. python将图片转换为Framebuffer裸数据格式(终端显示图片)

    要在ubuntu终端显示图片或者在板子的LCD显示图片,Framebuffer是一个简单易用的接口,直接写入像素信息即可. 但普通的图片带有头部信息或者编码格式不同,直接送入Framebuffer是显 ...

  5. iOS相册中图片按照时间排序

    ios相册默认是按照时间从过去到现在排列,图片顺序有正序和逆序,group可以用以下方法来选择顺序 /** @param NSIndexSet 需要获取的相册中图片范围 @param NSEnumer ...

  6. js获取页面中图片的总数

    查看效果:http://keleyi.com/keleyi/phtml/image/9.htm 下面是完整代码: <html><body><div id="ke ...

  7. 关于Android中图片大小、内存占用与drawable文件夹关系的研究与分析

    原文:关于Android中图片大小.内存占用与drawable文件夹关系的研究与分析 相关: Android drawable微技巧,你所不知道的drawable的那些细节 经常会有朋友问我这个问题: ...

  8. Android ListView滑动过程中图片显示重复错乱闪烁问题解决

    最新内容建议直接访问原文:Android ListView滑动过程中图片显示重复错乱闪烁问题解决 主要分析Android ListView滚动过程中图片显示重复.错乱.闪烁的原因及解决方法,顺带提及L ...

  9. IOS中图片拉伸技巧与方法总结(转载)

    以下内容转载自:http://my.oschina.net/u/2340880/blog/403996 IOS中图片拉伸技巧与方法总结 一.了解几个图像拉伸的函数和方法 1.直接拉伸法 简单暴力,却是 ...

随机推荐

  1. 偏最小二乘回归(PLSR)- 2 标准算法(NIPALS)

    1 NIPALS 算法 Step1:对原始数据X和Y进行中心化,得到X0和Y0.从Y0中选择一列作为u1,一般选择方差最大的那一列. 注:这是为了后面计算方便,如计算协方差时,对于标准化后的数据,其样 ...

  2. javascript获取地址栏参数

    http://gzycm520.blog.51cto.com/175220/71030方法一: <script type="text/javascript"> < ...

  3. mybatis WARN No appenders could be found for logger的解决方法

    log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.LogFactory).log4j:WARN ...

  4. 用bcdedit.exe重建bcd

    使用下面方法之前需要bcdedit.exe和bootsect.exe两个文件,bootsect.exe文件在vista和windows 7的安装光盘的boot目录下,而bcdedit.exe文件可以在 ...

  5. 稳固而窒息 jquery attr 和 Prop的区别

    通常在获取或者设置checked,selected,readonly,disabled等的时候使用prop效果更好 attr是通过setAtrribute和getAttribute来设置的,使用的是D ...

  6. 禁止用户登陆的 /bin/false和/sbin/nologin的区别

    1 区别 /bin/false是最严格的禁止login选项,一切服务都不能用. /sbin/nologin只是不允许login系统  小技巧: 查看 /etc/passwd文件,能看到各用户使用的sh ...

  7. zoj 1562 反素数 附上个人对反素数性质的证明

    反素数的定义:对于不论什么正整数,其约数个数记为.比如,假设某个正整数满足:对随意的正整 数.都有,那么称为反素数. 从反素数的定义中能够看出两个性质: (1)一个反素数的全部质因子必定是从2開始的连 ...

  8. Jackson 时间格式化,时间注解 @JsonFormat 与 @DatetimeFormat 用法、时差问题说明

    @JsonFormat 使用 我们可以有两种用法(我知道的),在对象属性上,或者在属性的 getter 方法上,如下代码所示: 增加到属性上: ... ... /**更新时间 用户可以点击更新,保存最 ...

  9. LINK : fatal error LNK1104

    今天本来想试试opencv的,于是就在自己的机子上部署一下试试,结果一直遇到这个错误:LINK : fatal error LNK1104 环境:win7 64位 vs2012 opencv 2.4. ...

  10. 从constructor中抛出exception后,constructor会返回null吗?

    刚才琢磨这个问题主要是在想,如果constructor抛出了exception,那么返回的object是什么一个情况呢?如果我这个object中有一些关键的资源没有初始化,比如说Database co ...