OpenGL ES是OpenGL的一個子集,它針對移動端或嵌入式系統作了部分精簡,而Android系統中集成了OpenGL ES,方便咱們經過其接口充分使用GPU的計算和渲染能力。android
OpenGL ES2.0是基於可編程管線設計。相對OpenGL ES 1.x,OpenGL ES 2.0進行了大變革,更具靈活性,功能也更強大,而且渲染效率更高,效果更好。目前Android對OpenGL ES的支持以下:編程
- OpenGL ES 1.0 和 1.1 可以被Android 1.0及以上版本支持
- OpenGL ES 2.0 可以被Android 2.2及更高版本支持
- OpenGL ES 3.0 可以被Android 4.3及更高版本支持
- OpenGL ES 3.1 可以被Android 5.0及以上版本支持
相比起來學習OpenGL ES選擇2.0版本是一個相對最佳的選擇,能夠兼容4.0以上的手機設備,而且OpenGL ES 3.x都向下兼容OpenGL ES 2.0。本系列以OPENGL ES2.0演示和開發。數組
這裏主要關注OpenGL ES的這兩方面能力:bash
- 攝像頭預覽效果處理。
- 視頻處理。
Android框架裏面兩個基本的類來方便使用OpenGL ES API建立和操做圖形: GLSurfaceView
和 GLSurfaceView.Renderer
。GLSurfaceView
是管理OpenGL surface的一個特殊的View,它能夠幫助咱們把OpenGL的surface渲染到Android的View上,而且封裝了不少建立OpenGL環境所須要的配置,使咱們可以更方便地使用OpenGL。框架
GLSurfaceView.Renderer
接口定義了在GLSurfaceView中繪製圖形所需的方法。一般狀況下咱們使用GLSurfaceView.setRenderer()
方法將此接口實現設置進GLSurfaceView
中。此接口方法主要爲:ide
- onSurfaceCreated():建立GLSurfaceView時,系統調用該方法。使用此方法執行只須要執行一次的操做,如設置OpenGL環境參數或初始化OpenGL圖形對象。
- onDrawFrame():系統在每次重繪GLSurfaceView時調用這個方法。使用此方法做爲繪製圖形時的主要方法。
- onSurfaceChanged():當GLSurfaceView的大小或設備屏幕方向發生變化時,系統調用此方法。例如:設備從縱向變爲橫向時,系統調用此方法。咱們應該使用此方法來響應GLSurfaceView容器的改變。
在AndroidManifest.xml文件中設置使用的OpenGL ES的版本:學習
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
複製代碼
通常來講,咱們以下建立GLSurfaceView
,設置 GLSurfaceView.Renderer
便可。接下來咱們將重點放在 GLSurfaceView.Renderer
的編寫上面。ui
public class ZkGLSurfaceView extends GLSurfaceView {
public ZkGLSurfaceView(Context context) {
this(context,null);
}
public ZkGLSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
//使用OpenGL ES 2.0
setEGLContextClientVersion(2);
//設置Renderer
setRenderer(new MyRenderer(context));
//設置刷新模式
setRenderMode(RENDERMODE_WHEN_DIRTY);
}
}
複製代碼
定點着色器this
attribute vec4 vPosition;
attribute vec2 vCoordinate;
varying vec2 aCoordinate;
void main() {
gl_Position = vPosition;
aCoordinate = vCoordinate;
}
複製代碼
片元着色器spa
precision mediump float;
uniform sampler2D vTexture;
varying vec2 aCoordinate;
void main() {
gl_FragColor = texture2D(vTexture,aCoordinate);
}
複製代碼
gl_Position
和gl_FragColor
都是Shader的內置變量,分別爲定點位置和片元顏色。
attribute 通常用於各個頂點各不相同的量。如頂點顏色、座標等
varying 用於vertex和fragment之間傳遞值,通常用於頂點着色器傳遞到片元着色器的量
uniform 通常用於對於3D物體中全部頂點都相同的量。好比光源位置,統一變換矩陣等
//頂點座標
private float[] vertex = {
-1.0f,1.0f, //左上角
-1.0f,-1.0f, //左下角
1.0f,1.0f, //右上角
1.0f,-1.0f //右下角
};
private final float[] sCoord={
0f, 0f, //左上角
0f, 1f, //左下角
1f, 0f, //右上角
1f, 1f //右下角
};
//申請底層空間 將座標數據轉換爲FloatBuffer,用以傳入給OpenGL ES程序
mVertexBuffer = ByteBuffer.allocateDirect(vertex.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer().put(vertex);
mVertexBuffer.position(0);
mFragmentBuffer = ByteBuffer.allocateDirect(sCoord.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer().put(sCoord);
mFragmentBuffer.position(0);
複製代碼
頂點座標和紋理座標的環繞方向必須一致。
int vertex_shader = loadShader(GLES20.GL_VERTEX_SHADER,VERTEX_SHADER);
int fragment_shader = loadShader(GLES20.GL_FRAGMENT_SHADER,FRAGMENT_SHADER);
private int loadShader(int glVertexShader, String vertexShader) {
//建立shader(着色器:頂點或片元)
int glCreateShader = GLES20.glCreateShader(glVertexShader);
//加載shader源碼並編譯shader
GLES20.glShaderSource(glCreateShader, vertexShader);
int[] compiled = new int[1];
//檢查是否編譯成功
GLES20.glGetShaderiv(glCreateShader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] != GLES20.GL_TRUE){
GLES20.glDeleteShader(glCreateShader);
return -1;
}
return glCreateShader;
}
複製代碼
//建立渲染程序
mProgram = GLES20.glCreateProgram();
//將着色器程序添加到渲染程序中
GLES20.glAttachShader(mProgram, vertex_shader);
GLES20.glAttachShader(mProgram, fragment_shader);
//連接源程序
GLES20.glLinkProgram(mProgram);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(mProgram, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE){
String info = GLES20.glGetProgramInfoLog(mProgram);
GLES20.glDeleteProgram(mProgram);
throw new RuntimeException("Could not link program: " + info);
}
複製代碼
private int loadTexture(int resId){
int[] textures = new int[1];
//建立和綁定紋理
GLES20.glGenTextures(1,textures,0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
//激活第0個紋理
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glUniform1i(mVTexture, 0);
//設置環繞和過濾方式
//環繞(超出紋理座標範圍):(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);
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), resId);
//設置圖片
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
bitmap = null;
//解綁紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textures[0];
}
複製代碼
@Override
public void onDrawFrame() {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1.0f, 0, 0, 1f);
//使用源程序
GLES20.glUseProgram(mProgram);
//綁定繪製紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureid);
//使頂點屬性數組有效
GLES20.glEnableVertexAttribArray(mVPosition);
//爲頂點屬性賦值
GLES20.glVertexAttribPointer(mVPosition, 2, GLES20.GL_FLOAT, false, 8, mVertexBuffer);
GLES20.glEnableVertexAttribArray(mVCoordinate);
GLES20.glVertexAttribPointer(mVCoordinate, 2, GLES20.GL_FLOAT, false, 8, mFragmentBuffer);
//繪製圖形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
//解綁紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
複製代碼
完整代碼:
public class MyRenderer implements GLSurfaceView.Renderer{
private static final String VERTEX_SHADER = "attribute vec4 vPosition;\n"
+ "attribute vec2 vCoordinate;\n"
+ "varying vec2 aCoordinate;\n"
+ "void main() {\n"
+ "gl_Position = vPosition;\n"
+ "aCoordinate = vCoordinate;\n"
+ "}";
private static final String FRAGMENT_SHADER = "precision mediump float;\n"
+ "uniform sampler2D vTexture;\n"
+ "varying vec2 aCoordinate;\n"
+ "void main() {\n"
+ "gl_FragColor = texture2D(vTexture,aCoordinate);\n"
+ "}";
private float[] vertex = {
-1.0f,1.0f, //左上角
-1.0f,-1.0f, //左下角
1.0f,1.0f, //右上角
1.0f,-1.0f //右下角
};
private final float[] sCoord={
0f, 0f, //左上角
0f, 1f, //左下角
1f, 0f, //右上角
1f, 1f //右下角
};
private FloatBuffer mVertexBuffer;
private FloatBuffer mFragmentBuffer;
private int mProgram;
private int mVPosition;
private int mVCoordinate;
private int mVTexture;
private Context mContext;
private int mTextureid;
public MyRenderer(Context context){
mContext = context;
mVertexBuffer = ByteBuffer.allocateDirect(vertex.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer().put(vertex);
mVertexBuffer.position(0);
mFragmentBuffer = ByteBuffer.allocateDirect(sCoord.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer().put(sCoord);
mFragmentBuffer.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
int vertex_shader = loadShader(GLES20.GL_VERTEX_SHADER,VERTEX_SHADER);
int fragment_shader = loadShader(GLES20.GL_FRAGMENT_SHADER,FRAGMENT_SHADER);
mProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mProgram, vertex_shader);
GLES20.glAttachShader(mProgram, fragment_shader);
GLES20.glLinkProgram(mProgram);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(mProgram, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE){
String info = GLES20.glGetProgramInfoLog(mProgram);
GLES20.glDeleteProgram(mProgram);
throw new RuntimeException("Could not link program: " + info);
}
mVPosition = GLES20.glGetAttribLocation(mProgram, "vPosition");
mVCoordinate = GLES20.glGetAttribLocation(mProgram, "vCoordinate");
mVTexture = GLES20.glGetUniformLocation(mProgram, "vTexture");
mTextureid = loadTexture(R.drawable.fengj);
}
private int loadTexture(int resId){
int[] textures = new int[1];
//建立和綁定紋理
GLES20.glGenTextures(1,textures,0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
//激活第0個紋理
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glUniform1i(mVTexture, 0);
//設置環繞和過濾方式
//環繞(超出紋理座標範圍):(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);
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), resId);
//設置圖片
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
bitmap = null;
//解綁紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textures[0];
}
private int loadShader(int glVertexShader, String vertexShader) {
int glCreateShader = GLES20.glCreateShader(glVertexShader);
GLES20.glShaderSource(glCreateShader, vertexShader);
GLES20.glCompileShader(glCreateShader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(glCreateShader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] != GLES20.GL_TRUE){
GLES20.glDeleteShader(glCreateShader);
return -1;
}
return glCreateShader;
}
@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, 0, 1f);
//使用源程序
GLES20.glUseProgram(mProgram);
//綁定繪製紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureid);
//使頂點屬性數組有效
GLES20.glEnableVertexAttribArray(mVPosition);
//爲頂點屬性賦值
GLES20.glVertexAttribPointer(mVPosition, 2, GLES20.GL_FLOAT, false, 8, mVertexBuffer);
GLES20.glEnableVertexAttribArray(mVCoordinate);
GLES20.glVertexAttribPointer(mVCoordinate, 2, GLES20.GL_FLOAT, false, 8, mFragmentBuffer);
//繪製圖形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
//解綁紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
}
複製代碼