OpenGL 中的Texture(纹理)
纹理介绍
纹理映射技术是构建一个真实3D世界最重要的方式。没有纹理映射的话,所以的东西都是光滑的渐变,看起来像人造的,像是90年代的控制台游戏
首先大量使用了纹理技术的游戏,比如Doom和Duke Nukem 3D,通过添加的视觉影响能极大的增强了游戏的真实性。
纹理坐标系
在OpenGL中,纹理坐标系使用(s,t)来代替(x,y)坐标,代表纹理上的点,最终映射到几何形上。另外需要注意的是纹理坐标系和其他的OpenGL坐标系一样,t(y)轴指向上,因此位置越高值越大。
纹理映射
在这课中注意看2D纹理(GL_TEXTURE_2D)。OpenGL ES 也提供了其他的纹理模型让你做不同和专业的效果。
顶点着色器
从之前博客中的顶点着色器,增加一些修改:
attribute vec2 a_TexCoordinate;
...
varying vec2 v_TexCoordinate;
...
//将纹理坐标信息传递给片段着色器
v_TexCoordinate = a_TexCoordinate;
在顶点着色器中我们添加了一个新的attribute类型2维向量来携带纹理的坐标信息作为输入数据。这会是基于每个顶点的,就像是位置,颜色,法线数据一样。我们还添加了一个新的varying型的变量来将数据传递进片段着色器中,通过三角形表面线性插入。
片段着色器
uniform sampler2D u_Texture;
...
varing vec2 v_TexCoordinate;
..
diffuse = diffuse * (1.0/(1.0+(0.1.*distance)));
...
diffuse = diffuse + 0.3;
gl_FragColor = (c_Color*diffuse*texture2D(u_Texture,v_TexCoordinate));
我们添加了一个新的uniform类型 sampler2D代表了实际的纹理数据(),v_TexCoordinate从顶点着色器中获取到了纹理坐标数据,之后我们调用texture2D(texture,textureCoordinate)来读出纹理在当前坐标处的值。获取到了这个值后将它与其它项相乘就得到了最后的输出颜色。这种方式添加的纹理会有时在整个环境中较暗,所以可以将环境光上调一些来减少光的衰减。
从一个图像中加载纹理
public static int loadTexture(Bitmap bitmap){
int[] textureHandle = new int[1];
//由OpenGL ES生成一个纹理句柄并存放到textureHandle中
GLES20.glGenTextures(1,textureHandle,0);
if(textureHandle[0]!=0){
//将纹理绑定到OpenGL上
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureHandle[0]);
// Set filtering
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Recycle the bitmap, since its data has been loaded into OpenGL.
bitmap.recycle();
}
if (textureHandle[0] == 0)
{
throw new RuntimeException("Error loading texture.");
}
return textureHandle[0];
}
传入一个Bitmap对象并将其加载到OpenGL中
定义纹理坐标
我们知道纹理坐标系左下是(0,0),而右上是(1,1),而我们的之前定义一个矩形是
-1,1,
-1,-1,
1,1,
-1,-1,
1,-1,
1,1
这样的顺序定义的,
我们的纹理坐标应该是怎么样的呢:
0f,0f,
0f,1f,
1f,0f,
0f,1f,
1f,1f,
1f,0f.
这个坐标的对应顺序可以自己在纸上画一下来增强理解.
(我个人理解和这个文章的作者不一样,在Android上,纹理坐标系好像是和Android的2d坐标系相同,Android的2D坐标系是y轴向下为正,x轴向右为正,坐标原点为左上,以左上角为(0,0),所以将纹理映射到OpenGL坐标系中就是上面这样)
使用纹理
在GLES20.glUseProgram之后添加下面的代码:
//纹理坐标句柄
mTextureHandle = GLES20.glGetAttribLocation(Program,"a_TexCoordinate");
//纹理采样器句柄
mTextureCoordHandle = GLES20.glGetUniformLocation(Program,"u_Texture");
//将纹理坐标传递进着色器程序
mTriangleTexture.position(0);
GLES20.glVertexAttribPointer(mTextureHandle,2 ,GLES20.GL_FLOAT,false,0,mTriangleTexture);
GLES20.glEnableVertexAttribArray(mTextureHandle);
//激活第一个纹理单元
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
//绑定纹理到指定的纹理通道,GL_TEXTURE_2D在着色器中就会使用simpler2D。
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,mTextureDataHandle);
//只使用一个纹理的话给Simpler2D传0就可以
GLES20.glUniform1i(mTextureCoordHandle,0);