之前提到过OGL中纹理缓存是作为输入缓存存在的,这使得输入缓存能够读取但是不能够改写。为了突破这一限制,在OGL中提出了Image的概念,这使得程序员能够有效的操作texture缓存——对背后的texture缓存进行读写操作。但是,这样的操作打破了原有的pipeline流水线,使得原本应该被OGL自身进行管理的缓存需要程序员自身来进行处理,因此提供了方便的同时也增加了程序员的编程负担。

为了对texture缓存进行写操作,需要将对应的纹理数据给绑定到相应的image uint中,需要注意的是image uint和texture uint是两个不同的区域,因此两者的ID号码计数是分离开的。由于image图像是和texture纹理绑定的,因此image的类型和texture的图像类型类似,其次每一个image里都有一个对应的存储类型,这个存储类型和image在内存中的分割以及图像像素的格式是相关的。

为了加载image中的数据,需要利用imageLoad内置函数,该函数需要两个参数,对应的输入图像以及相应的坐标信息,该函数会读取对应坐标处的图像的像素值。反过来,imageStore函数则会将相应的数据存入到对应的图像的对应的坐标位置处。下面是一个利用image进行存储和读取的实例。

首先创建两个纹理缓存,其中image_palette_buffer作为背后的缓存提供存储支持,该函数会分配相应的内存,但是并不进行数据的初始化,同时也不会将这个纹理缓存绑定到对应的texture uint中,真正被绑定到OGL管理的纹理缓存的数据是image_palette_texture。

    glGenBuffers(1, &image_palette_buffer);//创建调色板缓存(提供内存)glBindBuffer(GL_TEXTURE_BUFFER, image_palette_buffer);glBufferData(GL_TEXTURE_BUFFER, 256 * 4 * sizeof(float), NULL, GL_STATIC_DRAW);glGenTextures(1, &image_palette_texture);//创建调色板纹理,用于和片元着色器交互glBindTexture(GL_TEXTURE_BUFFER, image_palette_texture);glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, image_palette_buffer);vmath::vec4 * data = (vmath::vec4 *)glMapBuffer(GL_TEXTURE_BUFFER, GL_WRITE_ONLY);for (int i = 0; i < 256; i++){data[i] = vmath::vec4((float)i);}glUnmapBuffer(GL_TEXTURE_BUFFER);

首先调用纹理激活函数切换纹理缓存,同时创建新的纹理对象output_texture,这个对象作为输出图像来接收输出的数据。同时设置对应的纹理参数,紧接着将其绑定到第0号图像单元上。注意这里的第0号图像单元和第0号纹理单元不是同一个对象。glBindImageTexture函数的第一个参数用于指定image单元的索引号,第二个参数用于指定纹理的ID号,第三个参数用于指定纹理的层次,倒数第二个参数用于指定图像的读写属性,最后一个则指定图像的存储格式,这里是每一个像素的颜色通道是32位的浮点数,每一个像素包括四个颜色通道RGB以及透明度。

    glActiveTexture(GL_TEXTURE0);glGenTextures(1, &output_texture);//2D_TEXTURE,用作输出缓存glBindTexture(GL_TEXTURE_2D, output_texture);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, MAX_FRAMEBUFFER_WIDTH, MAX_FRAMEBUFFER_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);glBindTexture(GL_TEXTURE_2D, 0);glBindImageTexture(0, output_texture, 0, GL_TRUE, 0, GL_READ_WRITE, GL_RGBA32F);

为了更方便快捷的清除texture缓存中的数据,引入一个新的概念pixel pack object,该对象管理一个像素相关的缓存区域,其解压的端口和纹理缓存或者frame缓存关联,反之当frame缓存和纹理缓存进行打包处理时,则会传递数据到pack object。下面的例子中创建一个PIXEL_UNPACK_BUFFER在稍后的处理中将这个缓存中的数据可以上传到纹理缓存中,达到清除纹理缓存中数据的效果。

    glGenBuffers(1, &output_texture_clear_buffer);//解压出来的数据会提交给texture bufferglBindBuffer(GL_PIXEL_UNPACK_BUFFER, output_texture_clear_buffer);glBufferData(GL_PIXEL_UNPACK_BUFFER, MAX_FRAMEBUFFER_WIDTH * MAX_FRAMEBUFFER_HEIGHT * sizeof(GLuint), NULL, GL_STATIC_DRAW);data = (vmath::vec4 *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);memset(data, 0x00, MAX_FRAMEBUFFER_WIDTH * MAX_FRAMEBUFFER_HEIGHT * sizeof(GLuint));glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);

利用图像进行纹理写操作,首先将调色板纹理绑定点到纹理缓存中,作为输入图像颜色的初始数据。同时将输出缓存数据进行清空,主要是利用之前准备好的pack object实现。

    //将调色板纹理和图像绑定作为输入颜色的初始数据glBindImageTexture(0, image_palette_texture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F);//将输出缓存清零glBindBuffer(GL_PIXEL_UNPACK_BUFFER, output_texture_clear_buffer);glBindTexture(GL_TEXTURE_2D, output_texture);glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, current_width, current_height, GL_RGBA, GL_FLOAT, NULL);glBindTexture(GL_TEXTURE_2D, 0);//绑定输出缓存作为输出,glBindImageTexture(1, output_texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F);

在这里需要注意的是glBindImageTexture函数的第一个参数分别是0和1,和glsl语言中对应的图像对应。GLSL语言中利用binding指定绑定的点分别是0和1。在GLSL程序中,首先从给定的颜色纹理中读取颜色值,读取的索引根据给定的图元索引计算得到。第二步则将读取到的颜色值写到2D纹理中,这个纹理和output_texture绑定。GLSL程序中利用imageStore内置函数将颜色值写入到纹理缓存中。在稍后的处理中,只需要绑定output_texture就可以将其作为输入,同时利用output_texture进行片元。

#version 430 corelayout (binding = 0, rgba32f) uniform imageBuffer colors;//和texture buffer关联layout (binding = 1, rgba32f) uniform image2D output_buffer;//2D_textureout vec4 color;void main(void)
{//从一个图像缓存中读取数据,数据的索引根据图元的ID号码决定vec4 col = imageLoad(colors, gl_PrimitiveID & 255);imageStore(output_buffer, ivec2(gl_FragCoord.xy) - ivec2(200, 0), col);imageStore(output_buffer, ivec2(gl_FragCoord.xy) + ivec2(200, 0), col);
}

由于image的出现打破了pipeline的处理流程,因此需要程序员自己进行内存访问正确性的保证。OGL也提供相应的函数来保证对内存进行互斥访问,从而保证访问内存的正确性。主要是利用类似于OS层次的原子操作了外接的函数对这样的。比如利用imageAtomicAdd(output_buffer, ivec2(gl_FragCoord.xy), 1);函数进行原子加操作。

image最主要的一个应用层场景是可以创建OIT(顺序无关透明性),其主要实现原理是利用image缓存获取texture的中间输出,然后将其数据暂存起来,并在适当的时候进行排序,从而保证最终的渲染结果与原始的顺序无关。在红宝书上提供了一种顺序无关透明性的实现,其主要思想是利用链表暂存图像和当前像素相重叠的像素值,然后在需要渲染的时候将其进行排序处理从而保证每次渲染都与顺序无关。GLSL中第一步开启片元测试用于清除无效的片元数据,并且初始化原子计数器的数值为0。在GLSL中首先利用原子操作累加计数器的数值,并将其放入到链表的头指针中,链表是以数组的形式存在于图像缓存中。利用exchange操作返回原来保存的数据,从而可以得到上一个保存的索引数值。接下来利用光照位置以及顶点位置计算关照对像素颜色的影响。同时将计算得到的数据存储在图像list_buffer中,存放格式是x存放上下一个指针的索引,y则存放颜色信息,z则存放当前索引的深度数值,而w则存放一些

#version 420 corelayout (early_fragment_tests) in;layout (binding = 0, r32ui) uniform uimage2D head_pointer_image;
layout (binding = 1, rgba32ui) uniform writeonly uimageBuffer list_buffer;layout (binding = 0, offset = 0) uniform atomic_uint list_counter;layout (location = 0) out vec4 color;in vec3 frag_position;
in vec3 frag_normal;
in vec4 surface_color;uniform vec3 light_position = vec3(40.0, 20.0, 100.0);void main(void)
{uint index;uint old_head;uvec4 item;vec4 frag_color;index = atomicCounterIncrement(list_counter);old_head = imageAtomicExchange(head_pointer_image, ivec2(gl_FragCoord.xy), uint(index));vec3 L = normalize(light_position - frag_position);vec3 V = normalize(-frag_position);vec3 N = normalize(frag_normal);vec3 H = normalize(L + V);float NdotL = dot(N, L);float NdotH = dot(N, H);vec4 modulator = vec4(surface_color.rgb * abs(NdotL), surface_color.a);vec4 additive_component = mix(surface_color, vec4(1.0), 0.6) * vec4(pow(clamp(NdotH, 0.0, 1.0), 26.0)) * 0.7;item.x = old_head;item.y = packUnorm4x8(modulator);item.z = floatBitsToUint(gl_FragCoord.z);item.w = packUnorm4x8(additive_component);imageStore(list_buffer, int(index), item);frag_color = modulator;color = frag_color;
}

在真正渲染的时候根据上面得到的链表利用存储的深度数值进行排序,从而有有效的引导颜色的组合,保证了最终的渲染结果与混合的结果无关。

#version 420 corelayout (binding = 0, r32ui) uniform uimage2D head_pointer_image;
layout (binding = 1, rgba32ui) uniform uimageBuffer list_buffer;
layout (location = 0) out vec4 color;
#define MAX_FRAGMENTS 40
uvec4 fragment_list[MAX_FRAGMENTS];void main(void)
{uint current_index;uint fragment_count = 0;current_index = imageLoad(head_pointer_image, ivec2(gl_FragCoord).xy).x;while (current_index != 0 && fragment_count < MAX_FRAGMENTS){uvec4 fragment = imageLoad(list_buffer, int(current_index));fragment_list[fragment_count] = fragment;current_index = fragment.x;fragment_count++;}uint i, j;if (fragment_count > 1){for (i = 0; i < fragment_count - 1; i++){for (j = i + 1; j < fragment_count; j++){uvec4 fragment1 = fragment_list[i];uvec4 fragment2 = fragment_list[j];float depth1 = uintBitsToFloat(fragment1.z);float depth2 = uintBitsToFloat(fragment2.z);if (depth1 < depth2){//根据深度从大到小排序片元fragment_list[i] = fragment2;fragment_list[j] = fragment1;}}}}vec4 final_color = vec4(0.0);for (i = 0; i < fragment_count; i++){vec4 modulator = unpackUnorm4x8(fragment_list[i].y);vec4 additive_component = unpackUnorm4x8(fragment_list[i].w);final_color = mix(final_color, modulator, modulator.a) + additive_component;}color = final_color;
}

OpenGL 图像的加载和存储相关推荐

  1. OpenCV图像的加载、显示

    OpenCV图像的加载.显示 Mat类是OpenCV里使用广泛的一个类,使用它可以轻松的用几行代码实现图像的加载.显示. 先上代码: // OpenCV_self.cpp : 此文件包含 " ...

  2. Python之pandas数据加载、存储

    Python之pandas数据加载.存储 0. 输入与输出大致可分为三类: 0.1 读取文本文件和其他更好效的磁盘存储格式 2.2 使用数据库中的数据 0.3 利用Web API操作网络资源 1. 读 ...

  3. 单寄存器加载与存储指令

    这种指令用于把单一的数  传入或者传出一个寄存器.支持的数据类型有字(32 位 ) .半字(16 位)  和字节.常用的单寄存器加载与存储指令包括: LDR/STR            字数据加载/ ...

  4. OpenGL通过Assimp加载模型

    OpenGL通过Assimp加载模型 OpenGL通过Assimp加载模型简介 源代码剖析 主要源代码 OpenGL通过Assimp加载模型简介 到目前为止,我们已经使用了手动创建的模型.如您所见,为 ...

  5. python观察日志(part28)--数据的加载与存储

    学习笔记,仅供参考,有错必究 参考文献:编码问题:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb3 in position; 数据的加载 ...

  6. glide 加载webp_如何使您的网站通过WebP图像快速加载闪电

    glide 加载webp by Carmen Chung 通过钟Car 如何使您的网站通过WebP图像快速加载闪电 (How to make your website load lightning f ...

  7. xp系统打开计算机配置文件,浅析xp系统如何解决开机出现“Windows不能加载本地存储的配置文件“...

    对于打开电脑出现"Windows不能加载本地存储的配置文件"的问题,之前小编有跟大家分享通过用户配置文件设置来解决这个问题.可是有部分xp系统用户反馈设置完之后问题还是没有得到解决 ...

  8. 《利用Python进行数据分析·第2版》第6章 数据加载、存储与文件格式

    第1章 准备工作 第2章 Python语法基础,IPython和Jupyter 第3章 Python的数据结构.函数和文件 第4章 NumPy基础:数组和矢量计算 第5章 pandas入门 第6章 数 ...

  9. [OpenCV学习笔记3][图像的加载+修改+显示+保存]

    正式进入OpenCV学习了,前面开始的都是一些环境搭建和准备工作,对一些数据结构的认识主要是Mat类的认识: [1.学习目标] 图像的加载:imread() 图像的修改:cvtColor() 图像的显 ...

最新文章

  1. Netty笔记(八) Bootstrap
  2. dataTable表头未对其解决方法
  3. 【BZOJ】3456: 城市规划 动态规划+多项式求逆
  4. 三星 Nexus S刷MIUI ROM最新图文刷机教程
  5. php常用操作数组函数,PHP自带的几个实用的数组函数
  6. C++判断一个数是否为armstrong number阿姆斯特朗数(附完整源码)
  7. js 如何获取class的元素 以及创建方法getElementsByClassName
  8. 【JS 逆向百例】W店UA,OB反混淆,抓包替换CORS跨域错误分析
  9. LeetCode之Binary Tree Level Order Traversal 层序遍历二叉树
  10. 查询linux磁盘剩余空间脚本,linux磁盘空间报警脚本
  11. RemoteDebug iOS Webkit Adapter(适配器):一个可以让你(随时)随地调试Safari、 iOS WebView(的适配器)
  12. apache java cache-control,Tomcat: Cache-Control
  13. 黑客入侵 - 认识黑客入侵的利器 嗅探软件
  14. java物业管理系统描述,基于java小区物业管理系统.doc
  15. 电机正反转的远程计算机控制,plc控制电机正反转原理图
  16. shell脚本快速执行命令
  17. Ubuntu20.04安装Steam报错及解决
  18. 2022双十一最亮投影仪推荐,当贝X3激光投影3200ANSI流明超高亮度
  19. 七夕表白攻略:教你用自己的专业说情话,成功率100%,我只能帮你们到这里了啊~(程序员系列)
  20. winsxs探索之组件的本质:文件与注册表

热门文章

  1. adb 命令拉起 apk 并传递参数及相关注意事项
  2. java.awt.Font.createFont 画图的时候字体不显示问题
  3. 亚马逊、Lazada、阿里国际、eBay、Temu、Ozon好消息不断,机会来了
  4. android开发 android.view.View.OnClickListener和android.content.DialogInterface.OnClickListener冲突...
  5. PLC编程入门-01基础知识介绍
  6. 五行代码实现 炫动滑动 卡片层叠布局,仿探探、人人影视订阅界面 简单优雅:LayoutManager+ItemTouchHelper
  7. 时代超群交流伺服驱动器设置
  8. python sdklive2d_用Cubism 2制作自己的Live2D(尝试向)——android sdk样本的下载与Android studio编译!...
  9. java获取服务器的cpu和内存使用率
  10. php缓存输出压缩,PHP_控制PHP的输出:缓存并压缩动态页面,mod_gzip是一个Apache模块,其功 - phpStudy...