OpenGL 学习系列---纹理

OpenGL 学习系列文章

接下来探索纹理了。

纹理,简单的理解就是一副图像。而把一副图像映射到图形上的过程,叫做纹理映射

比如有如下图形和三角形,想要把图形中的一部分映射到三角形上。

结果就是这样的:

这就是纹理映射的一个小小例子。

基本原理

要注意到,OpenGL 绘制的物体是 3D 的,而纹理是 2D 的,那么纹理映射就是将 2D 的纹理映射到 3D 的物体上,可以想象成用一张纸裹着一个物体一样,不过要按照一定规律来。

OpenGL 中绘制的物体是有坐标系的,每个点都对应 x、y、z 坐标,而纹理也有着它的坐标,只要 3D 物体中的每个点都对应了 2D 纹理中的某个点,那么就可以把纹理映射到 3D 物体上去了。

纹理的坐标,叫做纹理坐标系。它的范围只有 $[0,0]$ 到 $[1,1]$ 。

它的坐标原点位于左下角,水平向右为 S 轴,竖直向上为 Y 轴。不论实际的纹理图片尺寸大小如何,横向、纵向坐标最大值都是 1 。

例如:实际图为 512 x 256 像素分辨率,则横向第 512 个像素对应纹理坐标为 1 ,纵向第 256 个像素对应纹理坐标为 1 。不过,纹理图最好是采用像素为 2 的 n 次方的纹理图。

纹理映射的基本思想就是:首先为图元中的每个顶点指定恰当的纹理坐标,然后通过纹理坐标在纹理图中可以确定选中的纹理区域,最后将选中纹理区域中的内容根据纹理坐标映射到指定的图元上。

纹理映射在 OpenGL 的渲染管线上的体现:在渲染管线中,先进行顶点着色器,绘制出物体的大致形状,之后会进行光栅化,将物体光栅化为许多片段组成,然后再进行片段着色器,将图形的每个片段进行着色。

那么就需要在 顶点着色器 中将纹理的坐标传入,在光栅化阶段,纹理坐标将根据 顶点着色器 对它的处理以及 片段和各顶点的位置关系 插值产生,然后才是将插值计算后的结果传入到片段着色器中。

着色器操作

相比直接绘制图形,使用纹理后,着色器也要改变了。

顶点着色器:

attribute vec4 a_Position;
attribute vec2 a_TextureCoordinates;
varying vec2 v_TextureCoordinates;
uniform mat4 u_ModelMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjectionMatrix;
uniform mat4 u_Matrix;

void main() {
    v_TextureCoordinates = a_TextureCoordinates ;
    gl_Position = u_ProjectionMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;
}

在顶点着色器中多了 v_TextureCoordinates 变量,它是 varying 类型,意思为可变类型,在光栅化处理时会对该变量进行处理,随后传入到片段着色器中。

片段着色器

precision mediump float;
uniform sampler2D u_TextureUnit;
varying vec2 v_TextureCoordinates;

void main(){
    // 未使用纹理的颜色赋值 : gl_FragColor = u_Color;
    gl_FragColor = texture2D(u_TextureUnit,v_TextureCoordinates);
}

v_TextureCoordinates1变量就是接受来自顶点着色器传的值,u_TextureUnit变量就是使用的采样器,类型是sampler2D

使用纹理后的片段着色器要使用 texture2D 函数给颜色赋值。

texture2D函数的作用就是采样,从纹理中采取像素赋值给 gl_FragColor变量,也就是最后的颜色。

上层代码

大致了解了着色器代码,接着就是上层的 Java 代码了。

和要创建一个 OpenGL ProgramId 类似,使用纹理也需要创建一个纹理 ID。

```java
/**
* 返回加载图像后的 OpenGl 纹理的 ID
* @param context
* @param resourceId
* @return
*/
public static int loadTexture(Context context, int resourceId) {
final int[] textureObjectIds = new int[1];
glGenTextures(1, textureObjectIds, 0);
if (textureObjectIds[0] == 0) {
Timber.d("Could not generate a new OpenGL texture object.");
return 0;
}

top Created with Sketch.