初识:OpenGL es在Android中实战的使用【渲染一张图
2020-04-27 本文已影响0人
Antonylr
一.简单了解OpenGL es的基础知识
Opengl 渲染图片纹理基本步骤:[使用前要了解一下基础知识哦! 更多Opengl知识:https://recomm.cnblogs.com/blogpost/4472599]
1.编写着色器(顶点着色器Vertex Shader 和 片元着色器Fragment Shader)
2.设置顶点,纹理坐标
3.加载着色器
4.创建纹理
5.渲染图片
Opengl的坐标系统:
opengl坐标系统.png
二. Android中使用OpenGL基础实战,显示一张图片。
A. 编写顶点着色器 和 片元着色器
顶点着色器vertex_shader.glsl
attribute vec4 av_Position; //定义顶点坐标 向量vec4 代表x(横坐标) y(纵坐标) z(z坐标) w(焦距)
attribute vec2 af_Position; //定义纹理坐标 向量vec2
varying vec2 v_texPosition;
void main() {
v_texPosition = af_Position;
gl_Position = av_Position; //gl_Position是OpenGL中提供的变量,装载顶点坐标
}
//attribute 只能在Vertex Shader(顶点着色器)中使用, attribute表示一些顶点的数据,这些顶点数据包含了顶点坐标,法线,纹理坐标,顶点颜色等.
//attribute 变量来表示一些顶点的数据,如:顶点坐标,法线,纹理坐标,顶点颜色等。
//varying[易变量] 用于Vertex Shader(顶点着色器)和Fragment Shader(纹理着色器)之间传递值
片元着色器fragment_shader.glsl
precision mediump float;
varying vec2 v_texPosition;
uniform sampler2D sTexture;
void main() {
gl_FragColor=texture2D(sTexture, v_texPosition);
}
//uniform 变量一般用来表示:变换矩阵,材质,光照参数和颜色等信息。它可以在vertex和fragment共享使用。(相当于一个被vertex和fragment shader共享的全局变量)
把vertex_shader.glsl 和 fragment_shader.glsl放到安卓工程中的res文件夹下的raw资源文件中。
B. 编写Shader(着色器)工具类
1.获取 raw文件下的 .glsl文件,读取文件转成String方便后续使用
2.加载Shader(着色器)
3.创建 并 链接 源程序(program)
注意:OpenGL的使用过程代码中都已经注释,大体12步(1~12)
import android.content.Context;
import android.opengl.GLES20;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* 着色器Shader工具类
*/
public class ShaderUtil {
/**
* 获取 raw 文件下的 .glsl
*
* @param context 全局环境
* @param resId raw id
* @return
*/
public static String getRawResource(Context context, int resId) {
InputStream inputStream = context.getResources().openRawResource(resId);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder stringBuilder = new StringBuilder();
try {
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line).append("\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufferedReader.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return stringBuilder.toString();
}
/**
* 创建并加载Shader(着色器)
*
* @param shaderType shader类型
* @param source .glsl【以字符串的形式加载进入】
* @return 0 失败 >0 成功
*/
public static int loadShader(int shaderType, String source) {
//1.创建Shader(着色器:顶点着色器 或 片元着色器)
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
//2.加载shader.glsl{这里把.glsl的文件以String字符串的方式加载} 并编译shader
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
//3.检查是否编译成功
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] != GLES20.GL_TRUE) {
GLES20.glDeleteShader(shader);
shader = 0;
}
return shader;
} else {
return 0;
}
}
/**
* 创建 并 链接 源程序 program
* @param vertex 顶点
* @param fragment 纹理
* @return 0失败 >0成功
*/
public static int createProgram(String vertex, String fragment) {
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertex);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragment);
if (vertexShader != 0 && fragmentShader != 0) {
//4.创建一个渲染程序(源程序program)
int prgram = GLES20.glCreateProgram();
//5.将着色器程序添加到渲染程序中
GLES20.glAttachShader(prgram, vertexShader);
GLES20.glAttachShader(prgram, fragmentShader);
//6.链接源程序
GLES20.glLinkProgram(prgram);
return prgram;
} else {
return 0;
}
}
}
C.在GLSurfaceView.Renderer 中使用OpenGL
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import com.antony.cfav.R;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class AnRender implements GLSurfaceView.Renderer {
private Context mContext;
//顶点坐标系(-1, -1) (1, -1) (-1, 1) (1, 1)
private float[] vertexData = {
-1f, -1f,
1f, -1f,
-1f, 1f,
1f, 1f
};
//纹理坐标系
private float[] fragmentData = {
0f, 1f,
1f, 1f,
0f, 0f,
1f, 0f
};
private FloatBuffer vertexBuffer; //顶点buffer
private FloatBuffer fragmentBuffer; //纹理buffer
private int program; //源程序
private int vPosition; //顶点位置
private int fPosition; //纹理位置
private int textureId; //纹理的ID
private int sampler;
public AnRender(Context context) {
this.mContext = context;
//为顶点坐标 分配本地内存地址
//为什么要分配本地内存地址呢? 因为Opengl取顶点的时候,每一次都到内存中去取值,
// 所以这个内存在运行过程中是不允许被java虚拟机GC回收的,我们就要把它搞成本地(底层)不受虚拟机控制的这种顶点
vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4) //分配内存大小(分配了32个字节长度)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);
fragmentBuffer = ByteBuffer.allocateDirect(fragmentData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(fragmentData);
fragmentBuffer.position(0);
}
private void initRender() {
String vertexSource = ShaderUtil.getRawResource(mContext, R.raw.vertex_shader);
String fragmentSource = ShaderUtil.getRawResource(mContext, R.raw.fragment_shader);
program = ShaderUtil.createProgram(vertexSource, fragmentSource); //创建源程序 program
//7.得到着色器中的属性 todo:我们就从源程序中获取他的属性了
vPosition = GLES20.glGetAttribLocation(program, "av_Position"); //顶点的向量坐标 todo:一定要跟vertex_shader.glsl中的变量对应上
fPosition = GLES20.glGetAttribLocation(program, "af_Position"); //纹理的向量坐标
sampler = GLES20.glGetUniformLocation(program, "sTexture"); // sampler2D
//a.创建纹理
int[] textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0);
//b.绑定纹理
textureId = textureIds[0];
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
//c.激活纹理 (激活texture0)
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glUniform1i(sampler, 0);
//d.设置纹理 环绕和过滤方式
//todo: 环绕(超出纹理坐标范围):(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);
//todo: 过滤(纹理像素映射到坐标点):(缩小,放大: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);
//e.把bitmap这张图片映射到Opengl上
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.androids);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);//这里 textre=0 相当于解绑了
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//TODO: OpenGL es 加载Shader
initRender();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); //这是清屏
GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);//红色清屏
//8.使用源程序
GLES20.glUseProgram(program);
//9.绑定纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
//10.使顶点属性数组有效, 使纹理属性数组有效
GLES20.glEnableVertexAttribArray(vPosition);
GLES20.glEnableVertexAttribArray(fPosition);
//11.为顶点属性赋值 todo;就是把 vertexBuffer的数据给到 vPosition
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer);
//为片元属性赋值 todo: 把textureBuffer的数据给到 fPosition
GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8, fragmentBuffer);
//todo; 到这里 vertex_shader.glsl中的 "av_Position", "af_Position"就有数据了
//12.绘制图形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);//这里 textre=0 相当于解绑了
}
}
D. GLSurfaceView中加载 Renderer
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
public class AnGLSurfaceView extends GLSurfaceView {
public AnGLSurfaceView(Context context) {
this(context, null);
}
public AnGLSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
//AnRender implements GLSurfaceView.Renderer
AnRender render = new AnRender(context);
setEGLContextClientVersion(2);//通知默认的EGLContextFactory和EGLConfigChooser选择哪个EGLContext客户端版本。
setRenderer(render); setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
}
E.布局中使用我们自定义的AnGLSurfaceView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.opengl.OpenGlDemoActivity">
<com.antony.cfav.opengl.AnGLSurfaceView
android:id="@+id/an_gl_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
F.效果图如下:
效果图.png源码地址:https://github.com/YuLingRui/AntonyCFAV
如果对你有帮助,麻烦给个start。进步的道路上你我相互鼓励!