OpenGL ES渲染图片纹理
1、步骤:
-
1、编写着色器(顶点着色器和片元着色器)
-
2、设置顶点、纹理坐标
-
3、加载着色器
-
4、创建纹理
-
5、渲染图片
2、OpenGL ES坐标系
首先介绍一下纹理映射时的坐标系,纹理映射的坐标系和顶点着色器的坐标系是不一样的。
2.1、顶点坐标系
顶点坐标系控制窗口坐标
2.2、纹理坐标系
控制纹理位置
纹理坐标用浮点数来表示,范围一般从0.0到1.0,左上角坐标为(0.0,0.0),右上角坐标为(1.0,0.0),左下角坐标为(0.0,1.0),右下角坐标为(1.0,1.0),具体如下:
纹理坐标系
3、OpenGL ES绘制四边形
将纹理映射到下边的两个三角形上(也就是一个矩形),需要将纹理坐标指定到正确的顶点上,才能使纹理正确的显示,否则显示出来的纹理会无法显示,或者出现旋转、翻转、错位等情况。规定:图形环绕方向必须一致!
顶点坐标系
1、GL_TRIANGLES:
v1, v2, v3,
v3, v2, v4,
2、GL_TRIANGLE_STRIP:
偶数:n-1, n-2, n
奇数:n-2, n-1, n
3.1、绘制坐标范围:
float[] vertexData = {
-1f, -1f,
1f, -1f,
-1f, 1f,
1f, 1f};
3.2、为坐标分配本地内存地址:
FloatBuffer vertexBuffer;
vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);
3.3、Shader编写
3.3.1、顶点着色器:vertex_shader.glsl
attribute vec4 v_Position;
attribute vec2 f_Position;
varying vec2 ft_Position;
void main() {
ft_Position = f_Position;
gl_Position = v_Position;
}
注: attribute 只能在vertex中使用;varying 用于vertex和fragment之间传递值。
3.3.2、片元着色器:fragment_shader.glsl
precision mediump float;
varying vec2 ft_Position;
uniform sampler2D sTexture;
void main() {
gl_FragColor=texture2D(sTexture, ft_Position);
}
注: uniform 用于在application中向vertex和fragment中传递值。
3.4、OpenGL ES加载Shader
1、创建shader(着色器:顶点或片元)
int shader = GLES20.glCreateShader(shaderType);
2、加载shader源码并编译shader
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
3、检查是否编译成功:
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
4、创建一个渲染程序:
int program = GLES20.glCreateProgram();
5、将着色器程序添加到渲染程序中:
GLES20.glAttachShader(program, vertexShader);
6、链接源程序:
GLES20.glLinkProgram(program);
前面6个部分可以用一个工具类完成,步骤都是一样的
7、检查链接源程序是否成功
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
8、得到着色器中的属性:
int vPosition = GLES20.glGetAttribLocation(program, "v_Position");
9、使用源程序:
GLES20.glUseProgram(program);
10、使顶点属性数组有效:
GLES20.glEnableVertexAttribArray(vPosition);
11、为顶点属性赋值:
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,vertexBuffer);
12、绘制图形:
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
3.5、OpenGL ES绘制纹理过程
1、加载shader和生成program过程不变
如3.4
2、创建和绑定纹理:
GLES20.glGenTextures(1, textureId, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureid);
3、设置环绕和过滤方式
环绕(超出纹理坐标范围):(s==x t==y GL_REPEAT 重复)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
过滤(纹理像素映射到坐标点):(缩小、放大:GL_LINEAR线性)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
4、设置图片(bitmap)
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
5、绑定顶点坐标和纹理坐标
6、绘制图形
4、实现
4.1、加载shader、创建program(前6步)
import android.content.Context;
import android.opengl.GLES20;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class JfShaderUtil {
/**
* 得到shade代码
* @param context
* @param rawId
* @return
*/
public static String getRawResource(Context context,int rawId){
InputStream inputStream = context.getResources().openRawResource(rawId);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer sb = new StringBuffer();
String line;
try {
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
/**
* 加载着色器源码
* @param shadeType 顶点、片元
* @param source
* @return
*/
public static int loadShader(int shadeType,String source){
//1、创建shader(着色器:顶点或片元)
int shader = GLES20.glCreateShader(shadeType);
if (shader != 0) {
//2、加载shader源码并编译shader
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
//3、检查是否编译成功
int[] compile = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS,compile,0);
if (compile[0] != GLES20.GL_TRUE) {
Log.e("zjf","编译失败");
GLES20.glDeleteShader(shader);
shader = 0;
}
return shader;
} else {
return 0;
}
}
/**
* 创建渲染程序
* @param vertexSource
* @param fragmentSoruce
* @return
*/
public static int createProgram(String vertexSource, String fragmentSoruce){
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,vertexSource);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentSoruce);
if (vertexShader != 0 && fragmentShader != 0) {
//4、创建一个渲染程序
int program = GLES20.glCreateProgram();
//5、将着色器程序添加到渲染程序中
GLES20.glAttachShader(program,vertexShader);
GLES20.glAttachShader(program,fragmentShader);
//6、链接源程序
GLES20.glLinkProgram(program);
return program;
}
return 0;
}
}
4.2、渲染纹理
public class JfTextureRender implements JfEGLSurfaceView.JfGLRender {
private Context context;
//顶点坐标
private float[] vertexData = {
-1f,-1f,
1f,-1f,
-1f,1f,
1f,1f
};
private FloatBuffer vertexBuffer;
//纹理坐标
private float[] fragmentData = {
0f,1f,
1f,1f,
0f,0f,
1f,0f
};
private FloatBuffer fragmentBuffer;
private int program;//程序
private int vPosition;//顶点着色器属性
private int fPosition;//片元着色器属性
private int textureId;
private int sampler;
public JfTextureRender(Context context){
this.context = context;
vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);
fragmentBuffer = ByteBuffer.allocateDirect(fragmentData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(fragmentData);
fragmentBuffer.position(0);
}
@Override
public void onSurfaceCreated() {
//获取源码
String vertexSource = JfShaderUtil.getRawResource(context,R.raw.vertex_shader);
String fragmentSource = JfShaderUtil.getRawResource(context,R.raw.fragment_shader);
//前6步完成
program = JfShaderUtil.createProgram(vertexSource,fragmentSource);
//得到着色器中的属性
vPosition = GLES20.glGetAttribLocation(program,"v_Position");
fPosition = GLES20.glGetAttribLocation(program,"f_Position");
sampler = GLES20.glGetUniformLocation(program,"sTexture");
//开始绘制纹理,只绘制一个图片纹理
int[] textureIds = new int[1];
GLES20.glGenTextures(1,textureIds,0);//创建纹理
textureId = textureIds[0];
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureId);//绑定纹理
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glUniform1i(sampler,0);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),R.mipmap.fengjing);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D,0,bitmap,0);
bitmap.recycle();
bitmap = null;
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0);//解绑纹理
}
@Override
public void onSurfaceChanged(int width, int height) {
GLES20.glViewport(0,0,width,height);
}
@Override
public void onDrawFrame() {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1f,0f,0f,1f);
//使用源程序
GLES20.glUseProgram(program);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureId);//再次绑定纹理
//使顶点属性数组有效
GLES20.glEnableVertexAttribArray(vPosition);
//为顶点属性赋值
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,
vertexBuffer);
GLES20.glEnableVertexAttribArray(fPosition);
GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8,
fragmentBuffer);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0);//再次解绑纹理
}
}