OpenGL 从入门到成魔-第7章-纹理和纹理坐标

OpenGL 从入门到成魔-第7章-纹理和纹理坐标,第1张

注:参考自bilibili系列视频,OpenGL 从入门到成魔-第7章-纹理和纹理坐标,更详细的内容可以从视频获取 >

您好,在Unity中,您可以使用[Sampler]标签来指定插值方式,比如[Sampler] _MainTex(linear),表示将使用线性插值方式处理_MainTex纹理中的像素。另外,在Unity中您还可以使用[Filter]标签来指定纹理的过滤模式,比如[Filter] _MainTex(Point),表示将使用点过滤模式来处理_MainTex纹理中的像素。

格式,如 jpg/png 等

纹理格式,如 ETC1/ETC2/PVRTC/ASTC等

电脑硬盘上的文件用来保存图形或图像的信息,包括大小、颜色等。的格式很多,但总体上可以分为 点阵图 和矢量图两种。

常见的位图格式:

考虑无压缩的 bmp 格式位图,文件在硬盘上的大小和以下几个因素有关

文件被加载到内存后,被转换为显卡能够识别的 纹理 提交到显存中,供 GPU 进行采样,纹理采样是根据 uv 坐标获得对应纹理中 纹素 颜色值的过程。

不同的显卡和图形API对纹理格式的支持不一样,RGBA32被显卡和图形API完全支持,DirectX特有的DXTx只有微软的部分设备支持,Android设备广泛支持ETC系列,iOS则支持 PVRTC 系列。

CPU将从硬盘加载到内存中,进行解码得到 RGBA32 格式位图,然后在 CPU 端进行编码,得到GPU支持的纹理格式,提交到显存供 GPU 采样访问。

这个过程中需要进行解码、重新编码,非常消耗 CPU,为了减少 CPU 的消耗,因此可以把预先转换成某种纹理格式放到硬盘上,CPU 加载后不需要解码和重新编码,直接提交给 GPU。大大节省了纹理从加载到使用的性能。

在Unity中,任何文件格式都存在一个导入过程,导入后的文件格式都是Texture2D,在Texture2D的导入设置选项中需要针对不同平台设置纹理压缩格式,在构建时,会直接将转换后的纹理格式构建到安装包或 assetbundle 中。

使用纹理压缩格式的好处

GPU 通常并行处理若干数据,无法预测纹理中纹素被访问的先后顺序,因此纹理格式必须支持GPU的 随机访问

几乎所有的纹理压缩算法都 以块为单位 压缩和存储纹素,可以理解为,每个纹素块(通常为4x4)占用的字节数是确定的,所以当我们需要访问某一个坐标的纹素时,可以明确得到该纹素所在块的数据,GPU纹理采样单元针对该块进行解压,读取对应纹素数据即完成采样。

而jpeg/png这类压缩算法考虑了的整体数据,像素数据之间互相依赖,无法针对其中某一块进行解压,必须全部解压才能正确访问纹素,所以不适宜作为纹理格式。

纹理压缩算法几乎都是有损压缩,通常情况下有限度的信息损失换到的性能上的提升是值得的。

如果使用了硬件不支持的纹理压缩格式,Unity在运行时会对纹理进行解压所到无压缩格式,而这种无压缩格式只是格式上是无压缩的,但是因为原始数据是有损压缩,所以视觉上纹理精度和压缩格式是一致的,因此单从视觉上很难察觉,而且占用的内存是双份纹理的开销,而带宽开销是无压缩格式的纹理大小所占用的开销。

例如:ETC格式在PC端Dx11的显卡是不支持的,如果使用,开销计算如下

1024 x 1024 ETC2 8bit的格式在PC端,会产生5M的内存开销(1M压缩的原图+4M解压出来的RGBA32bit图)

1024 x 1024 ETC 4bit格式在PC端,会产生45M的内存开销(05M压缩的原图+4M解压出来的RGBA32bit图)

而如果贴图格式在目标设备上支持,则不会发生内存中的解压缩 *** 作,压缩过的图大小是多少,在内存中的设备上大小就是多少:

1024 x 1024 ETC2 8bit的格式在Android端,会产生1M的内存开销(1M压缩的原图)

1024 x 1024 ETC 4bit格式在Android端,会产生05M的内存开销(05M压缩的原图)

纹理压缩是一次性的工作,例如 Unity 在导入资源时会有一段转换时间,被处理成对应的纹理格式后存储了下来,此时纹理压缩的速度慢一些是可以接受的

GPU 对纹理进行采样时,通常只会解压一个纹素块,不会全部解压,这个过程本身就是很快的

无论纹理坐标是否单位化,缺省条件下(默认)纹理引用使用浮点坐标,范围[0,N-1],N是纹理在相应维度的大小(因为可以进行采样到两个纹理坐标的中间值,所以变化范围是浮点型的)。例如,纹理大小为6432,则引用的坐标就是x和y维在[0,63]和[0,31]上的值,单位化的纹理坐标就是纹理索引在[00,10-1/N]而不是[0,N-1],所以6432的数据纹理的横纵坐标索引都可以用[00,1-1/N]来进行索引。

fetch的时候越界是合法的,默认会把非单位化的坐标进行压缩到[0,N),单位化的压缩到[0,10)。如果设定边界(border)模式,则越界的会返回0,对于单位化的坐标,有warp modemirror mode,warp mode时,假设如果采样点为x(如11),则采样值为xfloor(x),这里的floor(x)是比x小的最大的整数值,这里为1。Mirror模式中,floor(x)为偶数,则采样值为xfloor(x),如果为奇数,则采样值为1-xfloor(x)。

这是用CUDA从底层进行采样做的。OPENGL中就简单了,直接按照浮点从单位化的[0,1]采样就可以了。本人图形学专业,纯手打望采纳。

利用GLSL自定义的着色去加载一张,效果图如下

整体流程图如下

流程中主要分为4个模块

项目的创建及自定义视图创建等,这里不作过多说明,主要说说着色器文件是如何创建的

自定义的着色器本质上其实是一个字符串,且在Xcode中编写时,是没有任何提示的,所以需要格外仔细!

顶点着色器

片与着色器

初始化主要分为4部分

setupLayer函数:创建图层

layer主要是用于显示OpenGL ES绘制内容的载体

setupContext函数:创建上下文

上下文主要是用于保存 OpenGL ES 中的状态,是一个状态机,不论是 GLKit 还是 GLSL ,都是需要 context 的,主要创建流程如下

deleteRenderAndFrameBuffer函数:清理缓存区

清理缓冲区的目的在于清除残留数据,防止残留数据对本次 *** 作造成影响

需要清空两个缓存区: RenderBuffer和FrameBuffer

设置RenderBUffe & FrameBuffer

首先了解一下RenderBuffer和FrameBuffer

两者间的关系如下图:

setupRenderBuffer函数

主要是创建 RenderBufferID 并申请标识符,将标识符绑定至 GL_RENDERBUFFER ,并且将 layer 的相关存储绑定到 RenderBuffer 对象

setupFrameBuffer函数

主要是创建 FrameBuffer 的ID并申请标识符,将标识符绑定至 GL_FRAMEBUFFER ,然后将 RenderBuffer 通过 glFramebufferRenderbuffer 函数绑定到 FrameBuffer 中的 GL_COLOR_ATTACHMENT0 附着点上,通过 FrameBuffer 来管理 RenderBuffer , RenderBuffer 存储相关数据到相应缓存区

绘制的整体流程图所示:

主要包含5部分

初始化

需要注意的是,需要将视口的大小设置为与屏幕大小一致

GLSL自定义着色器加载

自定义着色器的加载主要分为以下几步

读取自定义着色器

读取自定义着色器文件的前提是需要获得文件的路径,将其传入 loadShaders 函数进行加载

loadShaders函数 & compileShader函数

loadShaders 函数:分别将顶点着色器和片元着色器编译完成后,并返回着色器对应的 ID ,然后通过 glAttachShader 函数将顶点和片元的 shader 分别附着到 program 上,然后释放不再使用的 shader ,并赋值给全局的 program

链接program

使用program

通过 glUseProgram 函数来使用链接成功的 program

顶点数据设置及处理

通过数组存储顶点数据,并将顶点坐标和纹理坐标读取到自定义的顶点着色器中

分为以下三步

开辟顶点缓存区

开启顶点缓存区,这部分其实跟之前使用 GLKit 框架开启缓存区步骤是一致的,没什么变化,有以下四步

打开顶点/片元的通道

在 iOS 中, attribute 通道默认是关闭的,需要手动开启,而数据有顶点坐标和纹理坐标两种,需要分别开启两次,这里的开启与 GLKit 框架中是有所区别的,

使用自定义着色器打开通道,一般有以下三步(相对于 GLKit 而言,只多了一个获取入口的步骤,后面两步是没有多大变化的)

加载纹理

这部分的内容主要是将 png/jpg 解压成位图,并通过自定义着色器读取纹理每个像素点的纹素,包含两部分

setupTexture函数

将 png/jpg 解压成位图,加载成纹理数据,其中纹理的解压缩使用的都是 CoreGraphic ,加载纹理的流程如下图

设置纹理采样器

主要是获取纹理中对应像素点的的颜色值,即纹素

绘制

开始绘制,存储到 RenderBuffer ,从 RenderBuffer 将显示到屏幕上

调用 glDrawArrays 函数指定图元连接方式进行绘制

以上就是关于OpenGL 从入门到成魔-第7章-纹理和纹理坐标全部的内容,包括:OpenGL 从入门到成魔-第7章-纹理和纹理坐标、OpenGL ES实践教程(八)blend混合与shader混合、unityshader指定插值方式等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/9603826.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-30
下一篇2023-04-30

发表评论

登录后才能评论

评论列表(0条)

    保存