Discuz! Board

标题: android 的opengl es与egl (1) [打印本页]

作者: zangcf    时间: 2016-5-7 10:25
标题: android 的opengl es与egl (1)

在学习Android OpenGL ES开发之前,你必须具备Java 语言开发经验和一些Android开发的基本知识,但并不需要有图形开发的经验,本教程也会涉及到一些基本的线性几何知识,如矢量,矩阵运算等。

此外Android SDK 中有关OpenGL ES API的开发文档

注:上述Android 文档基本为空,可以参见JSR239的文档,比较详细。

OpenGL ES Specification都是学习时常用到的参考资料。

什么是OpenGL ES?OpenGL 与 OpenGL ES的关系

OpenGL ES 是基于桌面版本OpenGL 的,下图显示了OpenGL 和 OpenGL ES之间的关系图

[attach]198[/attach]

OpenGL ES Profiles

OpenGL ES 1.x 支持两种Profile以支持不同类型的嵌入设备。

本教程主要针对 Common Profile 设备支持浮点运算。



作者: zangcf    时间: 2016-5-7 10:30
标题: Android OpenGL ES(二):OpenGL ES管道(Pipeline)
大部分图形系统都可以比作工厂中的装配线(Assemble line)或者称为管道(Pipeline)。前一道的输出作为下道工序的输入。主CPU发出一个绘图指令,然后可能由硬件部件完成坐标变换,裁剪,添加颜色或是材质,最后在屏幕上显示出来。

OpenGL ES 1.x 的工序是固定的,称为Fix-Function Pipeline,可以想象一个带有很多控制开关的机器,尽管加工的工序是固定的,但是可以通过打开或关闭开关来设置参数或者打开关闭某些功能。

OpenGL ES 2.0 允许提供编程来控制一些重要的工序,一些“繁琐”的工序比如栅格化等仍然是固定的。

下图显示了OpenGL ES 1.x 固定管道的结构图:

[attach]199[/attach]


以上管道中工序可以通过设置来打开或关闭某些功能(比如无需雾化Fog处理),并可以为某个工序设置参数,比如设置Vertext Array。

比如如下代码:

[java] view plain copy





本教程主要介绍 OpenGL ES 1.1 编程,支持OpenGL ES 2.0 的设备一般会同时支持OpenGL ES 1.1。



作者: zangcf    时间: 2016-5-7 10:33
标题: Android OpenGL ES(三):OpenGL ES API 命名习惯

OpenGL ES是个跨平台的3D图形开发包规范,最常见的实现是采用C语言实现的,AndroidOpenGL ES 实现上是使用Java 语言对底层的C接口进行了封装,因此在android.opengl javax.microedition.khronos.egl ,javax.microedition.khronos.opengles 包中定义的OpenGL相关的类和方法带有很强的C语言色彩。



作者: zangcf    时间: 2016-5-7 10:36
标题: Android OpenGL ES(四):关于EGL
OpenGL ES的javax.microedition.khronos.opengles 包定义了平台无关的GL绘图指令,EGL(javax.microedition.khronos.egl )

则定义了控制displays ,contexts 以及surfaces 的统一的平台接口。

[attach]200[/attach]



作者: zangcf    时间: 2016-5-7 11:05
标题: Android OpenGL ES(五):GLSurfaceView

Android OpenGL ES 相关的包主要定义在

其中GLSurfaceView 为android.opengl  包中核心类:

使用过Java ME ,JSR 239 开发过OpenGL ES可以看到 Android 包javax.microedition.khronos.egl ,javax.microedition.khronos.opengles 和JSR239 基本一致,因此理论上不使用android.opengl 包中的类也可以开发Android上OpenGL ES应用,但此时就需要自己使用EGL来管理Display,Context, Surfaces 的创建,释放,捆绑。

使用EGL 实现GLSurfaceView一个可能的实现如下:

可以看到需要派生SurfaceView ,并手工创建,销毁Display,Context ,工作繁琐。

使用GLSurfaceView 内部提供了上面类似的实现,对于大部分应用只需调用一个方法来设置OpenGLView用到的GLSurfaceView.Renderer.

[java] view plain copy




GLSurfaceView.Renderer定义了一个统一图形绘制的接口,它定义了如下三个接口函数:
[java] view plain copy




如果有需要,也可以通过函数来修改GLSurfaceView一些缺省设置:

如果有需要,也可以通过函数来修改GLSurfaceView一些缺省设置:










作者: zangcf    时间: 2016-5-7 11:06
public class GLWallpaperService extends WallpaperService {  
    private static final String TAG = "GLWallpaperService";  
  
    @Override  
    public Engine onCreateEngine() {  
        return new GLEngine();  
    }  
  
    public class GLEngine extends Engine {  
        public final static int RENDERMODE_WHEN_DIRTY = 0;  
        public final static int RENDERMODE_CONTINUOUSLY = 1;  
  
        private GLThread mGLThread;  
        private EGLConfigChooser mEGLConfigChooser;  
        private EGLContextFactory mEGLContextFactory;  
        private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;  
        private GLWrapper mGLWrapper;  
        private int mDebugFlags;  
  
        public GLEngine() {  
            super();  
        }  
  
        @Override  
        public void onVisibilityChanged(boolean visible) {  
            if (visible) {  
                onResume();  
            } else {  
                onPause();  
            }  
            super.onVisibilityChanged(visible);  
        }  
  
        @Override  
        public void onCreate(SurfaceHolder surfaceHolder) {  
            super.onCreate(surfaceHolder);  
            // Log.d(TAG, "GLEngine.onCreate()");  
        }  
  
        @Override  
        public void onDestroy() {  
            super.onDestroy();  
            // Log.d(TAG, "GLEngine.onDestroy()");  
            mGLThread.requestExitAndWait();  
        }  
  
        @Override  
        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {  
            // Log.d(TAG, "onSurfaceChanged()");  
            mGLThread.onWindowResize(width, height);  
            super.onSurfaceChanged(holder, format, width, height);  
        }  
  
        @Override  
        public void onSurfaceCreated(SurfaceHolder holder) {  
            Log.d(TAG, "onSurfaceCreated()");  
            mGLThread.surfaceCreated(holder);  
            super.onSurfaceCreated(holder);  
        }  
  
        @Override  
        public void onSurfaceDestroyed(SurfaceHolder holder) {  
            Log.d(TAG, "onSurfaceDestroyed()");  
            mGLThread.surfaceDestroyed();  
            super.onSurfaceDestroyed(holder);  
        }  
  
        /**
         * An EGL helper class.
         */  
        public void setGLWrapper(GLWrapper glWrapper) {  
            mGLWrapper = glWrapper;  
        }  
  
        public void setDebugFlags(int debugFlags) {  
            mDebugFlags = debugFlags;  
        }  
  
        public int getDebugFlags() {  
            return mDebugFlags;  
        }  
  
        public void setRenderer(Renderer renderer) {  
            checkRenderThreadState();  
            if (mEGLConfigChooser == null) {  
                mEGLConfigChooser = new SimpleEGLConfigChooser(true);  
            }  
            if (mEGLContextFactory == null) {  
                mEGLContextFactory = new DefaultContextFactory();  
            }  
            if (mEGLWindowSurfaceFactory == null) {  
                mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();  
            }  
            mGLThread = new GLThread(renderer, mEGLConfigChooser, mEGLContextFactory, mEGLWindowSurfaceFactory, mGLWrapper);  
            mGLThread.start();  
        }  
  
        public void setEGLContextFactory(EGLContextFactory factory) {  
            checkRenderThreadState();  
            mEGLContextFactory = factory;  
        }  
  
        public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) {  
            checkRenderThreadState();  
            mEGLWindowSurfaceFactory = factory;  
        }  
  
        public void setEGLConfigChooser(EGLConfigChooser configChooser) {  
            checkRenderThreadState();  
            mEGLConfigChooser = configChooser;  
        }  
  
        public void setEGLConfigChooser(boolean needDepth) {  
            setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));  
        }  
  
        public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize,  
                int stencilSize) {  
            setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, blueSize, alphaSize, depthSize,  
                    stencilSize));  
        }  
  
        public void setRenderMode(int renderMode) {  
            mGLThread.setRenderMode(renderMode);  
        }  
  
        public int getRenderMode() {  
            return mGLThread.getRenderMode();  
        }  
  
        public void requestRender() {  
            mGLThread.requestRender();  
        }  
  
        public void onPause() {  
            mGLThread.onPause();  
        }  
  
        public void onResume() {  
            mGLThread.onResume();  
        }  
  
        public void queueEvent(Runnable r) {  
            mGLThread.queueEvent(r);  
        }  
  
        private void checkRenderThreadState() {  
            if (mGLThread != null) {  
                throw new IllegalStateException("setRenderer has already been called for this instance.");  
            }  
        }  
    }  
  
    public interface Renderer {  
  
        public void onSurfaceCreated(GL10 gl, EGLConfig config);  
  
        public void onSurfaceChanged(GL10 gl, int width, int height);  
  
        public void onDrawFrame(GL10 gl);  
    }  
}  
  
class LogWriter extends Writer {  
    private StringBuilder mBuilder = new StringBuilder();  
  
    @Override  
    public void close() {  
        flushBuilder();  
    }  
  
    @Override  
    public void flush() {  
        flushBuilder();  
    }  
  
    @Override  
    public void write(char[] buf, int offset, int count) {  
        for (int i = 0; i < count; i++) {  
            char c = buf[offset + i];  
            if (c == '\n') {  
                flushBuilder();  
            } else {  
                mBuilder.append(c);  
            }  
        }  
    }  
  
    private void flushBuilder() {  
        if (mBuilder.length() > 0) {  
            Log.v("GLSurfaceView", mBuilder.toString());  
            mBuilder.delete(0, mBuilder.length());  
        }  
    }  
}  
  
// ----------------------------------------------------------------------  
  
/**
* An interface for customizing the eglCreateContext and eglDestroyContext calls.
*

* This interface must be implemented by clients wishing to call
* {@link GLWallpaperService#setEGLContextFactory(EGLContextFactory)}
*/  
interface EGLContextFactory {  
    EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);  
  
    void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);  
}  
  
class DefaultContextFactory implements EGLContextFactory {  
  
    public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {  
        return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, null);  
    }  
  
    public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {  
        egl.eglDestroyContext(display, context);  
    }  
}  
  
/**
* An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls.
*

* This interface must be implemented by clients wishing to call
* {@link GLWallpaperService#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
*/  
interface EGLWindowSurfaceFactory {  
    EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object nativeWindow);  
  
    void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);  
}  
  
class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {  
  
    public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay  
            display, EGLConfig config, Object nativeWindow) {  
        // this is a bit of a hack to work around Droid init problems - if you don't have this, it'll get hung up on orientation changes  
        EGLSurface eglSurface = null;  
        while (eglSurface == null) {  
            try {  
                eglSurface = egl.eglCreateWindowSurface(display,  
                        config, nativeWindow, null);  
            } catch (Throwable t) {  
            } finally {  
                if (eglSurface == null) {  
                    try {  
                        Thread.sleep(10);  
                    } catch (InterruptedException t) {  
                    }  
                }  
            }  
        }  
        return eglSurface;  
    }  
  
    public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) {  
        egl.eglDestroySurface(display, surface);  
    }  
}  
  
interface GLWrapper {  
    /**
     * Wraps a gl interface in another gl interface.
     *
     * @param gl
     * a GL interface that is to be wrapped.
     * @return either the input argument or another GL object that wraps the input argument.
     */  
    GL wrap(GL gl);  
}  
  
class EglHelper {  
  
    private EGL10 mEgl;  
    private EGLDisplay mEglDisplay;  
    private EGLSurface mEglSurface;  
    private EGLContext mEglContext;  
    EGLConfig mEglConfig;  
  
    private EGLConfigChooser mEGLConfigChooser;  
    private EGLContextFactory mEGLContextFactory;  
    private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;  
    private GLWrapper mGLWrapper;  
  
    public EglHelper(EGLConfigChooser chooser, EGLContextFactory contextFactory,  
            EGLWindowSurfaceFactory surfaceFactory, GLWrapper wrapper) {  
        this.mEGLConfigChooser = chooser;  
        this.mEGLContextFactory = contextFactory;  
        this.mEGLWindowSurfaceFactory = surfaceFactory;  
        this.mGLWrapper = wrapper;  
    }  
  
    /**
     * Initialize EGL for a given configuration spec.
     *
     * @param configSpec
     */  
    public void start() {  
        // Log.d("EglHelper" + instanceId, "start()");  
        if (mEgl == null) {  
            // Log.d("EglHelper" + instanceId, "getting new EGL");  
            /*
             * Get an EGL instance
             */  
            mEgl = (EGL10) EGLContext.getEGL();  
        } else {  
            // Log.d("EglHelper" + instanceId, "reusing EGL");  
        }  
  
        if (mEglDisplay == null) {  
            // Log.d("EglHelper" + instanceId, "getting new display");  
            /*
             * Get to the default display.
             */  
            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);  
        } else {  
            // Log.d("EglHelper" + instanceId, "reusing display");  
        }  
  
        if (mEglConfig == null) {  
            // Log.d("EglHelper" + instanceId, "getting new config");  
            /*
             * We can now initialize EGL for that display
             */  
            int[] version = new int[2];  
            mEgl.eglInitialize(mEglDisplay, version);  
            mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);  
        } else {  
            // Log.d("EglHelper" + instanceId, "reusing config");  
        }  
  
        if (mEglContext == null) {  
            // Log.d("EglHelper" + instanceId, "creating new context");  
            /*
             * Create an OpenGL ES context. This must be done only once, an OpenGL context is a somewhat heavy object.
             */  
            mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);  
            if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {  
                throw new RuntimeException("createContext failed");  
            }  
        } else {  
            // Log.d("EglHelper" + instanceId, "reusing context");  
        }  
  
        mEglSurface = null;  
    }  
  
    /*
     * React to the creation of a new surface by creating and returning an OpenGL interface that renders to that
     * surface.
     */  
    public GL createSurface(SurfaceHolder holder) {  
        /*
         * The window size has changed, so we need to create a new surface.
         */  
        if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {  
  
            /*
             * Unbind and destroy the old EGL surface, if there is one.
             */  
            mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);  
            mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);  
        }  
  
        /*
         * Create an EGL surface we can render into.
         */  
        mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl, mEglDisplay, mEglConfig, holder);  
  
        if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {  
            throw new RuntimeException("createWindowSurface failed");  
        }  
  
        /*
         * Before we can issue GL commands, we need to make sure the context is current and bound to a surface.
         */  
        if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {  
            throw new RuntimeException("eglMakeCurrent failed.");  
        }  
  
        GL gl = mEglContext.getGL();  
        if (mGLWrapper != null) {  
            gl = mGLWrapper.wrap(gl);  
        }  
  
        /*
         * if ((mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS))!= 0) { int configFlags = 0; Writer log =
         * null; if ((mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) { configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR; }
         * if ((mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) { log = new LogWriter(); } gl = GLDebugHelper.wrap(gl,
         * configFlags, log); }
         */  
        return gl;  
    }  
  
    /**
     * Display the current render surface.
     *
     * @return false if the context has been lost.
     */  
    public boolean swap() {  
        mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);  
  
        /*
         * Always check for EGL_CONTEXT_LOST, which means the context and all associated data were lost (For instance
         * because the device went to sleep). We need to sleep until we get a new surface.
         */  
        return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;  
    }  
  
    public void destroySurface() {  
        if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {  
            mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);  
            mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);  
            mEglSurface = null;  
        }  
    }  
  
    public void finish() {  
        if (mEglContext != null) {  
            mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);  
            mEglContext = null;  
        }  
        if (mEglDisplay != null) {  
            mEgl.eglTerminate(mEglDisplay);  
            mEglDisplay = null;  
        }  
    }  
}  
  
class GLThread extends Thread {  
    private final static boolean LOG_THREADS = false;  
    public final static int DEBUG_CHECK_GL_ERROR = 1;  
    public final static int DEBUG_LOG_GL_CALLS = 2;  
  
    private final GLThreadManager sGLThreadManager = new GLThreadManager();  
    private GLThread mEglOwner;  
  
    private EGLConfigChooser mEGLConfigChooser;  
    private EGLContextFactory mEGLContextFactory;  
    private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;  
    private GLWrapper mGLWrapper;  
  
    public SurfaceHolder mHolder;  
    private boolean mSizeChanged = true;  
  
    // Once the thread is started, all accesses to the following member  
    // variables are protected by the sGLThreadManager monitor  
    public boolean mDone;  
    private boolean mPaused;  
    private boolean mHasSurface;  
    private boolean mWaitingForSurface;  
    private boolean mHaveEgl;  
    private int mWidth;  
    private int mHeight;  
    private int mRenderMode;  
    private boolean mRequestRender;  
    private boolean mEventsWaiting;  
    // End of member variables protected by the sGLThreadManager monitor.  
  
    private GLWallpaperService.Renderer mRenderer;  
    private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();  
    private EglHelper mEglHelper;  
  
    GLThread(GLWallpaperService.Renderer renderer, EGLConfigChooser chooser, EGLContextFactory contextFactory,  
            EGLWindowSurfaceFactory surfaceFactory, GLWrapper wrapper) {  
        super();  
        mDone = false;  
        mWidth = 0;  
        mHeight = 0;  
        mRequestRender = true;  
        mRenderMode = GLWallpaperService.GLEngine.RENDERMODE_CONTINUOUSLY;  
        mRenderer = renderer;  
        this.mEGLConfigChooser = chooser;  
        this.mEGLContextFactory = contextFactory;  
        this.mEGLWindowSurfaceFactory = surfaceFactory;  
        this.mGLWrapper = wrapper;  
    }  
  
    @Override  
    public void run() {  
        setName("GLThread " + getId());  
        if (LOG_THREADS) {  
            Log.i("GLThread", "starting tid=" + getId());  
        }  
  
        try {  
            guardedRun();  
        } catch (InterruptedException e) {  
            // fall thru and exit normally  
        } finally {  
            sGLThreadManager.threadExiting(this);  
        }  
    }  
  
    /*
     * This private method should only be called inside a synchronized(sGLThreadManager) block.
     */  
    private void stopEglLocked() {  
        if (mHaveEgl) {  
            mHaveEgl = false;  
            mEglHelper.destroySurface();  
            sGLThreadManager.releaseEglSurface(this);  
        }  
    }  
  
    private void guardedRun() throws InterruptedException {  
        mEglHelper = new EglHelper(mEGLConfigChooser, mEGLContextFactory, mEGLWindowSurfaceFactory, mGLWrapper);  
        try {  
            GL10 gl = null;  
            boolean tellRendererSurfaceCreated = true;  
            boolean tellRendererSurfaceChanged = true;  
  
            /*
             * This is our main activity thread's loop, we go until asked to quit.
             */  
            while (!isDone()) {  
                /*
                 * Update the asynchronous state (window size)
                 */  
                int w = 0;  
                int h = 0;  
                boolean changed = false;  
                boolean needStart = false;  
                boolean eventsWaiting = false;  
  
                synchronized (sGLThreadManager) {  
                    while (true) {  
                        // Manage acquiring and releasing the SurfaceView  
                        // surface and the EGL surface.  
                        if (mPaused) {  
                            stopEglLocked();  
                        }  
                        if (!mHasSurface) {  
                            if (!mWaitingForSurface) {  
                                stopEglLocked();  
                                mWaitingForSurface = true;  
                                sGLThreadManager.notifyAll();  
                            }  
                        } else {  
                            if (!mHaveEgl) {  
                                if (sGLThreadManager.tryAcquireEglSurface(this)) {  
                                    mHaveEgl = true;  
                                    mEglHelper.start();  
                                    mRequestRender = true;  
                                    needStart = true;  
                                }  
                            }  
                        }  
  
                        // Check if we need to wait. If not, update any state  
                        // that needs to be updated, copy any state that  
                        // needs to be copied, and use "break" to exit the  
                        // wait loop.  
  
                        if (mDone) {  
                            return;  
                        }  
  
                        if (mEventsWaiting) {  
                            eventsWaiting = true;  
                            mEventsWaiting = false;  
                            break;  
                        }  
  
                        if ((!mPaused) && mHasSurface && mHaveEgl && (mWidth > 0) && (mHeight > 0)  
                                && (mRequestRender || (mRenderMode == GLWallpaperService.GLEngine.RENDERMODE_CONTINUOUSLY))) {  
                            changed = mSizeChanged;  
                            w = mWidth;  
                            h = mHeight;  
                            mSizeChanged = false;  
                            mRequestRender = false;  
                            if (mHasSurface && mWaitingForSurface) {  
                                changed = true;  
                                mWaitingForSurface = false;  
                                sGLThreadManager.notifyAll();  
                            }  
                            break;  
                        }  
  
                        // By design, this is the only place where we wait().  
  
                        if (LOG_THREADS) {  
                            Log.i("GLThread", "waiting tid=" + getId());  
                        }  
                        sGLThreadManager.wait();  
                    }  
                } // end of synchronized(sGLThreadManager)  
  
                /*
                 * Handle queued events
                 */  
                if (eventsWaiting) {  
                    Runnable r;  
                    while ((r = getEvent()) != null) {  
                        r.run();  
                        if (isDone()) {  
                            return;  
                        }  
                    }  
                    // Go back and see if we need to wait to render.  
                    continue;  
                }  
  
                if (needStart) {  
                    tellRendererSurfaceCreated = true;  
                    changed = true;  
                }  
                if (changed) {  
                    gl = (GL10) mEglHelper.createSurface(mHolder);  
                    tellRendererSurfaceChanged = true;  
                }  
                if (tellRendererSurfaceCreated) {  
                    mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);  
                    tellRendererSurfaceCreated = false;  
                }  
                if (tellRendererSurfaceChanged) {  
                    mRenderer.onSurfaceChanged(gl, w, h);  
                    tellRendererSurfaceChanged = false;  
                }  
                if ((w > 0) && (h > 0)) {  
                    /* draw a frame here */  
                    mRenderer.onDrawFrame(gl);  
  
                    /*
                     * Once we're done with GL, we need to call swapBuffers() to instruct the system to display the
                     * rendered frame
                     */  
                    mEglHelper.swap();  
                    Thread.sleep(10);  
                }  
            }  
        } finally {  
            /*
             * clean-up everything...
             */  
            synchronized (sGLThreadManager) {  
                stopEglLocked();  
                mEglHelper.finish();  
            }  
        }  
    }  
  
    private boolean isDone() {  
        synchronized (sGLThreadManager) {  
            return mDone;  
        }  
    }  
  
    public void setRenderMode(int renderMode) {  
        if (!((GLWallpaperService.GLEngine.RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= GLWallpaperService.GLEngine.RENDERMODE_CONTINUOUSLY))) {  
            throw new IllegalArgumentException("renderMode");  
        }  
        synchronized (sGLThreadManager) {  
            mRenderMode = renderMode;  
            if (renderMode == GLWallpaperService.GLEngine.RENDERMODE_CONTINUOUSLY) {  
                sGLThreadManager.notifyAll();  
            }  
        }  
    }  
  
    public int getRenderMode() {  
        synchronized (sGLThreadManager) {  
            return mRenderMode;  
        }  
    }  
  
    public void requestRender() {  
        synchronized (sGLThreadManager) {  
            mRequestRender = true;  
            sGLThreadManager.notifyAll();  
        }  
    }  
  
    public void surfaceCreated(SurfaceHolder holder) {  
        mHolder = holder;  
        synchronized (sGLThreadManager) {  
            if (LOG_THREADS) {  
                Log.i("GLThread", "surfaceCreated tid=" + getId());  
            }  
            mHasSurface = true;  
            sGLThreadManager.notifyAll();  
        }  
    }  
  
    public void surfaceDestroyed() {  
        synchronized (sGLThreadManager) {  
            if (LOG_THREADS) {  
                Log.i("GLThread", "surfaceDestroyed tid=" + getId());  
            }  
            mHasSurface = false;  
            sGLThreadManager.notifyAll();  
            while (!mWaitingForSurface && isAlive() && !mDone) {  
                try {  
                    sGLThreadManager.wait();  
                } catch (InterruptedException e) {  
                    Thread.currentThread().interrupt();  
                }  
            }  
        }  
    }  
  
    public void onPause() {  
        synchronized (sGLThreadManager) {  
            mPaused = true;  
            sGLThreadManager.notifyAll();  
        }  
    }  
  
    public void onResume() {  
        synchronized (sGLThreadManager) {  
            mPaused = false;  
            mRequestRender = true;  
            sGLThreadManager.notifyAll();  
        }  
    }  
  
    public void onWindowResize(int w, int h) {  
        synchronized (sGLThreadManager) {  
            mWidth = w;  
            mHeight = h;  
            mSizeChanged = true;  
            sGLThreadManager.notifyAll();  
        }  
    }  
  
    public void requestExitAndWait() {  
        // don't call this from GLThread thread or it is a guaranteed  
        // deadlock!  
        synchronized (sGLThreadManager) {  
            mDone = true;  
            sGLThreadManager.notifyAll();  
        }  
        try {  
            join();  
        } catch (InterruptedException ex) {  
            Thread.currentThread().interrupt();  
        }  
    }  
  
    /**
     * Queue an "event" to be run on the GL rendering thread.
     *
     * @param r
     * the runnable to be run on the GL rendering thread.
     */  
    public void queueEvent(Runnable r) {  
        synchronized (this) {  
            mEventQueue.add(r);  
            synchronized (sGLThreadManager) {  
                mEventsWaiting = true;  
                sGLThreadManager.notifyAll();  
            }  
        }  
    }  
  
    private Runnable getEvent() {  
        synchronized (this) {  
            if (mEventQueue.size() > 0) {  
                return mEventQueue.remove(0);  
            }  
  
        }  
        return null;  
    }  
  
    private class GLThreadManager {  
  
        public synchronized void threadExiting(GLThread thread) {  
            if (LOG_THREADS) {  
                Log.i("GLThread", "exiting tid=" + thread.getId());  
            }  
            thread.mDone = true;  
            if (mEglOwner == thread) {  
                mEglOwner = null;  
            }  
            notifyAll();  
        }  
  
        /*
         * Tries once to acquire the right to use an EGL surface. Does not block.
         *
         * @return true if the right to use an EGL surface was acquired.
         */  
        public synchronized boolean tryAcquireEglSurface(GLThread thread) {  
            if (mEglOwner == thread || mEglOwner == null) {  
                mEglOwner = thread;  
                notifyAll();  
                return true;  
            }  
            return false;  
        }  
  
        public synchronized void releaseEglSurface(GLThread thread) {  
            if (mEglOwner == thread) {  
                mEglOwner = null;  
            }  
            notifyAll();  
        }  
    }  
}  
  
interface EGLConfigChooser {  
    EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);  
}  
  
abstract class BaseConfigChooser implements EGLConfigChooser {  
    public BaseConfigChooser(int[] configSpec) {  
        mConfigSpec = configSpec;  
    }  
  
    public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {  
        int[] num_config = new int[1];  
        egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config);  
  
        int numConfigs = num_config[0];  
  
        if (numConfigs <= 0) {  
            throw new IllegalArgumentException("No configs match configSpec");  
        }  
  
        EGLConfig[] configs = new EGLConfig[numConfigs];  
        egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, num_config);  
        EGLConfig config = chooseConfig(egl, display, configs);  
        if (config == null) {  
            throw new IllegalArgumentException("No config chosen");  
        }  
        return config;  
    }  
  
    abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);  
  
    protected int[] mConfigSpec;  
    public static class ComponentSizeChooser extends BaseConfigChooser {  
        public ComponentSizeChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize,  
                int stencilSize) {  
            super(new int[] { EGL10.EGL_RED_SIZE, redSize, EGL10.EGL_GREEN_SIZE, greenSize, EGL10.EGL_BLUE_SIZE,  
                    blueSize, EGL10.EGL_ALPHA_SIZE, alphaSize, EGL10.EGL_DEPTH_SIZE, depthSize, EGL10.EGL_STENCIL_SIZE,  
                    stencilSize, EGL10.EGL_NONE });  
            mValue = new int[1];  
            mRedSize = redSize;  
            mGreenSize = greenSize;  
            mBlueSize = blueSize;  
            mAlphaSize = alphaSize;  
            mDepthSize = depthSize;  
            mStencilSize = stencilSize;  
        }  
  
        @Override  
        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {  
            EGLConfig closestConfig = null;  
            int closestDistance = 1000;  
            for (EGLConfig config : configs) {  
                int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);  
                int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);  
                if (d >= mDepthSize && s >= mStencilSize) {  
                    int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);  
                    int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);  
                    int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);  
                    int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);  
                    int distance = Math.abs(r - mRedSize) + Math.abs(g - mGreenSize) + Math.abs(b - mBlueSize)  
                    + Math.abs(a - mAlphaSize);  
                    if (distance < closestDistance) {  
                        closestDistance = distance;  
                        closestConfig = config;  
                    }  
                }  
            }  
            return closestConfig;  
        }  
  
        private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, int attribute, int defaultValue) {  
  
            if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {  
                return mValue[0];  
            }  
            return defaultValue;  
        }  
  
        private int[] mValue;  
        // Subclasses can adjust these values:  
        protected int mRedSize;  
        protected int mGreenSize;  
        protected int mBlueSize;  
        protected int mAlphaSize;  
        protected int mDepthSize;  
        protected int mStencilSize;  
    }  
  
    /**
     * This class will choose a supported surface as close to RGB565 as possible, with or without a depth buffer.
     *
     */  
    public static class SimpleEGLConfigChooser extends ComponentSizeChooser {  
        public SimpleEGLConfigChooser(boolean withDepthBuffer) {  
            super(4, 4, 4, 0, withDepthBuffer ? 16 : 0, 0);  
            // Adjust target values. This way we'll accept a 4444 or  
            // 555 buffer if there's no 565 buffer available.  
            mRedSize = 5;  
            mGreenSize = 6;  
            mBlueSize = 5;  
        }  
    }  
}  
作者: zangcf    时间: 2016-5-7 11:17
有了前面关于Android OpenGL ES的介绍,可以开始创建示例程序OpenGLDemos。

使用Eclipse 创建一个Android项目

Project Name: OpenGLDemos
Build Target: Android 1.6 ( >1.5 即可)
Application Name: Android OpenGL ES Demos
Package Name: com.pstreets.opengl.demo
Create Activity:AndroidOpenGLDemo
创建一个OpenGLRenderer 实现 GLSurfaceView.Renderer接口:
public class OpenGLRenderer implements Renderer {  
   
private final IOpenGLDemo openGLDemo;  
public OpenGLRenderer(IOpenGLDemo demo){  
openGLDemo=demo;  
}  
   
public void onSurfaceCreated(GL10 gl, EGLConfig config) {  
// Set the background color to black ( rgba ).  
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);  
// Enable Smooth Shading, default not really needed.  
gl.glShadeModel(GL10.GL_SMOOTH);  
// Depth buffer setup.  
gl.glClearDepthf(1.0f);  
// Enables depth testing.  
gl.glEnable(GL10.GL_DEPTH_TEST);  
// The type of depth testing to do.  
gl.glDepthFunc(GL10.GL_LEQUAL);  
// Really nice perspective calculations.  
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,  
GL10.GL_NICEST);  
   
}  
   
public void onDrawFrame(GL10 gl) {  
if(openGLDemo!=null){  
openGLDemo.DrawScene(gl);  
}  
   
}  
   
public void onSurfaceChanged(GL10 gl, int width, int height) {  
// Sets the current view port to the new size.  
gl.glViewport(0, 0, width, height);  
// Select the projection matrix  
gl.glMatrixMode(GL10.GL_PROJECTION);  
// Reset the projection matrix  
gl.glLoadIdentity();  
// Calculate the aspect ratio of the window  
GLU.gluPerspective(gl, 45.0f,  
(float) width / (float) height,  
0.1f, 100.0f);  
// Select the modelview matrix  
gl.glMatrixMode(GL10.GL_MODELVIEW);  
// Reset the modelview matrix  
gl.glLoadIdentity();  
}  
}  
作者: zangcf    时间: 2016-5-7 11:18
为简洁起见,为所有的示例定义了一个接口IOpenGLDemo,
[java] view plain copy
public interface IOpenGLDemo {  
public void DrawScene(GL10 gl);  
   
}  

作者: zangcf    时间: 2016-5-7 11:18
DrawScene 用于实际的GL绘图示例代码,其它的初始化工作基本就由GLSurfaceView 和OpenGLRenderer 完成,其中onSurfaceCreated 和 onSurfaceChanged 中的代码含义现在无需了解,后面会有具体介绍,只要知道它们是用来初始化GLSurfaceView就可以了。

最后使用一个简单的例子“Hello World”结束本篇,“Hello World” 使用红色背景刷新屏幕。
作者: zangcf    时间: 2016-5-7 11:18
public class HelloWorld extends Activity  
   implements IOpenGLDemo{  
   
    /** Called when the activity is first created. */  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);  
        getWindow()  
         .setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,  
            WindowManager.LayoutParams.FLAG_FULLSCREEN);  
   
        mGLSurfaceView = new GLSurfaceView(this);  
        mGLSurfaceView.setRenderer(new OpenGLRenderer(this));  
        setContentView(mGLSurfaceView);  
    }  
   
    public void DrawScene(GL10 gl) {  
        gl.glClearColor(1.0f, 0.0f, 0.0f, 0.0f);  
        // Clears the screen and depth buffer.  
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT  
          | GL10.GL_DEPTH_BUFFER_BIT);  
   
    }  
   
    @Override  
    protected void onResume() {  
        // Ideally a game should implement  
        // onResume() and onPause()  
        // to take appropriate action when the  
        //activity looses focus  
        super.onResume();  
        mGLSurfaceView.onResume();  
    }  
   
    @Override  
    protected void onPause() {  
        // Ideally a game should implement onResume()  
        //and onPause()  
        // to take appropriate action when the  
        //activity looses focus  
        super.onPause();  
        mGLSurfaceView.onPause();  
    }  
   
    private GLSurfaceView mGLSurfaceView;  
   
}  
作者: zangcf    时间: 2016-5-7 11:19
其对应在AndroidManifest.xml中的定义如下:

<activity android:name=”.HelloWorld” android:label=”@string/activity_helloworld”>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”intent.category.opengl.SAMPLE_CODE” />
</intent-filter>
</activity>
[attach]201[/attach]





欢迎光临 Discuz! Board (http://47.89.242.157:9000/bbs/discuz/) Powered by Discuz! X3.2