Standard)文件,纹理和图据的都依靠经编码的图像像素数量

【题外话】

1 纹理基础

纹理是同种植结构化的仓储形式(Textures are a structured form of
storage),着色器可以起里读取数据,也会拿数据勾勒副内。通常用于储存图像数据,并有多种形式。最广的纹理为2维,但是也有1维、3维要数组(多独纹理叠加形成单一的逻辑对象)、立方体等情势的纹路。

为创立一个纹理,首先要调用函数(glGenTextures())让OpenGL保存一个名以备使用。该名表示了一个早已让成立的纹理对象,只有以拿其绑定至纹路目标(texture
target)后才开它看做纹理的生命周期。这和以缓存对象绑定到缓存绑定点类似。然则不等的凡,一旦以一个纹理绑定到有目的后,它的类在它吃回收前还非可以改。

纹理和图纸据的还指经编码的图像像素数量。不同的凡广泛的图形文件格式,比如PNG,JPG,BMP等,是图像为存储音讯要使用的针对性消息之异编码形式。它存储在磁盘中,或者内存中,可是并无可以让GPU所识别。这多少个文件格式当为读入后,依然得通过CPU解压成bitmap,再传递至GPU端举行应用。

假定纹理格式是可以给GPU所识其它如素格式,能被迅速寻址并采样。压缩纹理,是平栽GPU能一贯读取并出示的格式,使得图像无需解压即可举办渲染,节约大量之内存。其中非减纹理的格式如下。

RGBA8888:每个像素4字节,RGBA通道各占8个,
OpenGL内GL_RGBA默认类型。
RGBA4444: 每个像素2字节,RGBA通道各占4号。
RGB888: 每个像素3字节,RGB通道各占8各项,无透明通道。
RGB565:每个像素2字节,RGB通道各占5/6/5员,无透明通道。
RGBA5551
每个像素2字节,RGB通道各占5各个,透明通道1位,所以要完全透明或不透明。

赶巧使图的JPEG和PNG等压缩格式一样,纹理也出夫各种压缩格式,如(DXT纹理压缩格式,.dds)(ETC纹理压缩格式,.ktx多纹理和.pkm单纹理)(PVRTC纹理压缩格式,.pvr)。具体的压缩格式不深远座谈,不过得明白不同GPU芯片创造商拔取了不同之GPU架构,因而她扶助的纹路压缩格式不同,其中一加连串使用的是Imagination
Technologies的PVRTC架构GPU,其并为BlackBerryA系列处理大旨之内。不同芯片创制商的GPU协助纹理可参看文章1文章2。下边图片来源著作2。

图片 1

新近实验室设自我修改C3D(The 3D Biomechanics Data
Standard)文件,虽然于网上找到了一个吃c3d4sharp的类库,这多少个类库单纯读取C3D文件之话语仍可以,不过假使一旦兑现修改要创设C3D文件就于麻烦了。同时c3d4sharp实现得相比较简单,很多C3D文件里有些数据还无襄助。好以C3D文件总体不是甚复杂,于是自己虽起再描绘了一个C3D文件读写的库,现在于codeplex上创制了单品种给C3D.NET

1.1 原始图像数据

处理器图形最初的时候只得用各图(bitmap)表示,最早的电脑图形对于有点只好用0和1象征青色或白色,后来好就此起0-256的价来表示其灰度。

 

1.1.1 像素包装

图像数据以内存中好少为严密包裹的样式是,在许多硬件平台上,出于性能考虑,图像的各国一行应都于有特定对齐地址开始。在默认境况下,OpenGL接纳4个字节的指向齐格局。例如,有平等布置RGB图像,包含3单重,每个分量占1只字节,假设图像宽度为199像素,那么每一行要的字节数为597字节。不过要是硬件的网布局是盖4字节排列,那么图像的各一样实践最终都会晤填充额外的3独空字节,从而令每一行的内存地址偏移量为4之整数加倍。即便这样表面上看起是荒废了内存空间,但是这种排列可以被多数CPU更迅速之获取数据块。其余多未经压缩的图样格式为以这种惯例,如Windows中之.BMP文件。Targa(.TGA)是盖1字节排列的。

以往OpenGL提交图像数据或者于OpenGL中得图像数据平日,OpenGL需要了然针对数码为什么种情势包装和解包装操作。通过函数void glPixelStorei (GLenum pname, GLint param)glPixelStoref (GLenum pname, GLfloat param)足变动仍然复苏如从的贮存方。例如需要变更成为一体包裹措施(即如素行的对齐格局,可选1.2.4.8)调用函数glPixelStorei(GL_UNPACK_ALIGNMENT, 1)。其参数都是盖GL_UNPACK_和GL_PACK_啊前缀形式改为对出现,前正象征于内存中读取数据(对函数glReadPixels来影响),后者表示存储数据及内存中(对调用函数glTexImage2D卓殊有影响)。该函数的详实介绍参考官网或原著。

【作品索引】

1.1.2 像素图

近日的微处理器图形都因为像素图(pixmap)的主意囤,它发生点儿栽不同之法门囤图形,其一为亮度音信(luminance)加颜色分量(Cb,
Cr),其第二啊RGB(A)颜色分量。

OpenGL中不可能直接读取像素图。只可以通过第三方库获取像素图中之像素数量。此外,在OpenGL中允许用GPU中的颜色缓存读取到内存中。对承诺函数如下。其中pixels所倚的内存空间需要充分存储图形。假诺指定的窗口坐标越了允许的限,实际只可以得到OpenGL缓存区内部数据。Format表示了pixel指向的内存空间颜色布局模式。

void  glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels)

除此以外要留意,glReadPixels从图硬件中复制数据,平时会经过总线传输至网内存。在这种意况下,应用程序必将被封堵,直至内存传输就。此外,假使指定了一个跟图片硬件本地排列不同之像素布局,那么以数据开展重定格式时以有额外的习性开销。此外要留意的是拖欠函数将数据形容副内存中,它的作为遭到上文的函数glPixelStorei安有些参数的熏陶。

其余该函数对于利用对缓存机制的OpenGL环境,默认的是读取后台缓存区数据,可以经调用如下函数来假诺其读取前台缓存区,其参数为GL_FRONT、GL_BACK等。

void glReadBuffer(GLenum mode);
  1. C3D文件格式的布局
  2. C3D文件头的结构
  3. C3D文件参数集合的构造
  4. C3D文件数量区域之布局
  5. 运用C3D.NET读写文件示例
1.1.3 包装的像素格式

直达一样省中函数glReadPixels的参数type,通常采用的是GL_FLOAT等品类,可是也供诸如GL_UNSIGNED_BYTE_3_2_2等包裹的RGB以及部分包的RGBA值类型,为之是许图形数据以重多的抽情势储存,以便与再普遍的颜色图形硬件匹配。

GL_UNSIGNED_BYTE_3_2_2包档次指用1只字节即8各存储两只颜色分量,其储存顺从高位到小分别吗第1及第3个轻重。GL_UNSIGNED_BYTE_3_2_2_REV坐反顺序存储。

 

1.1.4 保存像素

打OpenGL的休息存区读取出来的颜色数据可为积存到地面图片中。原书中运用的凡GLTools三正在库中之API,此处不现实介绍。对于mac
OS环境下,可以采取CoreGraphics框架中之连带API使用这一个像素数量绘制NSImage图片并储存至磁盘中。代码如下。

- (void)saveImage {
    CGSize imageSize = CGSizeZero;
    unsigned int imageByteSize = imageSize.width * imageSize.height * 4;
    GLubyte *rawImagePixels = (GLubyte *)malloc(imageByteSize);
    glReadPixels(0, 0, imageSize.width, imageSize.height, GL_RGBA, GL_FLOAT, rawImagePixels);

    CGColorSpaceRef genericRGBColorspace = CGColorSpaceCreateDeviceRGB();
    CGContextRef imageContext = CGBitmapContextCreate(rawImagePixels, imageSize.width, imageSize.height, 8, imageSize.width * 4, genericRGBColorspace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);
    CGImageRef cgImage = CGBitmapContextCreateImage(imageContext);
    NSImage *image = [[NSImage alloc] initWithCGImage:cgImage size:imageSize];
    CGContextRelease(imageContext);
    CGColorSpaceRelease(genericRGBColorspace);
    CGImageRelease(cgImage);
    free(rawImagePixels);

    [image lockFocus];
    //先设置 下面一个实例
    NSBitmapImageRep *bits = [[NSBitmapImageRep alloc]initWithFocusedViewRect:NSMakeRect(0, 0, imageSize.width, imageSize.height)];
    [image unlockFocus];
    //再设置后面要用到得 props属性
    NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.9] forKey:NSImageCompressionFactor];
    //之后 转化为NSData 以便存到文件中
    NSData *imageData = [bits representationUsingType:NSJPEGFileType properties:imageProps];
    //设定好文件路径后进行存储就ok了
    NSString *filePath = @"";
    [imageData writeToFile:filePath atomically:YES];
}

【一、C3D文件格式的布局】

1.1.5 读取像素

原书中行使GLTools三正值库相关API读取targa图片文件被之像素数量。而于mac
OS环境受到,可以事先经NSImage读取文件,然后经Core
Graphics框架中之连带API将该转化为像从数据,其代码如下。

// 读取图片
NSImage *image = [NSImage imageNamed:@""];
NSData *imageNSData = [image TIFFRepresentation];
CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageNSData, NULL);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
// 将图片转换为二进制数据
CGSize pixelSizeToUseForTexture = image.size;
GLubyte *imageData = calloc(1, (int)pixelSizeToUseForTexture.width * (int)pixelSizeToUseForTexture.height * 4);
CGColorSpaceRef genericRGBColorspace = CGColorSpaceCreateDeviceRGB();
CGContextRef imageContext = CGBitmapContextCreate(imageData, (size_t)pixelSizeToUseForTexture.width, (size_t)pixelSizeToUseForTexture.height, 8, (size_t)pixelSizeToUseForTexture.width * 4, genericRGBColorspace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);
CGContextDrawImage(imageContext, CGRectMake(0, 0, pixelSizeToUseForTexture.width, pixelSizeToUseForTexture.height), imageRef);
CGContextRelease(imageContext);
CGColorSpaceRelease(genericRGBColorspace);
//此时已经获取的像素数据存储在imageData所表示的内存空间内,使用像素数据生成纹理的方法在后面载入纹理时候介绍
free(imageData);

首先说C3D文件全部不是不行复杂,也不曾过多错综复杂的概念,C3D的文档格式可以起其官网下载或者在线阅读。首先C3D文件是坐Section为单位存储的,每一个Section固定啊512字节。Section一定是依顺序存储的,可是有意思的凡,Section的序号是自1上马之,而无是0。C3D文件分为三片段,分别是Section
ID = 1的C3D文件头(固定为一个Section,512字节),Section
ID一般等2(在文件头内会叫闹)的C3D参数集合以及Section
ID不精晓当几(在文件头与参数集合中都会师吃出)的C3D数据有。

1.2 载入纹理

若是获得到如从数据后,可以调用函数载入纹理,纹理要被载入,这一个纹理就会晤成当前纹理状态的如出一辙片段,有三独假如下OpenGL函数最常用来起缓存中载入纹理。

void glTexImage1D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
void glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
void glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels);

此地仅领了载入1维、2维同3维纹理,立方体纹理前边介绍。该函数从data指向的内存地址复制纹理音讯,这种数据复制可能会合有深挺的支付,稍后将研商六只减轻这么些题目标措施。其target参数平日选取GL_TEXTURE_1(2/3)D,同时也可行使GL_PROXY_TEXTURE_1(2/3)D指定代理纹理,代理纹理内容后介绍。level参数指定了mip贴图层次,对于非mip贴图的纹路,该参数总为0。

internalformat参数指定了纹路单元颜色存储方,以及是否开展削减,常用GL_RGBA。参数width、height和depth指定加载纹理的幅度、中度与深,此处需要专注的凡,这个价值最可以吗2底平头埋,对于OpenGL2.0先的本,不符合要求的值导致纹理贴图被隐式禁用,虽然事后的版可以使用不符合要求的数量,可是会骤降实现效用。

参数border指定了一个纹理边界宽度,那一个价少设置为0,在介绍纹理过滤时,该参数扮演了要之角色。参数format和type表示pixels指向的像素数量被之数格式。

只是C3D也发生酷复杂的地方,一个凡关于整型的运,可以行使应用暴发标志的(Int16),也得以用无符号的(UInt16),只可是后者能够储存的数据量要多一些罢了,既然这样,不知缘何当初还要使暴发标志的整型。而且极端要紧之是,文档内无其他标识能提议文档使用的凡何种整型。官方给来之化解措施是,能够因例如帧总数、帧索引等判断,假设念来负数,则用无符号的,否则用暴发记号的。

1.2.1 使用颜色缓存区

一维跟二维纹理能够起GPU的颜色缓存区加载数据并将该看成一个初的纹路来用。函数如下。源缓存区通过glReadBuffer设置。2D颜色缓存区是从未深度,由此不可知诵博3D纹理。

void glCopyTexImage1D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border);
void glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);

旁一个凡C3D文件可以以不同档次的CPU上生成,这呈现于不同CPU可能使用的许节序(Endian)和浮点数字不同,比如我们之所以底CPU都是使Little-Endian以及IEEE754的浮点数标准。从网上查阅还发现发生DEC
(VAX)以及IBM等CPU接纳不同的浮点数标准,详见我前边一样篇稿子:http://www.cnblogs.com/mayswind/p/3365594.html。而C3D则是永葆3近似CPU,IntelCPU接纳Little-Endian以及IEEE754标准的浮点数,DEC
(VAX)接纳的Little-Endian以及故意的浮点数,MIPS
(SGI)选用的Big-Endian以及IEEE754标准的浮点数,所以在读取文档的下也许要卓殊开展处理,在第三节会详细表达。

1.2.2 更新纹理

重新加载纹理可能相会变成性的瓶颈,当有已加载的纹理数据不再以,能够取代掉其中的有的要全部,替换纹理内容往往比使用函数glTextImage加载纹理更快。更新纹理函数如下。其中绝大部分参数和glTextImage函数中的参数准确对应。xOffset、yOffset、zOffset参数指定了以原先纹理贴图中伊始轮换纹理数据的偏移量。

void glTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels);
void glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
void glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels);

除此以外,OpenGL还可打GPU颜色缓存区读博纹理,并插入或沟通原来纹理的同样有的,其函数如下。这里用专注的凡由于颜色缓存区是2D的,因而glCopyTexSubImage3D获取到之是一个颜色平面。

void glCopyTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);
void glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
void glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);

 

1.2.3 纹理对象

纹理状态包含纹理图像本身及一致组纹理参数,这么些参数控制过滤跟纹理坐标的行事。使用函数glTexParameter可以安装那个纹理状态参数。glTexImage和glTexSubImage函数调用时耗费内存较多,在纹理直接举行连忙切换或者更加载不同纹理是开发很酷的操作。OpenGL可以透过纹理对象管理几近更纹理并于她们之间举办急速切换。纹理状态由时绑定纹理对象保障,纹理对象由一个无符号整数标识。能够经下函数生成一个纹理对象往往组。

void glGenTextures (GLsizei n, GLuint *textures);

绑定其中的某个纹理可以经过函数glBindTexture形成。此后持有纹理加载与纹理参数设置只影响时绑定的纹路对象。调用函数glDeleteTextures完了。多次调用函数在销毁大容量纹理时或者会师发出自然之推迟。判断有句柄是否问纹理句子柄可以调用函数glIsTexture

【二、C3D文件头之布局】

1.2.4 加载纹理示例

拿读取到之像素数量加载到纹理中代码如下,注意此处imageData未如从数据,即便改段内存也malloc函数分配,在以完后得释放内存。

// 将二进制数据写入纹理中
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// 下面函数设置了纹理参数,用于描述当纹素坐标不为整数时,以及纹理坐标不在[0-1]范围内时,纹理颜色采样策略
// 具体请参考文章http://blog.csdn.net/wangdingqiaoit/article/details/51457675
// 这里使用最近纹素坐标采样方式,下文还会继续讲解设置纹理参数
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)pixelSizeToUseForTexture.width, (int)pixelSizeToUseForTexture.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);

先是来说第一有,也尽管是C3D的文件头,C3D的文书头一定就占1只Section,即一定的512字节,所以若读取前512字节就好拿全体头数据获拿到了。即便每个Section有512字节的多,可是对于C3D的文本头就占了好少之同一局部,在文件头被起大气空手的区域。其中第一片凡文件头参数有,内容如下:

2 纹理应用

图片 2

2.1 创设与开端化纹理(Creating and Initializing Textures)

完的纹理创建进程包括好成一个名字,将其绑定至有纹理目的,然后报OpenGL需要以中存储图像的尺码。其应用实例的代码如下。

// The type used for names in OpenGL is GLuint
GLuint texture;

// Generate a name for the texture
glGenTextures(1, &texture);

// Now bind it to the context using the GL_TEXTURE_2D binding point
glBindTexture(GL_TEXTURE_2D, texture);

// Specify the amount of storage we want to use for the texture
// 注意该接口在OpenGL 4.3后才能使用,因此mac中无法调用该函数
glTexStorage2D(GL_TEXTURE_2D,    // 2D texture
               1,                // 1 mipmap level
               GL RGBA32F,       // 32-bit floating-point RGBA data
               256, 256);        // 256 x 256 texels

该函数的参数mipmapping代表表示纹理细分级数,该技术依照该参数生成一层层不同分辨率的贴图,在渲染时因物体远近接纳适当分辨率的贴图,从而加速渲染速度并解决锯齿现象。internal
format
为纹理内部保存图像的多少格式,width和height为图像的富裕和大。该函数调用后,OpenGL将汇合分配丰硕的内存空间用于存储纹理。接下来要利用函数glTexSubImage2D()为缓存中输入数据。其示例如下。

// Define some data to upload into the texture
float * data = new float[256 * 256 * 4];

// generate_texture() is a function that fills memory with image data
generate_texture(data, 256, 256);

// Assume the texture is already bound to the GL_TEXTURE_2D target 
glTexSubImage2D(GL_TEXTURE_2D,     // 2D texture
                0,                 // Level 0
                0, 0,              // Offset 0, 0
                256, 256,          // 256 x 256 texels, replace entire image
                GL_RGBA,           // Four channel data
                GL_FLOAT,          // Floating-point data
                data);             // Pointer to data
// Free the memory we allocated before - OpenGL now has our data
delete [] data;

概括的纹理贴图实例如下,源代码

图片 3

字节 类型 说明
00H Byte 参数集合开始的Section ID(通常为0x02,但也不一定)
01H Byte 文件标识(固定为0x50)
02H-03H Int16 每帧里3D坐标点的个数
04H-05H Int16 每帧里模拟测量的个数
06H-07H Int16 第1帧的序号(最小为1)
08H-09H Int16 最后1帧的序号
0AH-0BH Int16 最大插值差距
0CH-0FH Single 比例因子(正数表示存储的帧为整数帧,负数为浮点帧)
10H-11H Int16  数据区域开始的Section ID
12H-13H Int16 每帧模拟采样个数
14H-17H Single 帧率

2.2 纹理目的与项目(Texture Targets and Types)

OpenGL中可用之纹理类型如下,每个重点字还简单了千篇一律部分GL_TEXTURE_
1D:如出一辙维纹理。 2D:第二维纹理。
3D:其三维纹理。RECTANGLE:矩形纹理。1D_ARRAY:同样维纹理数组。2D_ARRAY:老二维纹理数组。CUBE_MAP:立方体纹理。CUBE_MAP_ARRAY:立方体纹理数组。BUFFER:缓存纹理。2D_MULTISAMPLE:二维基本上重样本纹理。2D_MULTISAMPLE _ARRAY:二维差不多重样本纹理数组。

GL_TEXTURE_2D连串的纹路是最常用色,平时用于包裹模型表面。GL_TEXTURE_1D种的纹理被看低度为1底第二维纹理。此外,3D纹理可以用来表示有容量的纹路(can
be used to represent a
volume),具有三维纹理坐标。矩形纹理是亚维纹理的一个特例,它们在通过着色器读取数据的艺术同辅助之参数方面发微小的差距。由于并非所有的硬件都襄助尺寸不为2之整数浅幂的纹理,矩形纹理便受引入。现代的硬件通常还协助尺寸未为2之平头赖幂的纹路,因而矩形纹理可以给当做是2维纹理的一个子集,平常不会晤接纳。

GL_TEXTURE_1D_ARRAYGL_TEXTURE_2D_ARRAY代表结合以么对象中的均等组纹理图像。在本章前边会详细座谈。同样的,立方体纹理表示了六张方形图片集合,它们做成一个立方,该立方体平日能够让用来模拟光照条件。正使GL_TEXTURE_1D_ARRAYGL_TEXTURE _2D_ARRAYGL_TEXTURE_CUBE_MAP_ARRAY
表示了立方体纹理的一个数组。

GL_TEXTURE_BUFFER意味着的纹路是相仿于1D纹理的一个不同平常类型,不同之处在于它的储存实际上在缓存中。除此之外,该型纹理的容量相比较1D纹理大得多。OpenGL中极其小之渴求凡65536纹初次(The
minimum requirement from the OpenGL specification is 65536
texels),然则其实大部分兑现允许创设更甚之缓存—通常只是高达几百百万字节。缓存纹理同样紧缺某些1D纹理帮助之表征,如过滤(filtering)和mipmaps。

最后,GL_TEXTURE_2D_MULTISAMPLEGL_TEXTURE_2D_MULTISAMPLE_ARRAY用于多重复采样抗锯齿(multi-sample
antialiasing),该计数用于加强图形质料,特别是对线条与四头形的边缘。

在此之后的第二局部,也就是储存的风波,听上应该占据多字节,不过出于限制了事件数量最为多不克过18单,同时事件名称最为丰硕呢4字节,所以事件有也只有占好少的空间。由于C3D首倘诺为记录运动的数额,可能在中起为数不少相比关键之地方,事件就用来标记出那些地方的。一个事变包括五个内容,分别是最为充裕四字节的风波名称、一字节的风波是否应亮的状态及一个四字节底但精度浮点数表示事件现身的时刻。

2.3 在方色器中自纹理中读取数据(Reading from Textures in Shaders)

创建纹理对象并以数据传其中后,可以于在色器中读取数据,例如用于为局部着色。在方色器中,纹理被声称也采样变量(sampler
variables),它们经过sampler类型的统一变量和标关系。GLSL中生出多独采样类型变量关键词用于阐明不同系列的纹路对象。在GLSL中得以内置函数texelFetch暨坐标值获取纹理中对承诺坐标的颜色值。其所以法如下。

#version 410 core 
uniform sampler2D s; 
out vec4 color;

void main(void) {
  color = texelFetch(s, ivec2(gl_FragCoord.xy), 0);
}

上述代码中gl_FragCoord为OpenGL的置变量,表示的凡正在让处理的局部在窗口坐标系中的坐标,坐标的单位为浮点型数据。不过函数texelFetch的参数必须是自(0,0)到(width,
height)范围外之整数型数据。第三单参数是纹理的mipmap等级。因为此纹理并未启用mipmap效用,由此使用0。

字节 类型 说明
12AH-12BH Int16 事件名是否支持4字节(支持为0x3039,不支持为0)
12CH-12DH Int16 事件数量(最大为18)
130H-176H Single[] 按事件顺序存储的每个事件发生的时间(第1个帧为0.0s)
178H-188H Byte[] 按事件顺序存储的每个事件是否应该显示(1为显示,0为不显示)
18CH-1D2H Char[] 按事件顺序存储的每个事件的名称(每个事件占4字节)
2.3.1 采样器类型(Sampler Types)

纹理类型以及纹理表明关键字的呼应如下。(GL_TEXTURE_1D,sampler1D)(GL_TEXTURE_2D,sampler2D)(GL_TEXTURE_3D,sampler3D)(GL_TEXTURE_RECTANGLE,sampler2DRect)(GL _TEXTURE_1D_ARRAY,sampler1DArray)(GL_TEXTURE_2D_ARRAY,sampler2DArray)(GL _TEXTURE _CUBE_MAP,samplerCube)(GL_TEXTURE_CUBE_MAP_ARRAY,samplerCubeArray)(GL _TEXTURE_BUFFER,samplerBuffer)(GL_TEXTURE_2D_MULTISAMPLE,sampler2DMS)(GL_ TEXTURE_2D_MULTISAMPLE_ARRAY,sampler2DMSArray)

GLSL中之一一sanpler类型纹理数据表示的免浮点型数据,它们为会以纹理中蕴藏有号和无符号的数量,并在正在色器中读取这么些多少。为了表示包含有标志整形数据的纹路数据变量,只待在对许要词前边加前缀i,同样的,为了表示包含无符号整形数据的纹理数据变量,只需要在针对承诺要词在此之前加前缀u。例如isampler2Dusampler2D

自纹理中读取数据的函数有多独变形。他们的首先个参数为纹理数据,第二参数为采样作标,第三单参数为纹理mipmap级别。函数如下。函数的重回值一定是4维向量,假如纹理中之颜色通道小于4,那么少的大路将重返回0。即使回去值备受之有成员永远不碰面给利用,那么正在色器编译器将相会优化掉冗余代码。

vec4 texelFetch(sampler1D s, int P, int lod); 
vec4 texelFetch(sampler2D s, ivec2 P, int lod); 
ivec4 texelFetch(isampler2D s, ivec2 P, int lod); 
uvec4 texelFetch(usampler3D s, ivec3 P, int lod);

 

2.4 从文本被读博纹理(Loading Textures from Files)

存储图片的格式很多,不过十分少的格式会丰盛好之蕴藏OpenGL协助之装有属性,或者会描述一下像mipmaps,cubemaps等高等特性。一个饱上述条件的纹路格式为.KTX或者Khronos
TeXture
format
,它专用于OpenGL中纹理的存储,它看作容器能储存压缩后底纹理,也克储存此处使用的免减纹理。实际上,.ktx文件格式中连了绝大多数得传递及纹理函数中之参数,这多少个函数能一贯由文本被加载纹理,如glTexStorage2D()glTexSubImage2D().KTX文本的头结构如下。

struct header {
  unsigned char      identifier[12];    用于验证是否为合法.ktx文件
  unsigned int       endianness;        表示文件中数据是以大端模式还是小端模式存储
  unsigned int       gltype;            GLenum类型数据
  unsigned int       gltypesize;        以gltype为单位的单个数据字节大小,当数据的大小端类型
                                        和系统环境不匹配时,在加载数据时需要转换
  unsigned int       glformat;          GLenum类型数据
  unsigned int       glinternalformat;        GLenum类型数据
  unsigned int       glbaseinternalformat;    GLenum类型数据
  unsigned int       pixelwidth;
  unsigned int       pixelheight;
  unsigned int       pixeldepth;
  unsigned int       arrayelements;
  unsigned int       faces;
  unsigned int       miplevels;
  unsigned int       keypairbytes;      在数据头末尾,纹理数据之前存储额外信息
};

一级圣经中提供了一个C++loader类用于加载纹理中的数量,可是遗憾的是彼中间接纳的是OpenGL4.3的接口,由此于mac上不可能正常下,因而无具体研商,需要了解得查阅官方源代码。这里就待指定其加载原来依然经过c语言文件操作读取二前进制流,遵照KTX文件官方的格式规范分析其中的像素数量,随后经过函数glTexStorage2D申请纹理内存,通过函数glTexSubImage2D以及像素数量载入。

【三、C3D文件参数集合的社团】 

2.4.1 纹理坐标(Texture Coordinates)

每当前头的演示中,片段着色器中间接利用时之正规配备坐标作为纹理坐标读取数据,此外,OpenGL也同意行使由定义的纹路坐标。平常对当一些着色器中之大都还纹理而言,推荐使用和受之纹理坐标。对应之顶峰着色器代码如下。

#version 410 core 
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
layout (location = 0) in vec4 position;
layout (location = 4) in vec2 tc; 
out VS_OUT {
  vec2 tc;
} vs_out;

void main(void) {
  //Calculate the position of each vertex
  vec4 pos_vs = mv_matrix * position;
  //Pass the texture coordinate through unmodified
  vs out.tc = tc;
  gl_Position = proj_matrix * pos_vs;
}

一些着色器代码如下。

#version 410 core
layout (binding = 0) uniform sampler2D tex_object;
// Input from vertex shader
in VS_OUT {
  vec2 tc;
} fs_in;
// Output to framebuffer
out vec4 color;

void main(void) {
  // Simply read from the texture at the (scaled) coordinates, and assign the result to the shader’s output.
  color = texture(tex_object, fs_in.tc * vec2(3.0, 1.0));
}

通过也终点指定纹理坐标的措施,可以接纳纹理贴图布满整个模型,得到如下效果,源代码。纹理坐标可以是程序生成,也能够是戏剧家使用模型编辑软件用这些蕴含在模型文件被。

图片 4

C3D文件存储了大气之参数,其使用了看似目录的措施囤了参数,不过还吓才出顶级。即参数有只有参数组和参数,并且每个参数组里只可以有参数不能重包含参数组,每个参数必须于一个参数组内。参数集合初始于文件头被的首先单字节表示的Section
ID,平日也2,然而也非肯定,有的文件会当文件头晚留下起空白,然后参数集合开始的Section
ID就延期了。所以判断是否也C3D文件千万不要同先河读进去个Int16然后判断是勿是0x5002,而迟早要是认清第二只字节是免是0x50,确定参数集合的岗位为必然假使基于文件之第一字节来。

2.4.2 纹理的施用

纹理的用含初阶化纹理对象,程序与着色器之间数据通信,着色器内部读博纹理数据三单至关首要阶段。前边已经讲到,OpenGL中时线程会存在一个OpenGLContext,纹理对象只有绑定到上下文中才会给在色器使用。一个着色器可以蕴涵多单纹理对象,它们得以透过GL_TEXTURE0等枚举值关联。

默认情况下,调用函数glBindTexture(GL_TEXTURE_2D, textureID);会默认将当前纹理关联到GL_TEXTURE0,再一次调用绑定函数会将最后一差绑定的纹路和GL_TEXTURE0关系,从前绑定的纹路和GL_TEXTURE1/2…涉。并且这正在色器内部的之兼具纹理变量都会面自GL_TEXTURE0提到的纹理对象吃读取数据。

小心,通常要以渲染代码少将纹理数据输入到正色器的纹路变量中,可是有时并不需要手动指定,OpenGL会自动关联数据,如上文的言传身教。可是有时对复杂的逻辑必须手动关联那么些数据。调用函数glUniform1i(_textureLoc, 0);,这里第二只参数的值和像GL_TEXTURE0顶枚举变量的末尾一个数值一一对应。

于欲启用多独纹理对象,完整的拔取办法如下。

// 1. 在着色器中声明纹理变量
uniform sampler2D length_texture;
uniform sampler2D orientation_texture;
uniform sampler2D grasscolor_texture;
uniform sampler2D bend_texture;

// 2. 在调用函数glLinkProgram(program);后,获取纹理变量的绑定点,即位置
_colorTextureLoc = glGetUniformLocation(_program, "grasscolor_texture");
_bendTextureLoc = glGetUniformLocation(_program, "bend_texture");
_orientationTextureLoc = glGetUniformLocation(_program, "orientation_texture");
_lengthTextureLoc = glGetUniformLocation(_program, "length_texture");

// 3. 在初始化程序后,从文件或者内存中加载纹理数据
GLenum texturePoint[] = {GL_TEXTURE1, GL_TEXTURE2, GL_TEXTURE3, GL_TEXTURE4};
NSArray *textureFileNames = @[@"grass_length.ktx", @"grass_orientation.ktx", @"grass_color.ktx" ,@"grass_bend.ktx"];
GLuint textures[] = {_lengthTxtureID, _orientationTxtureID, _colorTxtureID, _bendTxtureID};
for (int i = 0; i < 4; i++) {
  glActiveTexture(texturePoint[i]);
  [[TextureManager shareManager] loadObjectWithFileName:textureFileNames[i] toTextureID:&textures[i]];
}

// 4. 在渲染代码内,将纹理对象和着色器中的纹理变量相关联
glUniform1i(_lengthTextureLoc, 1);
glUniform1i(_orientationTextureLoc, 2);
glUniform1i(_colorTextureLoc, 3);
glUniform1i(_bendTextureLoc, 4);

一经对参数集合,起初的4字节概念如下:

2.5 控制纹理数据的读取模式(Controlling How Texture Data Is Read)

纹理坐标是准坐标,其范围也0.0交1.0中间。OpenGL允许控制当坐标不在这一个范围外常对应之拍卖政策,称为采样器的包情势(wrapping
mode)。同样,也得安装纹理数据的揣度办法,这吃称采样器的过滤格局(filtering
mode)。控制它们的参数为存在采样器对象(sampler
object)中。创制采样器的代码如下。

void glGenSamplers (GLsizei count, GLuint *samplers);

装采样器参数的代码如下。

void glSamplerParameteri (GLuint sampler, GLenum pname, GLint param);
void glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param);

在安装完毕采样器参数后,需要拿其绑定至某纹理单元,其代码如下。

void glBindSampler(GLuint unit, GLuint sampler);

参数unit指用绑定至采样器的纹理单元索引。这样纹理对象与采样器对象还绑定至平纹理单元,此时,完整的元素数据及参数就会构建着色器所用之纹素数据。将纹理采样器逻辑和纹理数据分离有以下六只特性。

第一,它同意吗不同的纹路对象下相同之纹理参数,不用在每个纹理数据中都含有冗余数据。
第二,它同意以革新纹理对象时,不用再行革新采样器参数。
第三,它同意对同一个纹理对象下多拟不同之采样器参数。

有的次可能以纹理采样器参数集成及纹理数据里。为了转移这系列型的采样器参数,首先得将纹理对象绑定至纹路单元,然后通过纹理单元调用以下函数。

void glTexParameterf (GLenum target, GLenum pname, GLfloat param);
void glTexParameteri (GLenum target, GLenum pname, GLint param);
字节 类型 说明
00H Byte 第一个参数所在的Section在整个参数集合中的位置(通常为0x01,说明开头4字节之后就是第一个参数)
01H Byte 参数集合部分标识(固定为0x50)
02H Byte 参数集合所占Section数量
03H Byte 生成文件的CPU类型(0x54为Intel,0x55为DEC (VAX, PDP-11),0x56为MIPS (SGI/MIPS))
2.5.1 使用多双重纹理

有时候,在么着色器中,需要以多独纹理。OpenGL直冲多还纹理单元,此时次中待创建多单采样器统一变量,它们引用了不同之纹路单元,同时还欲让绑定至上下文中。获取最老襄助纹理单元数量代码如下。

GLint units;
glGetIntegerv(GL MAX COMBINED TEXTURE IMAGE UNITS, &units);

拿纹理绑定至有特定的纹理单元从前,首先要改变活跃的纹理单元。其函数如下。void glActiveTexture (GLenum texture);,其中参数texture为中定义变量如GL_TEXTURE0格局。生成两只纹理单元的代码如下。

GLuint textures[3];
glGenTextures(3, &textures);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textures[1]);
glActiveTexture(GL_TEXTURE2); 
glBindTexture(GL_TEXTURE_2D, textures[2]);

当左右文中绑定五只纹理时,需要呢着色器中的采样器统一变量指定到不同之纹理单元,此时生零星种方法可供应采用。第一,通过以先后中调用函数glGetUniformLocation()获它们绑定的纹理单元。第二,可以一贯在在色器阐明变量时以指定绑定的纹理单元。当指定了纹理单元后,在程序中调用函数glUniform1i()得直接以纹理输入到在色器中。此处需要留意,采样器变量并无含有一个以在色器中可知给读取的整形变量,然则以设置纹理对应之纹理单元数据,他给作为是一个整形的联变量。在方色器中扬言绑定至纹路单元0、1以及2的采样器统一变量代码如下。

layout (binding = 0) uniform sampler2D foo; 
layout (binding = 1) uniform sampler2D bar; 
layout (binding = 2) uniform sampler2D baz;

个中前边2只字节官方说一向忽略就进行,但是为了配合于写入的时节要如写上的。第3字节其实大家仍梯次读到头也非欲者数据。这其间紧要之是CPU类型,由于不同CPU类型选拔的许节序以及存储的浮点数字有所不同,所以我们尚待基于CPU类型举办相应的拍卖。

2.5.2 纹理过滤(Texture Filtering)

纹理中之纹素坐标和屏幕及之如从坐标几乎无容许全对应。由此,纹理作为几何图形标贴图的下,它们总会于牵涉伸或者缩减。因为几哪图形具有方向性,一个规定的纹路在同一时间在模型的差表面上会有异的缩小表现。

每当眼前文中,着色器中对纹理采样使用的函数为texelFetch(),其参数必须为整数,这里可以领略也纹素坐标,即纹理中之比如说从坐标。对于使用正规纹理坐标时,纹理采样需要调用函数texture()。对诺变形如下。该函数的参数0和1分级指向承诺纹理中的呼应维度的可是小与极其特别纹素,p的取值可以不再0到1限外,此时OpenGL的处理政策将会师于后头介绍。

vec4 texture(sampler1D s, float P); 
vec4 texture(sampler2D s, vec2 P); 
ivec4 texture(isampler2D s, vec2 P); 
uvec4 texture(usampler3D s, vec3 P);

压缩或拉伸纹理的投射中,颜色之推断过程让称呼纹理过滤。在安装采样器参数函数中,参数GL_TEXTURE_MAG_FILTERGL_TEXTURE_MIN_FILTER各自代表拉伸和缩短纹理时用的颜料总括方法。二种为主的测算策略GL_NEARESTGL_LINEAR得被应用。确保于参数GL_TEXTURE_MIN_FILTER要挑选了上述两单政策之一,因为默认的纹理过滤设置更无mipmap纹理(稍后介绍)时莫会晤生效。

不久前过滤策略是极简易吗是绝抢之过滤方法。该策略下摘离想使采样的纹素坐标近年来之纹素,并将该颜色音信再次来到作为有颜色。该政策的一定是,当纹理被无限放大后,图像遭到相会冒出大量无总是色块。设置采样器过滤策略也多年来过滤代码如下。

glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

线性过滤的属性消耗又要命,可是对当代之图像硬件,这么些影响好忽略。线性过滤的政策是,对于用采样的纹理坐标,取其周围纹素及投机的加权平均值作为有的颜色值再次来到。当采样坐标位于单个纹素中央时,会直接重回该纹素的颜色值。线性过滤的风味是当纹理被顶拉伸时,图像模糊化。设置采样器过滤策略也线性过滤的代码如下。

glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

于与一个纹理使用简单栽不同之过滤方法相比较如下。

图片 5

对此AMD和DEC生成的文档,都是应用Little-Endian字节序存储的文档,所以必然要运Little-Endian来读取Int16、Single等门类;而MIPS则用的Big-Endian字节序存储文档,所以于读取的上自然要是认清当前电脑默认的许节序以及文档选用的配节序。

2.5.3 分级细化纹理(Mipmaps)

分别细化纹理可以加强渲染性能,同时晋级气象视觉质地。它解决了用标准纹理映射面临的点滴个问题。其一为闪烁(scintillation)(混叠效应(aliasing
artifacts)),它出现于当使用大尺寸纹理渲染小尺码模型时。在运动场景中,这种场所大累。

仲独问题还多之是与性相关,但其缘由和率先只问题类似。纹理占用了大量的内存,相邻片段访问了未挂钩的纹理,导致纹理中相当酷一部分几乎未叫采纳。

各自细化纹理指的凡一对列的图像,即以加载纹理时,同时加载了起最要命尺寸及绝小尺寸的纹理。OpenGL提供了新的一样组过滤情势来对不同之几哪模型选拔最符合的纹路。在牺牲内存的前提下,可以解除闪烁现象,同时解决以非相邻内存直接的寻址性能压力,同时可在待的下提供高分辨率的纹理。

各自细化纹理由同样体系纹理组成,每个纹理在各轴上的尺寸依旧眼前一个纹理的一半。分级细化纹理并无要求自然是正方形纹理,不过最后一摆纹理尺寸像素一定为1*1。当其中1只维度到1后,只待裁减另外一个维度即可。对于2D纹理,使用各自细化纹理将万分多1/3的内存开销。

这会儿调用函数glTexImage2D加载分级细化纹理时,参数level传分级索引,0代表未经裁减的纹路。使用函数glTexStorage2D()分配内存时方可指定在参数levels中指定分级的索引数,其它,可以安装渲染时使用的优异小最丰硕纹理索引值。其代码如下。

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4);

要是于AMD和MIPS生成的文档,对于浮点数字之蕴藏都是下专业的IEEE754浮点数字,对于.NET而言不需要举行其他处理;而DEC生成的文档则利用特有浮点数,需要拿4独字节全体读取将来还拓展非凡规之换,转换方法见自己前面的作品:http://www.cnblogs.com/mayswind/p/3365594.html

2.5.4 分级细化纹理过滤(Mipmap Filtering)

独家细化纹理过滤模式相比受一般纹理,其暴发4独附加的过滤格局。GL_NEAREST_MIPMA
P_NEAREST:
拔取近来底纹理贴图,选取最近的纹素。GL_LINEAR_MIPMAP_LINEAR
在分别纹理直接执行线性插值确定最终的纹理图,然后于推行纹素之间的加权平均线性过滤操作。GL_NEAREST_MIPMAP_LINEARGL_LINEAR_MIPMAP_NEAREST。前一个参数表示纹素的过滤形式,后一个参数表示分级纹理的挑选情势。

假设纹理的过滤格局于安装为GL_LINEAR或者GL_NEAREST,那么纹理分级不晤面让启用。必须动上述4个分级细化纹理过滤模式备受的一个。

纹理的过滤格局需要基于具体求来选取,例如,GL_NEAREST_MIPMAP_NEAREST供非常好之性质和低混染(闪烁)效果,不过近日过滤法总会导致视觉上的来得问题。GL_LINEAR_MIPMAP_NEAREST常备用于加速游戏,因为使用了重复胜似质料之线性过滤,同时多年来甄选个别纹理策略也会快速形成。需要专注的是,设置分级纹理过滤格局只可以用于参数GL_TEXTURE_MIN_FILTERGL_TEXTURE_MAG_FILTER只能使有限独主旨过滤法则。

用以来甄选个别纹理策略有老好之视觉感受。从模型侧面看,在不同的表上能瞥见不同分级索引纹理之间的过火,从一个高分辨率表面转换来其它一个表面可能相会看见扭曲线或者深强烈的超负荷。GL_LINEAR_MIPMAP_LINEARGL_NEAREST_MIPMAP_
LINEAR
每当接纳各自纹理时实施了额外的插值来祛除这种强烈的过于区域,然则会招致额外的性能开销。GL_LINEAR_MIPMAP_LINEAR普通为叫做3次线性纹理分级细化(trilinear
mipmapping),此外还有复上进的图像过滤技术,它们可以起非常好之结果。

在这个之下就囤在有的参数了,参数分为两像样,分别是参数组和参数。

2.5.5 生成分级细化纹理等级(Generating Mip Levels)

前文已经讲到,在加载纹理时加载所有级另外分级纹理会浪费额外的内存,并且其中一部分纹理无论是程序员还是用户几乎都非汇合利用,由此超越后中通过相关API生成分级纹理是一个再度高效更常用之不二法门。该效用对承诺函数如下,它会转移于平组完整的个别纹理。

void glGenerateMipmap(GLenum target);

纹理目标参数与前面文生成纹理可选参数相同,此时欲专注,生成纹理的速并无碰面比加载纹理的快慢又快。对于性要求大,以及视觉特效要求强的顺序,依旧推荐加载预生成纹理。

对参数组,要存储以下6独内容:

2.5.6 使用各自细化纹理(Mipmaps in Action)

选用各自纹理的实例如下,源代码

图片 6

字节 类型 说明
00H SByte 参数组名称长度(如果为负数则表示该参数组锁定请不要修改,而长度为绝对值)
01H SByte 参数组ID的负数
02H – … Char[] 参数组名称(仅包含大写字母、0-9以及下划线_)
… + 1 – … + 2 Int16 下一参数组/参数的偏移(包含本内容的2字节)
… + 3 Byte 参数组描述长度
… + 4 –  Char[] 参数组描述内容(ASCII码)
2.5.7 纹理包装(Texture Wrap)

数见不鲜,纹理采样函数中的地方坐标范围是0及1中,假诺过量这界定,OpenGL将会面因采样器的包情势处理数量。通过调用函数glSamplerParameteri得指定包装情势,其中参数GL_TEXTURE_WRAP_S(T/R)独家对诺坐标的老三独重,其值可以装的品类也GL_REPEATGL_MIRRORED_REPEATGL_CLAMP_TO_EDGEGL_CLAMP_TO_BORDER。其中repeat模式可解啊整数值取1,小数值仅抱小数部分,用于将小尺寸贴图贴于大尺寸的几哪表面。MIRRORED_REPEAT代表镜像重复。

当以线性过滤时,纹理边缘的纹素需要取得其周围纹素值总计,此时,如包装形式吗GL_REPEAT,那么OpenGL将会见获取纹理另一侧的首先排或者第一推行。这种情势在纹理将会师卷入模型,并且每单和针对性边拼接在一齐时那多少个常用(如球体中)。

夹有纹理包装格局还要分开为片种植。第一种to_border的颜料由预先通过函数glSamplerParameterfv和参数GL_TEXTURE_BORDER_COLOR设置而得。第二栽to_edge强制令大于1的值等于1,小于0的值等于0。四种不同之纹路包装格局如下,从达到顶下,从错误到右,分别吗TO_BORDERMIRRORED_REPEATTO_EDGEREPEAT。不同系列包装格局如下图所示,源代码

图片 7

C3D文件没有规定一个参数组后面与其它一个参数组依旧与该参数组里的持有参数,所以读取的当儿固然专注下。而参数的情节则同参数组基本相同,只是当生一样参数组/参数的偏移与参数组描述长度之间存放着该参数的骨子里多少罢了,由于地点描述起来最为难为了,那里就是不写了。

2.6 数组纹理(Array Textures)

上文中大多独正色器中援引多独纹理都是透过讲明多单采样器变量实现的,另外OpenGL还提供了采样器数组变量来加载数组纹理。其实以个别纹理中单个采样器同样引用了大多单纹理,其余cube类型的纹理中每个面都是独自的一个纹理对象。需要专注的是,OpenGL不帮助创造3D底纹路数组。数组纹理的成员为会是各自纹理。为了区别单个纹理和纹理数组,平时数组元素被称为层(layers)。

2D纹理数组可以给认为是一个例外之3D纹理(同样的1D纹理数组能叫看作是2D纹理),它们中的关键区别是屡组纹理的各层之间莫会见时有暴发过滤用,另外纹理数组能支撑之分子大小或者比较3D纹理尺寸又要命。

字节

2.6.1 加载2D纹理数组(Loading a 2D Array Texture)

始建2D纹理数组的步骤为,创制纹理对象,绑定至GL_TEXTURE_2D_ARRAY对象,分配内存,填充数据。这里用3D纹理模式分配内存和填充数据,第3维被清楚为数组元素,或者层。此外.KTX格式文件扶助数组纹理,可以起文本被直接读博纹理数组。读博纹理代码如下。

GLuint tex;
glGenTextures(1,_&tex); 
glBindTexture(GL TEXTURE 2D ARRAY, tex);
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 8, GL_RGBA8, 256, 256, 100);
for (int i = 0; i < 100; i++) {
  glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, 256, 256, 1, GL_RGBA, GL_UNSIGNED_BYTE, image_data[i]);
}

此间提供64独外星人头像纹,在屏幕中绘制264个头像,将每个头像的旋转角度与偏离值传入顶点着色器,并当里面总计模型出现的轻易地方、片段的纹理坐标和纹理索引。顶点着色器代码如下。

#version 410 core
layout (location = 0) in int alien_index;
out VS_OUT {
  flat int alien;
  vec2 tc; 
} vs_out;
struct droplet_t {
  loat x_offset; 
  float y_offset; 
  float orientation; 
  float unused;
};
layout (std140) uniform droplets {
  droplet_t droplet[256];
};

void main(void) {
  const vec2[4] position = vec2[4](vec2(-0.5, -0.5), vec2(0.5, -0.5), vec2(-0.5, 0.5), vec2(0.5, 0.5));
  vs_out.tc = position[gl VertexID].xy + vec2(0.5); 
  vs_out.alien = alien_index % 64;
  float co = cos(droplet[alien_index].orientation); 
  float so = sin(droplet[alien_index].orientation); 
  mat2 rot = mat2(vec2(co, so), vec2(-so, co)); 
  vec2 pos = 0.25 * rot * position[gl VertexID];
  gl_Position = vec4(pos.x + droplet[alien_index].x_offset, pos.y + droplet[alien_index].y_offset,0.5, 1.0);
}

在部分着色器中,使用自极限着色器中得到的数目及由程序中获得的纹路数据直接着色。代码如下。其中采样函数中之老三独参数为利用纹理的层索引,即现实接纳何人外星头像。

#version 410 core
layout (location = 0) out vec4 color;
in VS_OUT {
  flat int alien;
  vec2 tc; 
} fs_in;
layout (binding = 0) uniform sampler2DArray tex_aliens; 

void main(void) { 
  color = texture(tex_aliens, vec3(fs_in.tc, float(fs_in.alien)));
 }

渲染模型有代码如下。

void render(double currentTime) {
  static const GLfloat black[] = { 0.0f, 0.0f, 0.0f, 0.0f }; 
  float t = (float)currentTime;
  glViewport(0, 0, info.windowWidth, info.windowHeight);     
  glClearBufferfv(GL_COLOR, 0, black);
  glUseProgram(render_prog);
  glBindBufferBase(GL_UNIFORM_BUFFER, 0, rain_buffer); 
  //GL_MAP_INVALIDATE_BUFFER_BIT 表示对映射的内存除缓存以外区域不会更新其值
  vmath::vec4 * droplet = (vmath::vec4 *)glMapBufferRange(GL_UNIFORM_BUFFER, 0, 256 * sizeof(vmath::vec4), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
  // droplet_x_offset等数组值已经提前预设
  for (int i = 0; i < 256; i++) {
    droplet[i][0] = droplet_x_offset[i]; 
    droplet[i][1] = 2.0f - fmodf((t + float(i)) * droplet_fall_speed[i], 4.31f);
    droplet[i][2] = t * droplet_rot_speed[i];
    droplet[i][3] = 0.0f; 
  }
  glUnmapBuffer(GL_UNIFORM_BUFFER);
  int alien_index;
  for (alien_index = 0; alien_index < 256; alien_index++) {
    glVertexAttribI1i(0, alien_index);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  }
}

最终绘制结果如下,圆源码

图片 8

类型

2.7 在方色器中向纹理中形容副数据(Writing to Textures in Shaders)

需要注意的凡,下文内容在OpenGL4.2及以上版本才会利用,在4.2从前只好通过离屏渲染的技能将一切图像渲染到整纹理中(该技术延续著作提到)。OpenGL不仅能当以被读博纹理数据及纹理对象吃,还同目的在于正在色器中直接读取和描绘副纹理数据。正而利用采样器变量可以表示一个纹理对象,使用”image”变量可以代表纹理内的平摆图。和sampler变量一样,image变量有image1D等重重变形,最常用之是image2D。着色器中纹理的宣示形式吗uniform image2D my_image;

着色器中读博纹理数据与描写副纹理数据的函数如下。注意,对于不同的纹路类型,该函数有各类变形。这里需要留意,坐标参数p为整形值,其象征纹素坐标,此处过滤情势尚未其他意义。

vec4 imageLoad(readonly image2D image, ivec2 P); 
void imageStore(image2D image, ivec2 P, vec4 data);

除此以外,图像变量也克积存整形数据,其针对性诺函数如下。

ivec4 imageLoad(readonly iimage2D image, ivec2 P); 
void imageStore(iimage2D image, ivec2 P, ivec4 data); 
uvec4 imageLoad(readonly uimage2D image, ivec2 P); 
void imageStore(uimage2D image, ivec2 P, uvec4 data);

跟纹理绑定需要激活当前纹理不同,使用函数glBindImageTexture()得一向用图像绑定至图像单元。其参数unit为从0增长之绑定点;参数level表示分级数量的目录;在行使单个纹理的时光参数layered设置为GL_FALSE,此时参数layer被忽略,当以纹理数组时欠参数设置为GL_TRUE,此时参数layer为对应索引;参数可选GL_READ_ONLY、GL_WRITE_ONLY或者GL_READ_WRITE。

参数format指图片中数据,必须和分配内存时同,可选GL_RGBA32F等(占用内存大小一致即可,例如GL_RGBA32F和GL_RGBA32I以及GL_RGBA32UI中间可以自由匹配)。另外,在方色器中形容副数据时系统活动处理,读取数据时要以相应品种的扬言标识符号(如rgba32f)。需要提议的凡格式GL_R11F_G11F_B10F只和和谐配合,而GL_RGB10_A2UI和GL_RGB10_A2仅相互配合。

下加载与仓储图片数的着色器示例代码如下。

#version 410 core
// Uniform image variables:
// Input image - note use of format qualifier because of loads 
layout (binding = 0, rgba32ui) readonly uniform uimage2D image_in; 
// Output image
layout (binding = 1) uniform writeonly uimage2D image_out;
void main(void) {
  // Use fragment coordinate as image coordinate
  ivec2 P = ivec2(gl_FragCoord.xy); 
  // Read from input image 
  uvec4 data = imageLoad(image_in, P);
  // Write inverted data to output image
  imageStore(image_out, P, ~data);
}

虽然达文代码很复杂,但当时让部分着色器更加强。那意味着部分着色器不再是只好向定点地点写副数据,它会往一个图片的即兴地点写副数据,同时通过多单图像变量还是可以通往四只图像被写副数据。同时下面的技能驱动任意阶段的着色器都可以望图片被描写副数据。然而得留意的是,倘使多独在色器都指向同一个内存地点的图像更改,必须使原子操作。(GPU高并发特性导致同时刻五只同等级着色器函数同时运转)。

说明

2.7.1 图像被之原子操作(Atomic Operations on Images)

恰巧而正在色器储存闭包统一变量中之原子操作一样,图像变量中为提供了对应的原子操作。OpenGL中提供的连锁内置函数如下。除了imageAtomicCompSwap,各样函数参数都出于图形变量,修改点坐标,数据做。

imageAtomicAdd:读-加-存,再次回到原始值。
imageAtomicAnd:读-逻辑与-存,重临原始值。
imageAtomicOr:读-逻辑或-存,重返原始值。
imageAtomicXor:读-逻辑异或-存,再次回到原始值。
imageAtomicMin:读-取小-存,再次来到原始值。
imageAtomicMax:读-取大-存,重返原始值。
imageAtomicExchange:读-存,再次回到原始值。
imageAtomicCompSwap:读-和comp比较-倘诺相等存,重回原始值。

上述顺序函数都存在以下多只还载函数,其代码如下。

uint imageAtomicAdd(uimage1D image, int P, uint data); 
uint imageAtomicAdd(uimage2D image, ivec2 P, uint data); 
uint imageAtomicCompSwap(uimage3D image, ivec3 P, uint comp, uint data);

利用原子统计变量,image变量,着色器储存闭包变量可以呢每个像素的具有片段建立一个链表。其中原子变量用于表示各样部分在多少数组中之目,着色器存储闭包用于表示数据数组,image变量用于存储每个像素点的链表头索引(这里要留意OepnGL在光栅化过程遭到,平时每个像素包含四个组成部分)。一个演示代码如下。

#version 430 core
// Atomic counter for filled size
layout (binding = 0, offset = 0) uniform atomic_uint fill_counter; 
// 2D image to store head pointers
layout (binding = 0) uniform uimage2D head_pointer;
// Shader storage buffer containing appended fragments
struct list_item {
  vec4 color; 
  float depth; 
  int facing; 
  uint next;
};
layout (binding = 0, std430) buffer list_item_block {
  list_item   item[];
};
// Input from vertex shader
in VS_OUT {
  vec4 in; 
} fs_in;
void main(void) {
  ivec2 P = ivec2(gl_FragCoord.xy);
  uint index = atomicCounterIncrement(fill_counter);
  uint old_head = imageAtomicExchange(head_pointer, P, index);
  item[index].color = fs_in.color;
  item[index].depth = gl_FragCoord.z;
  // gl_FrontFacing 为back-face culling stage阶段生成的变量,无论精选是否被禁用,该变量都会生成。
  item[index].facing = gl_FrontFacing ? 1 : 0;
  // 需要注意的是在OpenGL的光栅化过程中,每个像素会含多个片段,
  // 而此处内置变量gl_FragCoord的x和y取值为0到当前在屏幕中渲染视图的宽高
  item[index].next = old_head;
}

以此外一个部分着色器中,通过追溯链表的道以每个像素的具有片的深值叠加在一起,并将该当作出口颜色用于渲染图形。其代码如下。注:这里开中例子分了三独program来拍卖图像,第一个program用于先行处理多少,其中调用函数imageStore(head_ pointer, P, uvec4(0xFFFFFFFF));将image变量中具有变量赋值为最特别整数,用于标识链表末尾。而这里上下两独片着色器分别吗第二与老多少个阶段中之一部分着色器代码。

#version 430 core
// 2D image to store head pointers,关键字coherent表示内存连续
layout (binding = 0, r32ui) coherent uniform uimage2D head_pointer;
// Shader storage buffer containing appended fragments
struct list_item {
  vec4 color; 
  float depth; 
  int facing; 
  uint next;
};
layout (binding = 0, std430) buffer list_item_block {
  list_item  item[];
};
layout (location = 0) out vec4 color; 
const uint max_fragments = 10;
void main(void) {
  uint frag_count = 0;
  float depth_accum = 0.0;
  ivec2 P = ivec2(gl_FragCoord.xy);
  uint index = imageLoad(head_pointer, P).x;
  while (index != 0xFFFFFFFF && frag_count < max_fragments) {
    list_item this_item = item[index];
    if (this_item.facing != 0) {
      depth_accum -= this_item.depth;
    } else { 
      depth_accum += this_item.depth;
    }
    index = this_item.next;
    frag_count++;
  }
  depth_accum *= 3000.0;
  color = vec4(depth_accum, depth_accum, depth_accum, 1.0); 
}

事先的情

2.8 同台看图片(Synchronizing Access to Images)

刚而前文表明在色器存储闭包统一变量时同步操作一样,对于图片数据的描摹副乎起外存屏障来实现联机看。其函数为glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BIT);。即当某些着色器必须下前一个着色器写副的颜料变量时,需要在简单单操作间插入内存屏障以管教第一只在色器中的图像写入操作让全体执行后,后续使用及该数量的代码才会见实施。同样的,该函数也来照应的着色器语法版本,为memoryBarrierImage()。该函数包了再某个函数中图纸操作完执行后套数才汇合吃实施回来操作。

此地原著中发出一个演示程序,使用了前文的少数独片着色器,可是该有逻辑必须拔取OpenGL4.2上述特性,mac只好襄助到4.1。示例程序运行结果如下。

图片 9

 

2.9 纹理压缩(Texture Compression)

渲染图形时以纹理所有数据全载入GPU对于大质地之图像渲染非凡重点,对于丰硕容量的纹路数据一般采用纹理压缩的措施确保GPU处理纹理的急速。纹理的缩减并无像类似于JPEG这种图像压缩有特别高的压缩率,不过据会节省大量的空中。其它是因为GPU在拍卖压缩纹理时所待之多少再一次少,因而处理常占用的内存带富(memory
bandwidth)也再一次粗。OpengGL中襄助之压缩格式如下。其中各样品类大概相同关键字GL_COMPRESSED_

这边小无涉嫌通用为PC端(由Nvidia和AMD提供的GPU芯片)的DXT(.dds)格式,通用为iOS设备的PVRTC纹理格式(A8电脑后,匡助ASTC格式,其缩减比更胜似,苹果设备的GPU为PowerVR),以及高通GPU辅助之ATC格式。其中ETC2吧保有辅助OpenGL
ES3.0的GPU通用纹理压缩格式。

Generic类型
RED、RG、RGB、RGBA、SRGB、DRGB_ALPHA、
RGTC类型
RED_RGTC1、SIGNED_RED_RGTC1、RG_RGTC2、SIGNED_RG_RGTC2
BPTC类型
RGBA_BPTC_UNORM、SRGB_ALPHA_BPTC_UNORM、RGB_BPTC_SIGNED_FLOAT、RGB_BPTC_UNSIGNED_FLOAT
ETC2类型
RGB8_ETC2、SRGB8_ETC2、RGB8_PUNCHTHROUGH_ALPHA1_ETC2、SRGB8_PUNCHTHROUGH_ALPHA1_ETC2、RGBA8_ETC2_EAC、SRGB8_ALPHA8_ETC2_EAC
EAC类型
R11_EAC、SIGNED_R11_EAC、RG11_EAC、SIGNED_RG11_EAC

Generic类型的纹理压缩格式允许OpenGL自行以最好精良办法决定缩短编制。

RGTC类型压缩格式将纹理分解为4乘胜4纹素的片,各类块内部开展削减。该格式就针对利用单或者双通道的生标志和无符号数据格式的纹理生效。该格式压缩率大约为50%。

BPTC类型压缩格式同样用纹理分解为4就4纹素的丘,每个片的高等学校啊16字节。每个片内部使用了重新扑朔迷离的缩减方法,它对平组端点和他们中连线的职位描述进行削减。这样就是可知通过端点来转每个像素的颜料消息。该格式用于单个通道也1字节之不标准化数据及单个通道也4字节底浮点型数据格式的纹路。该格式对于RGBA浮点型格式的纹路压缩率为25%,对于RGB未标准化格式纹理压缩率为33%。

ETC2(爱立信纹理收缩)格式和EAC(埃里克(Eric)(Eric)sson Alpha
Compression)格式都能让OpenGL
ES3.0之上版本襄助。它用于单个像素内存占用最为小之应用程序,这个应用程序位于移动设备遭受,他们的内存带富和处理器遭到CPU的牵动富有这多少个可怜的区别。

拍卖上述涉的格式,还有类似于S3TC格式(DXT的最初版本)和ETC1格式,在运用他们前边都得检查时GPU是否帮助该格式的纹路。最好之章程是反省是不是协助相关的扩展,例如当检查是否匡助S3TC格式时,使用字符串GL_EXT_texture_compression_
s3tc

Int16

2.9.1 使用压缩纹理(Using Compression)

减纹理可以因而简单栽办法,推荐使用第一种植先拿纹理压缩后存储于文书中,OpenGL帮助读取压缩纹理。其余OpenGL也补助在程序中减弱纹理,通过点名某个internalformat为某某压缩格式,可以当加载纹理的同时对纹理举行压缩。

以动压缩纹理和未减纹理时连没外区别。在采样函数内部OpenGL会自动举行转换。KTX格式文件能够平素存储压缩纹理。检查纹理是否为压缩可以用函数glGetTex LevelParameteriv (GLenum target, GLint level, GLenum pname, GLint *params);。可以因此参数GL_TEXTURE_INTERNAL_FORMAT检查纹理格式是否为削减纹理。压缩的纹理格式可以指定为能够透过函数glGetInternalFormativ()与参数GL_TEX
TURE_COMPRES SED拿到。另外可以一贯通过参数GL_TEXTURE_
COMPRESSED直接检查纹理是否给抽。

当加载了压缩纹理后,可以调用函数glGetCompressedTexImage()直沾压缩的纹路数据。其代码如下。

 Glint imageSize = 0;
glGetTexParameteriv(GL_TEXTURE_2D,
                    GL_TEXTURE_COMPRESSED_IMAGE_SIZE,
                    &imageSize); 
void *data = malloc(imageSize);
glGetCompressedTexImage(GL_TEXTURE_2D, 0, data);

加载压缩纹理可以调用函数glCompressedTexImage1D()对等与她的照应更新纹理函数glCompressedTexSubImage3D。在动上述函数时欲注意x和y轴上之偏移量,其它大多数减纹理格式都是经过压缩4*4的纹素块来实现纹理压缩。(在原著示例代码loder类中,并未表现裁减纹理的奇处理,但写被干可以应用其loader类可以平素加载压缩纹理,可能是以普通纹理加载函数仍会加载压缩纹理,此处需继续探讨)。

产同样参数组/参数的皇(包含本内容之2字节)

2.9.2 共享指数(Shared Exponents)

即便在真实情形下,共享指数并无是一个确实的滑坡纹理格式,不过其仍会以节约存储空间的气象下同意采用浮点型纹理数据。该格式下将每个通道的小数部分及他们共的指数部分以平头道囤(The
fractional and exponential parts of each value are stored as
integers),在采样时算出颜色值。例如格式GL_RGB9_E5即是9bits用于存储每个通道的小数部分,5bit用于存储他们之一头指数部分,这样于RGB格式可以上67%底压缩率。

 

2.10 纹理视图(Texture Views)

前文中兼有案例被,程序中加载的纹理类型和着色器中行使的类是一模一样的。可是有时会赶上加载的纹理类型和着色器中得的纹路类型不匹配的情状。此时得以利用纹理视图(Texture
Views)在新的纹理对象吃选取原纹理对象的数量。该技术的利用形式要出以下简单栽,当然她可以又以。

一:纹理视图可以拿某体系型纹理包装也一个异的类,例如,可以经过纹理视图将一个2D纹理作为一个单层2D累组纹理使用。

二:纹理视图可以以纹理中的多寡包装成那么些忠实数据类型以外的数据。例如,加载一个间格式为GL_RGBA32F(即4独32bit之float类型数据)纹理,创造一个GL_RGBA32UI的纹路视图。(so
that you can get at the individual bits of the texels)

Byte

2.10.1 纹理视图(Texture Views)

大多数纹理类型且能够创建至少一个以上之纹理视图,可是缓存纹理(buffer
textures)不可知,因为它曾是缓存对象的视图,对于此类型纹理,只可以拿与一个缓存对象绑定至此外一个缓存纹理来采用其中的数量。

void glTextureView(GLuint texture, 
                   GLenum target,
                   GLuint origtexture,
                   GLenum internalformat,
                   GLuint minlevel,
                   GLuint numlevels,
                   GLuint minlayer,
                   GLuint numlayers);

连锁函数如达到所示,参数texture为视图内部纹理的标识名,target为新创立纹理类型,origtexture代表原本纹理标识名,target必须和origtexture兼容。internalformat为新纹理的数据类型,它要同原有纹理的数据类型兼容。

纹理类型兼容表如下。

Original Texture               New Texture
1D                             1D or 1D_ARRAY
2D                             2D or 2D_ARRAY
3D                             3D
CUBE_MAP                       CUBE_MAP, 2D, 2D_ARRAY, or CUBE_MAP_ARRAY
RECTANGLE                      RECTANGLE
BUFFER                         none
1D_ARRAY                       1D or 1D_ARRAY
2D_ARRAY                       2D or 2D_ARRAY
CUBE_MAP_ARRAY                 CUBE_MAP, 2D, 2D_ARRAY, or CUBE_MAP_ARRAY
2D_MULTISAMPLE                 2D_MULTISAMPLE or 2D_MULTISAMPLE_ARRAY
2D_MULTISAMPLE_ARRAY           2D_MULTISAMPLE or 2D_MULTISAMPLE_ARRAY

配合的格式必须于平等类格式中,格式分类如下。

Format Class           Members of the Class
128-bit                GL_RGBA32F, GL_RGBA32UI, GL_RGBA32I
96-bit                 GL_RGB32F, GL_RGB32UI, GL_RGB32I
64-bit                 GL_RGBA16F, GL_RG32F, GL_RGBA16UI, GL_RG32UI, GL_RGBA16I, GL_RG32I, GL_RGBA16, GL_RGBA16_SNORM
48-bit                 GL_RGB16, GL_RGB16_SNORM, GL_RGB16F, GL_RGB16UI, GL_RGB16I
32-bit                 GL_RG16F, GL_R11F_G11F_B10F, GL_R32F, GL_RGB10_A2UI, GL_RGBA8UI, GL_RG16UI, GL_R32UI, GL_RGBA8I, GL_RG16I, GL_R32I, 
                       GL_RGB10_A2, GL_RGBA8, GL_RG16, GL_RGBA8_SNORM, GL_RG16_SNORM, GL_SRGB8_ALPHA8, GL_RGB9_E5
24-bit                 GL_RGB8, GL_RGB8_SNORM, GL_SRGB8, GL_RGB8UI, GL_RGB8I
16-bit                 GL_R16F, GL_RG8UI, GL_R16UI, GL_RG8I, GL_R16I, GL_RG8, GL_R16, GL_RG8_SNORM, GL_R16_SNORM
8-bit                  GL_R8UI, GL_R8I, GL_R8, GL_R8_SNORM
RGTC1_RED              GL_COMPRESSED_RED_RGTC1, GL_COMPRESSED_SIGNED_RED_RGTC1
RGTC2_RG               GL_COMPRESSED_RG_RGTC2, GL_COMPRESSED_SIGNED_RG_RGTC2
BPTC_UNORM             GL_COMPRESSED_RGBA_BPTC_UNORM, GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM
BPTC_FLOAT             GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT

minlevel和numlevels分别表示初纹理中首先独与各自纹理和各自数量,那点儿单参数允许打原的分别纹理中取部分叠,例如当就创建旧的各自纹理第一层纹理时该组参数分别吗0暨1。

同一的参数minlayer和numlayers用于取数组纹理类型中之有重叠。例如当得抽取一个20叠旧纹理的中4层凡是,该组参数可以分别安装为8及4。

于动该法变通新纹理后,新纹理中之数据变动会潜移默化原有纹理中的数量。例如,当用2D项目纹理通过纹理视图引用2D屡屡组纹理中的某个平等叠时,使用函数glTexSubImage2D立异新纹理中之数据后,旧纹理中针对应层的数量吧随即变动。新的纹理在正色器中运用的模式与纹理的主旨用法平,上述新纹理在正在色器中因故要字sampler2D表示。

参数存放内容的品种(-1 Char,1 Byte,2
Int16,4 Single),相对值即为长

2.11 OpenGL数据操作总括

该章紧要介绍OpenGL中大量数目标传递格局。在管道的起源,通过缓存对象与终极属性为极着色器自动提供极相关数据。其余还可因而常量为着色器赋值,如统一变量,统一变量的用可以假如通过缓存或者默认统一变量闭包的方。另外在色器存储闭包可以用于在色器间接的数目互换。统一变量可以用来存储纹理、图像及缓存数据,在正在色器中可以直接指向纹理数据、图像数据与缓存举行读写操作。纹理数据好抽取部分并以该卷入称为此外一种不同格式和品种的纹理数据镜像使用。此外原子操作可以包现代赛并发GPU安全访问数。

 

Byte

参数内容维数(0-3)

 

Byte[]

参数每一样维大小(倘若维数为0,就一直不这么些部分)

 

Byte[] 

参数实际内容

 

Byte

参数组描述长度

从此将来的始末

此处用征的虽是,由于参数可以存放数组,所以多了维数的标识,即当维数为0时,存放的情吧Char、Byte、Int16、Single等转移出的字节数组;而当维数为1不时,存放的吧Char[]、Byte[]、Int16[]、Single[]相当于转移出的字节数组,以此类推。而对数组的囤,其实就是数组每个元素依次举办仓储,而对于多维数组,则是按行优先开展仓储的,比如三维数组,先囤Data[0,0,1]再存储Data[0,0,2],依次类推。

但用说明的凡,对于Char[]以及Char[,]立马半栽,如若表示的话语实际应当相应之是String以及String[]。

 

【四、C3D文件数量区域之结构】

C3D数据区域为帧为单位寄放的,其实一定给这么些区域便是一个帧的聚众。而C3D帧其实分为两栽,一栽是整数幅,而此外一样种植是浮点帧。这二者的别在,前者存储的有着情节依然Int16,而后人则也Single,除此之外,前者的3D坐标点(X、Y、Z)还索要倍加比(加比(Gaby))例因子才方可,而后者存储的内容卓殊给已经乘机以了百分比因子了。

多少区域最先于参数集合中的”POINT”参数组吃之”DATA_START”参数,其代表数据区域开首之Section
ID,除此之外,在文书头着吗时有发生同等份副本。但是以官方的说教,假如文件头与参数集合中还有些内容,优先读取参数集合中之数据。

于每个帧,又饱含多少个组成部分,第一有也3D坐标点部分,第二局部吗仿效采样部分。

  • 对每帧的3D坐标点部分,存储在该帧所有3D坐标点的数码,每个3D坐标点包括4单Int16要么Single数据,分别是X坐标、Y坐标、Z坐标以及Residual和Camera
    Mask,其中Residual和Camera
    Mask共占用一个Int16。相比好玩的是,对于浮点帧,Residual和Camera
    Mask如故为或一个Int16,只可是存储的早晚要拿相应的数值转换为Single再拓展仓储。

    • 对此浮点帧,存储的X、Y、Z坐标就是其实的坐标;而对整数幅,存储的X、Y、Z的坐标还欲倍Gaby例因子才得,比例因子存储于参数集合中的”POINT”参数组被之”SCALE”参数。
    • Residual和Camera
      Mask共占一个Int16,将这易为字节数组之后,高位字节(第1个字节)的嵩位代表Residual的记号,即表示该坐标点是否中,如若也0虽象征中,倘使为1则代表无效,而剩余的7个字节则为Camera
      Mask,每一样各项代表一个录像机,从低到高位分别表示7独摄像机是否利用(为1吧运用,为0吧免用)。而Residual的真正数据则也字节数组的第0许节乘以比例因子(浮点帧则为比例因子的相对值)。
  • 即使仿照采样部分,则存储着该帧所有的学采样的数,可是每个帧可能带有多独拟采样,同时每个模仿采样可能而带有多单channel,存储的多少就为该channel下记录的多寡。不过存储的多寡及事实上的数据还亟需基于下述公式举行折算,其中data
    value为存储的数,real world value为实际的数目。

    • zero
      offset能够于”ANALOG”参数组吃的”OFFSET”中取,该多少为Int16之累组,第i各项拄的便是第i单channel的zero
      offset。
    • channel
      scale可以于”ANALOG”参数组吃的”SCALE”中得到,该数额为Single的再三组,第i各种拄的哪怕是dii个channel的scale。
    • general
      scale是怀有拟采样都要倍加的比重,该多少好从”ANALOG”参数组被之”GEN_SCALE”中获取,为Single。

    real world value = (data value – zero offset) channel scale general scale

 

【五、使用C3D.NET读写文件示例】

眼前说了这样多,其实只要由此C3D.NET来分析的言语实际是分外简单的。我们可由https://c3d.codeplex.com/下载C3D.NET之二进制文件或者源码,引用后根本的好像都于C3D这些命名空间下。

对遍历所有的3D坐标可以下以下的法门,首先得于文本或者从流中创造C3D文件,然后打文本头挨读取存储的第1轴的序号,然后读取采样点的多寡就是可了,当然也得不从参数组被读取,直接以file.AllFrames[i].Point3Ds.Length也可以:

 1 C3DFile file = C3DFile.LoadFromFile("文件路径");
 2 Int16 firstFrameIndex = file.Header.FirstFrameIndex;
 3 Int16 pointCount = file.Parameters["POINT:USED"].GetData<Int16>();
 4 
 5 for (Int16 i = 0; i < file.AllFrames.Count; i++)
 6 {
 7     for (Int16 j = 0; j < pointCount; j++)
 8     {
 9         Console.WriteLine("Frame {0} : X = {1}, Y = {2}, Z = {3}",
10             firstFrameIndex + i,
11             file.AllFrames[i].Point3Ds[j].X,
12             file.AllFrames[i].Point3Ds[j].Y ,
13             file.AllFrames[i].Point3Ds[j].Z);
14     }
15 }

若果读取模拟采样的话,采纳的不二法门也类似:

 1 Single frameRate = file.Parameters["POINT", "RATE"].GetData<Single>();
 2 Int16 analogChannelCount = file.Parameters["ANALOG", "USED"].GetData<Int16>();
 3 Int16 analogSamplesPerFrame = (Int16)(file.Parameters["ANALOG", "RATE"].GetData<Int16>() / frameRate);
 4 
 5 for (Int16 i = 0; i < file.AllFrames.Count; i++)
 6 {
 7     for (Int16 j = 0; j < analogChannelCount; j++)
 8     {
 9         for (Int16 k = 0; k < analogSamplesPerFrame; k++)
10         {
11             Console.WriteLine("Frame {0}, Sample {1} : {2}",
12                 firstFrameIndex + i, j + 1,
13                 file.AllFrames[i].AnalogSamples[j][k]);
14         }
15     }
16 }

而外五回性将C3D文件内容总体读取出来的这种措施外,还好使用C3D里德r来一帧一帧的读取。

 1 using (FileStream fs = new FileStream("文件路径", FileMode.Open, FileAccess.Read))
 2 {
 3     C3DReader reader = new C3DReader(fs);
 4     C3DHeader header = reader.ReadHeader();
 5     C3DParameterDictionary dictionary = reader.ReadParameters();
 6     Int32 index = header.FirstFrameIndex;
 7 
 8     while (true)
 9     {
10         C3DFrame frame = reader.ReadNextFrame(dictionary);
11 
12         if (frame == null)
13         {
14             break;
15         }
16 
17         for (Int16 j = 0; j < frame.Point3Ds.Length; j++)
18         {
19             Console.WriteLine("Frame {0} : X = {1}, Y = {2}, Z = {3}",
20                 index++,
21                 frame.Point3Ds[j].X,
22                 frame.Point3Ds[j].Y,
23                 frame.Point3Ds[j].Z);
24         }
25     }
26 }

对此开创一个C3D文件,只需要动用C3DFile.Create()就好创建一个缺损的C3D文件的,不分包其他的参数集合。而保存C3D文件则一向行使file.SaveTo(“文件路径”)就足以了。

于增长参数集合好接纳以下的代码:

1 //首先需要添加参数集合,ID为正数
2 file.Parameters.AddGroup(1, "POINT", "");
3 //然后往指定ID的参数集合中添加参数即可
4 file.Parameters[1].Add("USED", "").SetData<Int16>(5);

补给加帧可以应用如下的代码:

1 file.AllFrames.Add(new C3DFrame(new C3DPoint3DData[] {
2     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
3     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
4     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
5     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
6     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask} }));

本,也得以C3DPoint3DData数组换成C3DAnalog山姆ples数组,或者两者又添加也可。

 

【相关链接】

  1. C3D.ORG:http://www.c3d.org/
  2. c3d4sharp – C3D File reading/writing tools written in
    C#:http://code.google.com/p/c3d4sharp/
  3. C3D.NET:https://c3d.codeplex.com/

相关文章