OpenGL图像数据操作

  之前的实践中,我们在着色器中的输入输出都是比较固定的。比如在顶点或片元着色器中,顶点属性的输入和帧缓存的颜色值;虽然我们可以通过纹理或者纹理缓存对象(TBO)来读取任意的内存区域,不过总体上来说写入的时机是固定的,也是可以预知的。比如我们在固定的阶段通过transform feedback操作来获取顶点数据并传递到transform feedback缓存中,也可以根据光栅化阶段的标准样式,将片元着色器产生的像素写入帧缓存中。

  在这次实践中我们将使用一种允许用户在指定的位置进行读写操作的机制,着色器可以因此在内存中建立数据结构,然后谨慎地更新同一块内存位置,完成彼此之间的各级通信过程。首先我们介绍使用纹理存储通用数据。

使用纹理存储通用数据

  我们可以使用内存来表示一个缓存对象,或则一个单一层级的纹理对象,并且在着色器中进行通用目的的读写操作。在OpenGL中提供一些特别的图像类型来支持这一需求,它们主要用来表达未编码的图像数据。图像类型和采样器(sampler)非常的类似,比如采样器类型sampler2D,对应的图像类型image2D或iimage2D,在着色器中也是使用uniform定义,但它们并不相同:首先,图像类型表达的是单一层级的纹理,不是完整的mipmap;其次,图像类型不支持滤波等采样操作。注意不支持的操作还包括深度比较(depth comparison),所以阴影类型的采样器并没有对应的图像类型。下面我们列了一些常用的图像类型:

图像类型 意义
image1D 1D浮点数类型
image2D 2D浮点数类型
image3D 3D浮点数类型
imageCube 浮点数Cube map数组类型

imageBuffer

浮点数缓存类型

iimage1D

1D有符号整数类型
iimage2D 2D有符号整数类型
iimage3D 3D有符号整数类型
iimageBuffer 有符号整数缓存类型
uimage1D 1D无符号整数类型
uimage2D 2D无符号整数类型
uimage3D 3D无符号整数类型
uimageCube 无符号整数Cube map数组类型
uimageBuffer 无符号缓存类型

  这些图像类型定义的是通用的数据类型,我们还需要一个format的限定符来设置数据在内存中的图像格式。下表中将列举常用的一些图像格式限定符。

图像类型 OpenGL内部格式
rgba32f GL_RGBA32F
rgba16f GL_RGBA16F
rg32f GL_RG32F
rg16f GL_RG16F
r32f GL_R32F
r16f GL_R16F
rgba32i GL_RGBA32I
rgba16i GL_RGBA16I
rgba8i GL_RGBA8I
rgba32ui GL_RGBA32UI
rgba16ui GL_RGBA16UI
rgba8ui GL_RGBA8UI

  图像的format限定符是作为图像变量声明的一部分提供的,并且必须在声明一个用来读取图像的变量的时候使用。如果图像变量只用来写入,那么我们也可以忽略这个限定符。注意,我们在使用图像限定符的时候一定要和图像本身的基本数据类型相匹配。比如,image2D必须使用浮点类型的限定符,如r32f或者rgba16_unorm,而非浮点型的限定符rg8ui是不行的。图像类型在着色器中的声明示例如下:

layout(binding=,rgba32f) uniform image2D image1;

  其中bingding表示图像单元的索引,类似采样器的位置(如GL_TEXTURE0),也可以使用glUniform1i来设置,默认为0,如果只使用一个图像就不需要显示使用glUniform1i设置。rgba32f即图像的format。

  下面的例子我们将实现在glsl中存取图像数据:

static const GLfloat cData[] = {
1.0,0.0,0.0,1.0, 0.0,1.0,0.0,1.0,
0.0,0.0,1.0,1.0, 1.0,1.0,0.0,1.0
};
glGenTextures(,&tex);
glBindTexture(GL_TEXTURE_2D,tex);
glTexStorage2D(GL_TEXTURE_2D,,GL_RGBA32F,,);
glTexSubImage2D(GL_TEXTURE_2D,
,
,,
,,
GL_RGBA,GL_FLOAT,
cData); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); //输出图像
glGenTextures(,&otex);
glBindTexture(GL_TEXTURE_2D,otex);
glTexStorage2D(GL_TEXTURE_2D,,GL_RGBA32F,,);
glBindTexture(GL_TEXTURE_2D,); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glBindTexture(GL_TEXTURE_2D,);

  首先生成两张图片,一张用于输入,一张用于输出。生成的过程于普通的纹理生成过程一样,需要注意的是纹理的格式,即format,在这里,我们使用的是rgba32f,这个要于着色其中的格式相对应。

  接下来是使用这两张纹理:

         glClear(GL_COLOR_BUFFER_BIT);

         dShader->Use();
glBindImageTexture(,tex,,GL_FALSE,,GL_READ_ONLY,GL_RGBA32F);
glBindImageTexture(,otex,,GL_FALSE,,GL_WRITE_ONLY,GL_RGBA32F);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES,,);
//glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); tShader->Use();
glBindTexture(GL_TEXTURE_2D,otex);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES,,);

  重点是glBindImageTexture这个函数,函数原型:

  void glBindImageTexture(GLuint unit,GLuint texture,GLint level, GLboolear layered,GLint layer, GLenum access, GLenum format);

  函数意义是将level层的纹理绑定到unit的图像单元。如果texture是1D或者2D的纹理数组,且layered为GL_TRUE,则绑定整个数组,而layered为GL_FALSE则只绑定layer层。access可以有三种选择:GL_READ_ONLY,GL_WRITE_ONLY,GL_READ_WRITE,表示的是对图像的访问方式,最后一个format即图像数据在内存中的格式,于texture声明的格式要兼容。

  所以上述代码的表示将tex与0图像单元绑定,用于读取数据,otex与1图像单元绑定,用于写入数据,最后再用otex渲染。

  下面为顶点和片元着色器代码:

#version  core

layout(location=) in vec3 iPos;
layout(location=) in vec2 iTexcoord; uniform mat4 model;
uniform mat4 view;
uniform mat4 proj; out vec2 texcoords; void main()
{
texcoords = iTexcoord;
gl_Position = proj * view * model * vec4(iPos,);
}
#version  core

layout(binding=,rgba32f) uniform image2D colors;
layout(binding=,rgba32f) uniform image2D output_buffer; //uniform sampler2D image; in vec2 texcoords;
out vec4 color; void main()
{
//ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = imageSize(output_buffer);
vec4 col = imageLoad(colors,ivec2(,));//ivec2(gl_FragCoord.xy));
imageStore(output_buffer,ivec2(size.x * texcoords.x, size.y * texcoords.y),vec4(texcoords,,));
//imageStore(output_buffer,ivec2(texcoords),vec4(1,1,0,1));
color = col;//vec4(1,0,0,1);
//color = texture(image,texcoords);
}

  顶点着色器没什么变化,在片元着色器中需要对于图像单元的进行读取和写入。在着色器中对图像进行操作需要借助着色器的内置函数:imageLoad,imageStore,imageSize。这三个函数有很多重载,分别对应不同的图像,比如我们这次针对image2D使用的:

  gvec4 imageLoad(readonly gimage2D image,ivec2 P);

  gvec4 imageStore(writeonly gimage2D image,ivec2 P, gvec4 data);

  ivec2 imageSize(gimage2D image);

  imageLoad用于从图像中读取数据,imageStore则是写,imageSize则返回图像的大小。需要注意的是P的类型是ivec2,表示坐标位置,是整形的,于我们常用的vec2浮点型不一样,这个是像素的实际位置,没有进行归一化,即表示第P.x行,P.y列的数据。在本例中我们使用imageSize和顶点的纹理坐标(已归一化)来定位。

  效果如图,背后的绿色物体使用从图像的(1,0)位置(整个它图像为2*2的,(1,0)位置为绿色)取的的数据,用于片元着色器输出,中间的矩形是使用着色器写入数据图像的图像渲染的,我们写入的数据是纹理坐标。

  

  

  源代码:https://github.com/xin-lover/opengl-learn/tree/master/chapter-15-memory

Linux OpenGL 实践篇-15-图像数据操作的更多相关文章

  1. Linux OpenGL 实践篇-10-framebuffer

    在之前的实践中我们都是在当前的窗口中渲染,即使用的缓存都是由glutCreateWindow时创建的缓存,我们可称之为默认缓存.它是唯一一个可以被图形服务器的显示系统识别的帧缓存,我们在屏幕上看到的只 ...

  2. Linux OpenGL 实践篇-5 纹理

    纹理 在之前的实践中,我们所渲染的物体的表面颜色都是纯色或者根据顶点位置计算出的一个颜色,这种方式在表现物体细节方面是比较吃资源的,因为我们每增加一个细节,我们就需要定义更多的顶点及其属性.所以美术人 ...

  3. Linux OpenGL 实践篇-3 绘制三角形

    本次实践是绘制两个三角形,重点理解顶点数组对象和OpenGL缓存的使用. 顶点数组对象 顶点数组对象负责管理一组顶点属性,顶点属性包括位置.法线.纹理坐标等. OpenGL缓存 OpenGL缓存实质上 ...

  4. Linux OpenGL 实践篇-16 文本绘制

    文本绘制 本文主要射击Freetype的入门理解和在OpenGL中实现文字的渲染. freetype freetype的官网,本文大部分内容参考https://www.freetype.org/fre ...

  5. Linux OpenGL 实践篇-6 光照

    经典光照模型 经典光照模型通过单独计算光源成分得到综合光照效果,然后添加到物体表面特定点,这些成分包括:环境光.漫反射光.镜面光. 环境光:是指不是来特定方向的光,在经典光照模型中基本是个常量. 漫反 ...

  6. Linux OpenGL 实践篇-14-多实例渲染

    多实例渲染 OpenGL的多实例渲染是一种连续执行多条相同的渲染命令的方法,并且每条命令产生的结果都有轻微的差异,通常用于渲染大量的几何物体. 设想一个场景,比如太空,我们需要渲染数以万记的星球,如果 ...

  7. Linux OpenGL 实践篇-12-procedural-texturing

    程序式纹理 简单的来说程序式纹理就是用数学公式描述物体表面的纹路 .而实现这个过程的着色器我们称之为程序纹理着色器,通常在这类着色器中我们能使用的输入信息也就是顶点坐标和纹理坐标. 程序式纹理的优点 ...

  8. Linux OpenGL 实践篇-11-shadow

    OpenGL 阴影 在三维场景中,为了使场景看起来更加的真实,通常需要为其添加阴影,OpenGL可以使用很多种技术实现阴影,其中有一种非常经典的实现是使用一种叫阴影贴图的实现,在本节中我们将使用阴影贴 ...

  9. Linux OpenGL 实践篇-9 模型

    之前一直渲染箱子,显得有点单调.这一次我们绘制一个用艺术家事先用建模工具创建的模型. 本次实践参考:https://learnopengl-cn.github.io/03%20Model%20Load ...

随机推荐

  1. C# 外界调用方法是 方法名是string类型的解决方法

  2. CCF 201604-2 俄罗斯方块 (模拟)

    问题描述 俄罗斯方块是俄罗斯人阿列克谢·帕基特诺夫发明的一款休闲游戏. 游戏在一个15行10列的方格图上进行,方格图上的每一个格子可能已经放置了方块,或者没有放置方块.每一轮,都会有一个新的由4个小方 ...

  3. 挨踢职场求生法则-----我在IT职场打滚超过15年了,从小小的程序员做到常务副总

    摘要我在IT职场打滚超过15年了,从小小的程序员做到常务副总.相对于其它行业,IT职场应该算比较光明的了,但也陷阱重重,本文说说我的亲身体会,希望大家能在IT职场上战无不胜! 通用法则 法则1:忍耐是 ...

  4. 《剑指offer》面试题16—反转链表

    Node* p1  p2  p3 思路:开始时,p1为NULL,p2=phead,p3=p2—>next.使p2—>next = p1,然后使p1=p2,p2=p3.如果只有1个结点则此时 ...

  5. IOS实时监控上传下载速度

    在开发中要获取网络类型是很简单的,导入Reachability直接获取状态就行了,现在我们要做一个类似下载器的那种实时把上传下载速度显示出来. 需要用到的头文件 使用Reachability 要测速度 ...

  6. 如何解决启动Error:com.intellij.util.indexing.StorageException问题?

    启动tomcat时idea出现如下错误: Error:com.intellij.util.indexing.StorageException: com.intellij.util.indexing.S ...

  7. JDK1.7 和 jetty配置教程

    系统是windows 7 64位版本,32位版本同理,xp系统的自己google设置环境变量 打开设置环境变量窗口,右键计算机->我的电脑,选择属性 点击高级系统设置 选择环境变量 红线为需要设 ...

  8. Luogu P4892 GodFly的寻宝之旅【状压dp】By cellur925

    题目传送门 又是一道状压+计数类好题hh(真香).数据范围非常友好,告诉我们\(n<=18\),非常符合状压的性质. 其实感觉和\(Hamilton\)路径那题还是有些相似的,我们可以类似地设计 ...

  9. Hexo搭建博客教程(2) - 博客的简单个性化配置

    本章主要讲博客的个性化,譬如站点的基本配置(语言.头像.站点图标等).安装新的Hexo主题(NexT主题)以及主题的配置. 1. 修改站点配置 打开站点配置文件 ,找到: # Site title: ...

  10. iOS蓝牙连接流程介绍-1

    蓝牙连接流程介绍 1.1-程序员找女朋友流程介绍 0.程序员找女朋友参与者 1.你 2.受害者(女性同胞)  (1)她的性格1 性格的特点 (2)她的性格2  分析性格的特点 1.寻找女性 寻尽身边一 ...