彻底理解OPENGLES 纹理是如何映射的

纹理,就是通过一系列表示颜色的数据,调用对应的GL函数,把绘制出来的几何图形,填充纹理数据所携带的颜色。

比如绘制了两个三角形组成的矩形,没有使用纹理的时候
彻底理解OPENGLES 纹理是如何映射的_第1张图片
是使用常量颜色填充的。

当使用纹理的时候
彻底理解OPENGLES 纹理是如何映射的_第2张图片
如何实现,可以通过《OPENGL ES 3.0 编程指南》第九章的学习和样例代码
的编译运行,已经有一定了解。

但纹理是查看过代码,对如何映射到绘制的图形,纹理坐标与顶点的关系,可能还是一头雾水。

比如,纹理数据保存了9个颜色值(原案例是4个颜色,这里加深理解进行了修改)

        byte[] pixels =
                {
                        0, (byte) 0xff, 0, // Green
                        0, 0, (byte) 0xff, // Blue
                        (byte) 0xff, 0, 0, // Red
                        (byte) 0xff, 0, 0, // Red
                        0, (byte) 0xff, 0, // Green
                        (byte) 0xff, (byte) 0xff, 0,// Yellow
                        0, 0, (byte) 0xff, // Blue
                        (byte) 0xff, (byte) 0xff, 0,// Yellow
                        (byte) 0xff, 0, 0 // Red

                };

绿色,蓝色,红色,红色,绿色,黄色,蓝色,黄色,红色
为什么通过顶点和片段着色器进行,最终结果是这样的
彻底理解OPENGLES 纹理是如何映射的_第3张图片
而不是

彻底理解OPENGLES 纹理是如何映射的_第4张图片
或者
彻底理解OPENGLES 纹理是如何映射的_第5张图片

的呢。

这就要研究一下OPENGL纹理的存储方式和纹理坐标与顶点映射的关系了
这里只涉及最简单的情况,不涉及压缩纹理。

纹理坐标,是指一个坐标系,通过坐标系,可以查询出纹理的值

以简单2维纹理来说,(3维纹理也是2维纹理进行的扩展)

2维纹理坐标,左下角为原点,右向为+s轴,向上为+t轴

在坐标的s t坐标的0 - 1范围内保存纹理。

比如通过

        GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGB, 3, 3, 0, GLES30.GL_RGB, GLES30.GL_UNSIGNED_BYTE, pixelBuffer);

加载了一个3x3的纹理,纹理数据就是上面的9个颜色值。

则这时候纹理在纹理坐标中的表示为

彻底理解OPENGLES 纹理是如何映射的_第6张图片
注意,坐标轴最大值均为 1

纹理坐标0,0处的颜色数据为 绿色
纹理坐标0,1处的颜色数据为 红色
纹理坐标1,0处的颜色数据为 蓝色
纹理坐标1,1处的颜色数据为 红色

因为已经通过顶点绘制了一个矩形,若要把这些颜色填充给这个矩形,就需要通过设置纹理坐标与每个顶点的对应关系了。

    private final float[] mVerticesData =
            {
                    -0.5f, 0.5f, 0.0f, // Position 0
                    0.0f, 0.0f, // TexCoord 0
                    -0.5f, -0.5f, 0.0f, // Position 1
                    0.0f, 1.0f, // TexCoord 1
                    0.5f, -0.5f, 0.0f, // Position 2
                    1.0f, 1.0f, // TexCoord 2
                    0.5f, 0.5f, 0.0f, // Position 3
                    1.0f, 0.0f // TexCoord 3
            };

通过顶点属性可知
彻底理解OPENGLES 纹理是如何映射的_第7张图片

编号为0 的顶点 是左上角的,编号为1 的顶点,是左下角的,编号为 2 的顶点,是右下角的,编号为 3 的顶点,是右上角的。

图中,坐标系为OPENGL顶点坐标系。

在这里插入图片描述
程序里,
将索引为0的顶点绑定的纹理坐标 0,0
也即左上角的顶点,使用0,0纹理坐标对应的颜色值
彻底理解OPENGLES 纹理是如何映射的_第8张图片
接下来,
在这里插入图片描述
将索引为1的顶点绑定的纹理坐标 0,1
也即左下角的顶点,使用0,1纹理坐标对应的颜色值
彻底理解OPENGLES 纹理是如何映射的_第9张图片
接下来,在这里插入图片描述
将索引为2的顶点绑定的纹理坐标 1,1
也即右下角的顶点,使用1,1纹理坐标对应的颜色值
彻底理解OPENGLES 纹理是如何映射的_第10张图片
接下来,在这里插入图片描述
将索引为3的顶点绑定的纹理坐标 1,0
也即右上角的顶点,使用1,0纹理坐标对应的颜色值
彻底理解OPENGLES 纹理是如何映射的_第11张图片
到这里,各顶点的纹理坐标都已经设置好了,中间的纹理颜色值会根据插值自动映射。

完整的映射如下图
彻底理解OPENGLES 纹理是如何映射的_第12张图片
这样,通过逐顶点指定纹理坐标的方式,整张纹理就可以映射到绘制的图形上了。

当然,纹理保存在纹理坐标的0-1的区间内,
为顶点指定纹理坐标,可以指定区间以外的值,比如指定(0,2)这样的纹理坐标

如下

    private final float[] mVerticesData =
            {
                    -0.5f, 0.5f, 0.0f, // Position 0
                    0.0f, 0.0f, // TexCoord 0
                    -0.5f, -0.5f, 0.0f, // Position 1
                    0.0f, 2.0f, // TexCoord 1
                    0.5f, -0.5f, 0.0f, // Position 2
                    2.0f, 2.0f, // TexCoord 2
                    0.5f, 0.5f, 0.0f, // Position 3
                    2.0f, 0.0f // TexCoord 3
            };

这里,每个顶点指定的纹理坐标范围,扩大到了2*2的区域。这时候纹理应用的效果如下
彻底理解OPENGLES 纹理是如何映射的_第13张图片
可以看到,因为纹理坐标范围s,t指定的坐标值扩大了一倍,完整纹理映射了1/4的区域就已经完成了映射,超出纹理坐标的区域将纹理进行了重复,这个行为可以通过

GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_REPEAT);

来设置超出纹理范围时的行为,当前设置的是从重复,纹理坐标会再从起始开始映射。

又如设置s方向的超出纹理坐标范围时,使用纹理坐标边界数据来处理,则代码为

GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);

实现的效果如下图
彻底理解OPENGLES 纹理是如何映射的_第14张图片

综上,对OPENGL的纹理处理方式进行了初步的解读,希望能帮助大家理解如何加载纹理。

纹理应用很广泛,比如使用OPENGL 播放视频,是将视频帧数据作为纹理数据来设置到OPENGL绘制的形状上,如果不了解纹理坐标的工作方式,可能会出现视频影像倒置等BUG。

希望感兴趣的同学进行交流。真心推荐这个开源demo
是经典教程书籍《OPENGL ES 3.0 编程指南》配套的代码。自己下载下来编译运行一下,对这本书的理解会起到关键性的作用。

上述的项目包含整个OpenGL ES 3.0编程指南的例子,本次介绍的这个例子我也独立出来了一个demo工程放到了gtihub上,可以在AS上编译。

你可能感兴趣的:(OpenglES,Android)