Texture Array

今天偶尔碰到一种叫texture array的opengl技术,因为看起来不怎么难掌握,因此就地学了学,做了点小小的东西。恩,就这样。——ZwqXin.com

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
原文地址:http://www.zwqxin.com/archives/opengl/learn-texture-array.html

Texture Array,一种opengl拓展,从名字就可以看出它是什么:存储纹理的数组。当然,数组存什么不可以?但是我真的第一次见它用来存储数组的 - - 纹理是什么?一块内存区域。(OpenGL Shading Language里作者称它为一种“抽象的复杂的数据类型”,好了,与其深究,还是把它看作存储区域吧...)我们通过什么操纵这种内存?纹理单元+纹理ID。前者就是一种object,类似FBO的容器,用来装纹理——但是一般一个纹理单元只装载一张纹理。然而,你可以随意更改一个纹理单元里面那张纹理图:通过纹理ID。可以说,一张纹理图对应一个纹理ID,这个ID在生成纹理(glGenTextures)的时候就唯一指定了,OpenGL靠它标识你载入的纹理。纹理单元(或者你干脆叫它纹理对象[texture object]好了)默认只开启一个,叫“0号纹理单元”,当然你可以用glActive(GL_TEXTUREi)来开启其他的,不多说。一个纹理单元每个瞬间只能绑定一张纹理图(glBindTexture),你不开启其他纹理单元情况下,看到你的程序里面花花绿绿的纹理,其实只不过是该纹理单元不停绑定一个纹理ID,然后解开再绑定下一个——它们不是同时发生的,只不过因为太快(一帧内完成),你的“幻觉”。

好了,小小基础讲到这里。Texture Array纹理数组的最显著特性,就是它不同于以上传统的经验:同样一个纹理单元,却可以在它身上同时绑定多张纹理图。当然不变的仍是纹理单元与纹理ID一一对应,因为这多张纹理图共享一个纹理ID。这可怎么用呢?且听我说。

首先,如何生成一个纹理数组?这跟我们平时载入纹理的过程是很相似的,更确切地说,是跟生成三维纹理的过程相似。(注意,我这里谈论二维纹理数组,Texture Array支持一维与二维纹理。)但是关键的“目标Target”要设成GL_TEXTURE_2D_ARRAY_EXT。注意这里filename是一个指向文件名数组的指针,我载入了Texnum张纹理,把其信息暂存入pImagex数组内,再用载入三维纹理的方法一一载入纹理ID,texid中。

  1. boolCMainFrame::SetTextureArray(char**filename,GLuint&texid)
  2. {
  3. boolStatus=false;
  4. AUX_RGBImageRec*pImagex[Texnum];
  5. memset(pImagex,0,sizeof(void*)*Texnum);
  6. for(inti=0;i<Texnum;i++)
  7. {
  8. pImagex[i]=auxDIBImageLoad(filename[i]);
  9. if(!pImagex[i])returnfalse;
  10. }
  11. Status=true;
  12. glGenTextures(1,&texid);
  13. glBindTexture(GL_TEXTURE_2D_ARRAY_EXT,texid);
  14. glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
  15. glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
  16. glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
  17. glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
  18. glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT,GL_GENERATE_MIPMAP_SGIS,GL_TRUE);
  19. glTexImage3D(GL_TEXTURE_2D_ARRAY_EXT,0,GL_RGBA8,pImagex[0]->sizeX,pImagex[0]->sizeY,Texnum,0,GL_RGB,GL_UNSIGNED_BYTE,NULL);
  20. for(intj=0;j<Texnum;j++)
  21. {
  22. glTexSubImage3D(GL_TEXTURE_2D_ARRAY_EXT,0,0,0,j,pImagex[j]->sizeX,pImagex[j]->sizeY,1,GL_RGB,GL_UNSIGNED_BYTE,pImagex[j]->data);
  23. }
  24. //..................
  25. returnStatus;
  26. }
 
  
bool CMainFrame::SetTextureArray(char* *filename, GLuint& texid)
{	bool Status=false;	
	AUX_RGBImageRec *pImagex[Texnum] ;	
	memset(pImagex, 0,sizeof(void *) *Texnum);
	for(int i = 0; i < Texnum; i++)
	{
		pImagex[i]	=auxDIBImageLoad(filename[i]);
		if(!pImagex[i])
return false;
	}  
Status=true;
    glGenTextures(1, &texid);  
 glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, texid);
	glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
  glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
   glTexParameteri( GL_TEXTURE_2D_ARRAY_EXT, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
	glTexImage3D(GL_TEXTURE_2D_ARRAY_EXT, 0, GL_RGBA8, pImagex[0]->sizeX, pImagex[0]->sizeY, Texnum, 0, GL_RGB,  GL_UNSIGNED_BYTE, NULL);
	for(int j = 0; j < Texnum; j++)	
{	
glTexSubImage3D(GL_TEXTURE_2D_ARRAY_EXT, 0,  0,0,j, pImagex[j]->sizeX, pImagex[j]->sizeY,1, GL_RGB, GL_UNSIGNED_BYTE, pImagex[j]->data);	
}//..................   return Status;}


用法也很简单,不过得fragment shader配合。注意设定纹理坐标得时候要指定r纹理坐标,它表明该顶点选择的是纹理数组中第几张纹理图的相应st纹理坐标。纹理图的排列顺序是上面载入时候的顺序(因此这里也就是filename数组中相应文件名的顺序),载入纹理数组后,用“Layer”来标识(相当于数组下标)。Layer 0就是第一张纹理,依此类推。glTexCoord3f(1.0, 0.0, 1.0)表示对当前顶点应用第二张纹理(Layer2)中(1.0, 0.0)的st坐标。虽然r纹理坐标是浮点数,但因为这里它只起指涉作用,因此只有整数部分有作用(别以为可以偏移,至少我试过,不可以。小数部分依然对选择有影响,但这种影响是混乱的),在shader中,我们要用floor取整。

应用纹理,不能通过以往“绑定纹理ID”的做法,而要通过shader,在需要应用纹理数组做纹理的图元应用shader:

  1. #extensionGL_EXT_gpu_shader4:enable
  2. uniformsampler2DArraytexarray;
  3. voidmain()
  4. {
  5. vec4texCoord=vec4(gl_TexCoord[0].xy,floor(gl_TexCoord[0].z),gl_TexCoord[0].w);
  6. vec4color1=texture2DArray(texarray,texCoord.xyz);
  7. floatbl=fract(gl_TexCoord[0].z);
  8. texCoord+=vec4(0.0,0.0,1.0,0.0);
  9. vec4color2=texture2DArray(texarray,texCoord.xyz);
  10. floatal=fract(gl_TexCoord[0].z);
  11. gl_FragColor=mix(color1,color2,al*bl);
  12. }
 
  
#extension GL_EXT_gpu_shader4 : enable uniform sampler2DArray texarray;void main(){    vec4 texCoord = vec4(gl_TexCoord[0].xy, floor(gl_TexCoord[0].z),gl_TexCoord[0].w);       vec4 color1 = texture2DArray(texarray, texCoord.xyz);     float bl = fract(gl_TexCoord[0].z);           texCoord += vec4(0.0, 0.0, 1.0,0.0);    vec4 color2 = texture2DArray(texarray, texCoord.xyz);     float al = fract(gl_TexCoord[0].z);    gl_FragColor = mix( color1, color2, al*bl);}


其中vertex shader没什么用,直接去掉(disattach)也可 - -不过我还是习惯让顶点shader和像素shader在一起。注意的是在vertex shader传递过来的(或者从应用直接过来的)gl_TexCoord[0]的r坐标(shader中叫p坐标,这里用z表示)要取整(floor),因为在像素处理前各像素的纹理坐标是经过插值的(栅格化Rasterization,这里有提到),而对每个像素真正有用的r坐标只为了指出该像素位置用的是第几张纹理的纹理坐标。采样变量sampler2DArray和纹理颜色获取函数texture2DArray用于texture array版本的纹理处理,应用前要#extensionGL_EXT_gpu_shader4:enable启用拓展。shader这里如果直接输出color1会是如下结果(举例):

texture array demo
注意纹理坐标是怎么被应用的

texture array demo
而用我的shader做pingpong后可以有这样的边界朦胧效果(另举例)

这里mix混合了两次具有偏移的纹理坐标采样所得的颜色。注意我偏移的是r纹理坐标(偏移1单位),这样就可在隔壁纹理图上采样了。用小数部分做混合因子,得到的是相邻纹理的边界混合。美妙!

texture array demo
改变r纹理坐标可有这样的结果

texture array demo
把4个这样的矩形按螺旋顺序拼接起来......

texture array demo
然后适当改变r纹理坐标(进而改变混合因子)......

texture array demo
哈哈,雪融化后是春天啊(怀念小透)

试试把上面的效果应用到一座山怎么样?呵呵,我试了,还做了demo呵呵。就附在下面的演示demo里。看看下篇文章:Terrain Texture-Array Demo看看我是怎么做的吧~
本日志demo放出:TextureArrayDemobyzwqxin.rar
DEMO使用了shader,需要下载glew库到指定目录,下载见此:OpenGL常用的库
按键:
↑ ↓ 在自动放映完毕后可手动调节矩形角点r纹理坐标

本文来源于 ZwqXin ( http://www.zwqxin.com/), 转载请注明.
原文地址: http://www.zwqxin.com/archives/opengl/learn-texture-array.html
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值