基于textureview编写opengl程序

原文链接:https://my.oschina.net/fuyajun1983cn/blog/263790

与SurfaceView相比,TextureView并没有创建一个单独的Surface用来绘制,这使得它可以像一般的View一样执行一些变换操作,设置透明度等。

另外,Textureview必须在硬件加速开启的窗口中。下面的例子演示了通过TextureView来创建一个opengl程序。


基于TextureView的程序,我们需要实现TextureView.SurfaceTextureListener这个接口,首先给出Activity的代码,在该类中,我们实现了此接口:

package com.fyj.test;

import java.util.concurrent.atomic.AtomicBoolean;

import android.app.Activity;
import android.graphics.SurfaceTexture;
import android.os.Bundle;
import android.view.TextureView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

public class TextureviewtestActivity extends Activity implements TextureView.SurfaceTextureListener{

	private TextureView mTextureView;
	private Thread mProducerThread = null;
	private GLRendererImpl mRenderer;
	

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,   
                WindowManager.LayoutParams.FLAG_FULLSCREEN);  
        mTextureView = new TextureView(this);
        mTextureView.setSurfaceTextureListener(this);
        mTextureView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);//隐藏虚拟按键,即navigator bar
        setContentView(mTextureView);
        
    	mRenderer = new GLRendererImpl(this);
    }


	@Override
	public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
			int height) {
		// TODO Auto-generated method stub
		mRenderer.setViewport(width, height);
		mProducerThread = new GLProducerThread(surface, mRenderer, new AtomicBoolean(true));
		mProducerThread.start();
	}

	@Override
	public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
		// TODO Auto-generated method stub
		mProducerThread = null;
		return true;
	}


	@Override
	public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
			int height) {
		// TODO Auto-generated method stub
		mRenderer.resize(width, height);
		
	}


	@Override
	public void onSurfaceTextureUpdated(SurfaceTexture surface) {
		// TODO Auto-generated method stub
		
	}
}

其中,在方法onSurfaceTextureAvailable中,我们开启一个OpenGL线程,在该线程中执行OpenGL的一些操作,该线程的定义如下:

package com.fyj.test;

import java.util.concurrent.atomic.AtomicBoolean;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL;

import android.graphics.SurfaceTexture;
import android.opengl.GLUtils;

public class GLProducerThread extends Thread {

	private AtomicBoolean mShouldRender;
	private SurfaceTexture mSurfaceTexture;
	private GLRenderer mRenderer;
	
	private EGL10 mEgl;
	private EGLDisplay mEglDisplay = EGL10.EGL_NO_DISPLAY;
	private EGLContext mEglContext = EGL10.EGL_NO_CONTEXT;
	private EGLSurface mEglSurface = EGL10.EGL_NO_SURFACE;
	private GL mGL;
	
	
	private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
	private static final int EGL_OPENGL_ES2_BIT = 4;
	
	public interface GLRenderer {
		public void drawFrame();
	}
	
	public GLProducerThread(SurfaceTexture surfaceTexture, GLRenderer renderer, AtomicBoolean shouldRender) 
	{
		mSurfaceTexture = surfaceTexture;
		mRenderer = renderer;
		mShouldRender = shouldRender;
	}
	
	private void initGL()
	{
		mEgl = (EGL10)EGLContext.getEGL();
		
		mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
		if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
			throw new RuntimeException("eglGetdisplay failed : " + 
																	GLUtils.getEGLErrorString(mEgl.eglGetError()));
		}
		
		int []version = new int[2];
		if (!mEgl.eglInitialize(mEglDisplay, version)) {
			throw new RuntimeException("eglInitialize failed : " + 
					GLUtils.getEGLErrorString(mEgl.eglGetError()));
		}
		
		int []configAttribs = {
				EGL10.EGL_BUFFER_SIZE, 32,
				EGL10.EGL_ALPHA_SIZE, 8,
				EGL10.EGL_BLUE_SIZE, 8,
				EGL10.EGL_GREEN_SIZE, 8,
				EGL10.EGL_RED_SIZE, 8,
				EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
				EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
				EGL10.EGL_NONE
		};
		
		int []numConfigs = new int[1];
		EGLConfig []configs = new EGLConfig[1]; 
		if (!mEgl.eglChooseConfig(mEglDisplay, configAttribs, configs, 1, numConfigs)) {
			throw new RuntimeException("eglChooseConfig failed : " + 
					GLUtils.getEGLErrorString(mEgl.eglGetError()));
		}
		
		int []contextAttribs = {
				EGL_CONTEXT_CLIENT_VERSION, 2,
				EGL10.EGL_NONE
		};
		mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], EGL10.EGL_NO_CONTEXT, contextAttribs);
		mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, configs[0], mSurfaceTexture, null);
		if (mEglSurface == EGL10.EGL_NO_SURFACE || mEglContext == EGL10.EGL_NO_CONTEXT) {
			int error = mEgl.eglGetError();
			if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
				throw new RuntimeException("eglCreateWindowSurface returned  EGL_BAD_NATIVE_WINDOW. " );
			}
			throw new RuntimeException("eglCreateWindowSurface failed : " + 
					GLUtils.getEGLErrorString(mEgl.eglGetError()));
		}
		
		if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
			throw new RuntimeException("eglMakeCurrent failed : " + 
					GLUtils.getEGLErrorString(mEgl.eglGetError()));
		}
		
		mGL = mEglContext.getGL();
	}
	
	private void destoryGL() 
	{
		mEgl.eglDestroyContext(mEglDisplay, mEglContext);
		mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
		mEglContext = EGL10.EGL_NO_CONTEXT;
		mEglSurface = EGL10.EGL_NO_SURFACE;
	}
	
	public void run() 
	{
		initGL();
		
		if (mRenderer != null) {
			((GLRendererImpl)mRenderer).initGL();
		}
		
		while (mShouldRender != null && mShouldRender.get() != false) {
			if (mRenderer != null)
				mRenderer.drawFrame();
			mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
			
			try {
				sleep(5);
			} catch(InterruptedException e) {
				
			}
		}
		
		destoryGL();
	}
}

在该线程中,我们定义了GLRenderer接口,该接口的实现者负责具体的OpenGL绘制代码:

package com.fyj.test;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;

import com.fyj.test.GLProducerThread.GLRenderer;

public class GLRendererImpl implements GLRenderer {
	
	private int mProgramObject;
	private int mWidth;
	private int mHeight;
	private FloatBuffer mVertices;
	private ShortBuffer mTexCoords;
	private Context mContext;
	private int mTexID;
	private static String TAG = "GLRendererImpl";

	private final float[] mVerticesData = { -0.5f, -0.5f, 0, 0.5f, -0.5f, 0, -0.5f, 0.5f, 0, 0.5f, 0.5f, 0 };
	private final short[] mTexCoordsData = {0, 1, 1, 1, 0, 0, 1, 0};

	public GLRendererImpl(Context ctx)
	{
		mVertices = ByteBuffer.allocateDirect(mVerticesData.length * 4)
				.order(ByteOrder.nativeOrder()).asFloatBuffer();
		mVertices.put(mVerticesData).position(0);
		
		mTexCoords = ByteBuffer.allocateDirect(mTexCoordsData.length * 2)
				.order(ByteOrder.nativeOrder()).asShortBuffer();
		mTexCoords.put(mTexCoordsData).position(0);
		
		mContext = ctx;
		
	}
	
	public void setViewport(int width, int height) 
	{
		mWidth = width;
		mHeight = height;
	}
	
	public void initGL()
	{
		comipleAndLinkProgram();
		
		loadTexture();
		
		GLES20.glClearColor(0,  0, 0, 0);
	}
	
	public void resize(int width, int height) 
	{
		mWidth = width;
		mHeight = height;
		
	}
	
	@Override
	public void drawFrame() {
		// TODO Auto-generated method stub
		GLES20.glViewport(0, 0, mWidth, mHeight);
		
		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

		GLES20.glUseProgram(mProgramObject);
		
		GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 0, mVertices);
		GLES20.glEnableVertexAttribArray(0);
		GLES20.glVertexAttribPointer(1, 2, GLES20.GL_SHORT, false, 0, mTexCoords);
		GLES20.glEnableVertexAttribArray(1);
		
		GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexID);
		int loc = GLES20.glGetUniformLocation(mProgramObject, "u_Texture");
		GLES20.glUniform1f(loc, 0);
		GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
	
		Log.i("GLRendererImpl", "drawing..." + mWidth);
	}

	
	private void loadTexture() {
		Bitmap b = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher);
		if (b != null) {
			int []texID = new int[1];
			GLES20.glGenTextures(1, texID, 0);
			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texID[0]);
			mTexID = texID[0];
			
			GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, 
					GLES20.GL_LINEAR);
			GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, 
					GLES20.GL_LINEAR);
			
			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);
			
			GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, b, 0);
			b.recycle();
		}
	}
	
	private int loadShader(int shaderType, String shaderSource) {
		int shader;
		int[] compiled = new int[1];

		// Create the shader object
		shader = GLES20.glCreateShader(shaderType);

		if (shader == 0)
			return 0;

		// Load the shader source
		GLES20.glShaderSource(shader, shaderSource);

		// Compile the shader
		GLES20.glCompileShader(shader);

		// Check the compile status
		GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);

		if (compiled[0] == 0) {
			Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
			GLES20.glDeleteShader(shader);
			return 0;
		}
		return shader;
	}
	
	private void comipleAndLinkProgram() {
		String vShaderStr = "attribute vec4 a_position;    \n"
				+"attribute vec2 a_texCoords; \n"
				+"varying vec2 v_texCoords; \n"
				+ "void main()                  \n"
				+ "{                            \n"
				+ "   gl_Position = a_position;  \n"
				+"    v_texCoords = a_texCoords; \n"
				+ "}                            \n";

		String fShaderStr = "precision mediump float;					  \n"
				+"uniform sampler2D u_Texture; \n"
				+"varying vec2 v_texCoords; \n"
				+ "void main()                                  \n"
				+ "{                                            \n"
				+ "  gl_FragColor = texture2D(u_Texture, v_texCoords) ;\n"
				+ "}                                            \n";

		int vertexShader;
		int fragmentShader;
		int programObject;
		int[] linked = new int[1];

		// Load the vertex/fragment shaders
		vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vShaderStr);
		fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fShaderStr);

		// Create the program object
		programObject = GLES20.glCreateProgram();

		if (programObject == 0)
			return ;

		GLES20.glAttachShader(programObject, vertexShader);
		GLES20.glAttachShader(programObject, fragmentShader);

		// Bind vPosition to attribute 0
		GLES20.glBindAttribLocation(programObject, 0, "a_position");
		GLES20.glBindAttribLocation(programObject, 1, "a_texCoords");

		// Link the program
		GLES20.glLinkProgram(programObject);

		// Check the link status
		GLES20.glGetProgramiv(programObject, GLES20.GL_LINK_STATUS, linked, 0);

		if (linked[0] == 0) {
			Log.e(TAG, "Error linking program:");
			Log.e(TAG, GLES20.glGetProgramInfoLog(programObject));
			GLES20.glDeleteProgram(programObject);
			return  ;
		}

		mProgramObject = programObject;
	}
}

需要注意的是,所有EGL及GL相关的操作必须置于GLThread线程中进行。

转载于:https://my.oschina.net/fuyajun1983cn/blog/263790

上一篇:Android 7.0源码环境单元测试


下一篇:Android:如何以编程方式找出设备的精确像素长宽比?