本文在上一篇文章的基礎上完成紋理的繪製。java
繪製紋理流程也不復雜:android
頂點着色器git
在 main() 方法中進行賦值。github
#version 300 es
layout (location = 0) in vec4 a_Position;
layout (location = 1) in vec2 a_texCoord;
out vec2 v_texCoord;
void main()
{
gl_Position = a_Position;
v_texCoord = a_texCoord;
}
複製代碼
片斷着色器數組
s_texture:紋理的採樣緩存
經過 texture() 方法將傳進來的紋理和座標進行差值採樣,輸出到顏色緩衝區。bash
#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout (location = 0) out vec4 outColor;
uniform sampler2D s_texture;
void main(){
outColor = texture(s_texture, v_texCoord);
}
複製代碼
opengl 的座標系是歸一化座標系,原點在屏幕中心,橫向是橫座標,縱向是縱座標,範圍都是[-1,1]ui
通常設備的屏幕座標系遠點在屏幕左上角,橫向是橫座標,縱向是縱座標,可是縱座標的正方向向下spa
紋理的座標系是左下角是原點,右上方是正方向,也是歸一化座標系,範圍是[0,1]code
因此紋理顯示到屏幕上是,紋理座標與定點座標的 y 座標方向是反着的。
//頂點,按逆時針順序排列
public static final float[] VERTEX = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f};
//紋理座標,(s,t),t座標方向和頂點y座標反着
public static final float[] TEXTURE_COORD = {
0.5f,0.0f,
0.0f,1.0f,
1.0f,1.0f
};
複製代碼
主要就是經過 glGenTextures
方法建立紋理對象
而後將圖片加載進來生成位圖,綁定到紋理對象上,別忘了設置放大和縮小的過濾模式
再使用 glBindTexture
方法綁定紋理到 opengl
經過 texImage2D 將位圖綁定到紋理上,glGenerateMipmap
建立 mip 貼圖
最後位圖的資源已經綁定並複製到紋理對象上了,因此 bitmap 對象就能夠釋放了,而且紋理也能夠從 opengl 解綁。
public static int loadTexture(int resId){
//建立紋理對象
int[] textureObjIds = new int[1];
//生成紋理:紋理數量、保存紋理的數組,數組偏移量
glGenTextures(1, textureObjIds,0);
if(textureObjIds[0] == 0){
throw new RuntimeException("建立紋理對象失敗");
}
//原尺寸加載位圖資源(禁止縮放)
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resId, options);
if (bitmap == null){
//刪除紋理對象
glDeleteTextures(1, textureObjIds, 0);
throw new RuntimeException("加載位圖失敗");
}
//綁定紋理到opengl
glBindTexture(GL_TEXTURE_2D, textureObjIds[0]);
//設置放大、縮小時的紋理過濾方式,必須設定,不然紋理全黑
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//將位圖加載到opengl中,並複製到當前綁定的紋理對象上
texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
//建立 mip 貼圖
glGenerateMipmap(GL_TEXTURE_2D);
//釋放bitmap資源(上面已經把bitmap的數據複製到紋理上了)
bitmap.recycle();
//解綁當前紋理,防止其餘地方之外改變該紋理
glBindTexture(GL_TEXTURE_2D, 0);
//返回紋理對象
return textureObjIds[0];
}
複製代碼
流程基本和以前文章的繪製流程相同,具體的細節下面代碼中都有很詳細的註釋。
public void render(Surface surface, int width, int height){
//建立屏幕上渲染區域:EGL窗口
int[] surfaceAttribList = {EGL_NONE};
EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, surface, surfaceAttribList, 0);
//指定當前上下文
eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
//獲取着色器
int texVertexShader = loadShader(GL_VERTEX_SHADER, loadShaderSource(R.raw.texture_vertex_shader));
int texFragmentShader = loadShader(GL_FRAGMENT_SHADER, loadShaderSource(R.raw.texture_fragtment_shader));
//建立並鏈接程序
int program = createAndLinkProgram(texVertexShader, texFragmentShader);
//設置清除渲染時的顏色
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
//設置視口
glViewport(0, 0, width, height);
//獲取頂點、紋理座標數據
FloatBuffer vertexBuffer = getVertextBuffer();
FloatBuffer texCoordBuffer = getTextureCoordBuffer();
//擦除屏幕
glClear(GL_COLOR_BUFFER_BIT);
//使用程序
glUseProgram(program);
//綁定頂點、紋理座標到指定屬性位置
int aPosition = glGetAttribLocation(program, "a_Position");
int aTexCoord = glGetAttribLocation(program, "a_texCoord");
glVertexAttribPointer(aPosition,3,GL_FLOAT,false,0,vertexBuffer);
glVertexAttribPointer(aTexCoord, 2, GL_FLOAT, false, 0, texCoordBuffer);
glEnableVertexAttribArray(aPosition);
glEnableVertexAttribArray(aTexCoord);
//綁定紋理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, loadTexture(R.drawable.texture));
//Set the sampler texture unit to 0
glUniform1i(glGetUniformLocation(program, "s_texture"),0);
//繪製
glDrawArrays(GL_TRIANGLES,0,3);
//交換 surface 和顯示器緩存
eglSwapBuffers(eglDisplay, eglSurface);
//釋放
eglDestroySurface(eglDisplay, eglSurface);
}
複製代碼
本文在前文的基礎上,梳理了具體的紋理操做方法,給繪製的圖形貼上一張圖片紋理,整個的流程和前文差很少。
須要注意就是紋理到頂點的座標匹配時的 y 方向,以正常顯示紋理。