OpenGLES入门笔记:Rajawali学习(2)场景绘制基本流程
2016-08-15 23:38
627 查看
背景
上一篇文章中我们简单体验了一下Rajawali的基本功能,现在我们来具体看一下这些物体是如何封装,最终调用GLES 绘制的。上一篇中通过阅读代码我们发现Rajawali的绘制也是类似于Surface和Renderer的机制,Surface用于最终的呈现,Renderer用于渲染图形。实现
Surface
ISurface
这个接口定义了Surface中的基本功能,实现了它才算是一个基本的Surface。我们看看Surface需要哪些基本方法。/** * 抗锯齿选项 */ public static enum ANTI_ALIASING_CONFIG { NONE, MULTISAMPLING, COVERAGE; public static ANTI_ALIASING_CONFIG fromInteger(int i) { switch (i) { case 0: return NONE; case 1: return MULTISAMPLING; case 2: return COVERAGE; } return NONE; } } /** * The renderer only renders * when the surface is created, or when {@link #requestRenderUpdate()} is called. */ public final static int RENDERMODE_WHEN_DIRTY = 0; /** * The renderer is called * continuously to re-render the scene. */ public final static int RENDERMODE_CONTINUOUSLY = 1; /** * 设置帧率 */ public void setFrameRate(double rate); /** * 获取渲染模式 */ public int getRenderMode(); /** * 设置渲染模式 */ public void setRenderMode(int mode); /** * 使能多重采样 * Must be called before {@link #setSurfaceRenderer(ISurfaceRenderer)}. */ public void setAntiAliasingMode(ANTI_ALIASING_CONFIG config); /** * 设置采样次数,多重采样开启时生效 */ public void setSampleCount(int count); /** * 设置render */ public void setSurfaceRenderer(ISurfaceRenderer renderer) throws IllegalStateException ; /** * render请求生效时调用 */ public void requestRenderUpdate();
TextureView
这是我们真正的Sureface,它并不是Android原生的TextureView,而是继承了原生TextureView并实现了ISurface。这个类的代码比较长,不过我们可以发现其中包含了很多内部类,现在我们简单梳理一下这些内部类的功能。GLThreadManager 通过判断标志及使用notifyAll进行线程调度
GLThread GL线程,用于委托render进行绘制,同步相关在GLThreadManager中完成
在run方法中执行guardedRun()方法,guardedRun()循环判断同步条件后执行如下代码
... try { mEglHelper.start(); } catch (RuntimeException t) { sGLThreadManager.releaseEglContextLocked(this); throw t; } mHaveEglContext = true; createEglContext = true; sGLThreadManager.notifyAll(); ...
其中mEglHelper.start()的作用是初始化了如下OpenGL变量
EGL10 mEgl; EGLDisplay mEglDisplay; EGLSurface mEglSurface; EGLConfig mEglConfig; EGLContext mEglContext;
EglHelper
ComponentSizeChooser 设置使用的rgba 深度,模板的大小
BaseConfigChooser
DefaultWindowSurfaceFactory EGLSurfaces的工厂类,提供了生成和销毁的方法
DefaultContextFactory EGLContext的工厂类,提供了生成和销毁的方法
RendererDelegate render委托类,将帧率,抗锯齿,以及关联的TextureView设置给render
我们自定义的TextureView类就是靠上面这些类来实现上述具体的功能。
了解了内部类的基本作用,我们开始看TextureView的主要功能。
public TextureView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); applyAttributes(context, attrs); }
构造函数中,我们将帧率等属性进行赋值。注意获取属性列表的方式:
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TextureView);
下面看一下onAttachedToWindow
@Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (LOG_ATTACH_DETACH) { Log.d(TAG, "onAttachedToWindow reattach =" + mDetached); } if (mDetached && (mRendererDelegate != null)) { int renderMode = RENDERMODE_CONTINUOUSLY; if (mGLThread != null) { renderMode = mGLThread.getRenderMode(); } mGLThread = new GLThread(mThisWeakRef); if (renderMode != RENDERMODE_CONTINUOUSLY) { mGLThread.setRenderMode(renderMode); } mGLThread.start(); } mDetached = false; }
此处执行我们上文提到过的GLThread进行GL绘制,可见Surface中更新UI并不是在主线程,而是在GL线程中进行。
Renderer
ISurfaceRenderer
同理,renderer也有个接口,作为renderer必须实现这个接口。/** * Fetch the current target frame rate in frames per second. * * @return {@code double} The target frame rate. */ public double getFrameRate(); /** * Sets the target frame rate in frames per second. * * @param rate {@code int} The target rate. */ public void setFrameRate(int rate); /** * Sets the target frame rate in frames per second. * * @param rate {@code double} The target rate. */ public void setFrameRate(double rate); /** * Called to inform the renderer of the multisampling configuration on this surface. * * @param config {@link ISurface.ANTI_ALIASING_CONFIG} The desired anti aliasing configuration. */ public void setAntiAliasingMode(ISurface.ANTI_ALIASING_CONFIG config); /** * Sets the {@link ISurface} which this implementation will be rendering on. * * @param surface {@link ISurface} The rendering surface. */ public void setRenderSurface(ISurface surface); /** * Called when the renderer should pause all of its rendering activities, such as frame draw requests. */ public void onPause(); /** * Called when the renderer should continue all of its rendering activities, such as frame draw requests. */ public void onResume(); /** * This corresponds to {@link TextureView.SurfaceTextureListener#onSurfaceTextureAvailable(SurfaceTexture, int, int)} * and {@link GLSurfaceView.Renderer#onSurfaceCreated(GL10, EGLConfig)}. Unused parameters are passed as null or -1. * * @param config {@link EGLConfig config}. This is used if the surface is {@link GL10} type (SurfaceView). * @param gl {@link GL10} for rendering. * @param width {@code width} The surface width in pixels. * @param height {@code height} The surface height in pixels. */ public void onRenderSurfaceCreated(EGLConfig config, GL10 gl, int width, int height); /** * Called when the rendering surface has been destroyed, such as the view being detached from the window. * * @param surface {@link SurfaceTexture} The texture which was being rendered to. */ public void onRenderSurfaceDestroyed(SurfaceTexture surface); /** * This corresponds to {@link TextureView.SurfaceTextureListener#onSurfaceTextureSizeChanged(SurfaceTexture, int, int)} * and {@link GLSurfaceView.Renderer#onSurfaceChanged(GL10, int, int)}. * * @param gl {@link GL10} for rendering. * @param width {@code width} The surface width in pixels. * @param height {@code height} The surface height in pixels. */ public void onRenderSurfaceSizeChanged(GL10 gl, int width, int height); /** * Called when the renderer should draw its next frame. * * @param gl {@link GL10} for rendering. */ public void onRenderFrame(GL10 gl); /** * NOTE: Only relevant when rendering a live wallpaper. * * Called to inform you of the wallpaper's offsets changing within its contain, corresponding to the container's * call to WallpaperManager.setWallpaperOffsets(). * * @param xOffset * @param yOffset * @param xOffsetStep * @param yOffsetStep * @param xPixelOffset * @param yPixelOffset */ public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset); /** * Called as the user performs touch-screen interaction with the window that is currently showing this wallpaper. * Note that the events you receive here are driven by the actual application the user is interacting with, * so if it is slow you will get fewer move events. * * @param event {@link MotionEvent} The touch event. */ public void onTouchEvent(MotionEvent event);
上面一堆主要做了以下几件事
1.采样率设置
2.抗锯齿设置
3.surface相关回调方法
*surface创建
*surface销毁
*surface大小发生改变
4.绘制下一帧回掉
5.外部容器位置变化回掉
6.Touch事件回调
Renderer
Renderer是一个实现了ISurfaceRenderer的抽象类,其中void initScene()方法需要我们自己实现,功能是初始化摆放我们的各种模型。private Scene mCurrentScene; //场景列表 protected final List<Scene> mScenes; //List of all scenes this renderer is aware of. //目标列表 protected final List<RenderTarget> mRenderTargets; //List of all render targets this renderer is aware of. //添加场景内容任务 private final Queue<AFrameTask> mFrameTaskQueue; private final SparseArray<ModelRunnable> mLoaderThreads; private final SparseArray<IAsyncLoaderCallback> mLoaderCallbacks;
构造方法
public Renderer(Context context, boolean registerForResources) { RajLog.i("Rajawali | Bombshell | v1.1 Development "); RajLog.i("THIS IS A DEV BRANCH CONTAINING SIGNIFICANT CHANGES. PLEASE REFER TO CHANGELOG.md FOR MORE INFORMATION."); mHaveRegisteredForResources = registerForResources; mContext = context; RawShaderLoader.mContext = new WeakReference<>(context); mFrameRate = getRefreshRate(); mScenes = Collections.synchronizedList(new CopyOnWriteArrayList<Scene>()); mRenderTargets = Collections.synchronizedList(new CopyOnWriteArrayList<RenderTarget>()); mFrameTaskQueue = new LinkedList<>(); mSceneCachingEnabled = true; mSceneInitialized = false; mLoaderThreads = new SparseArray<>(); mLoaderCallbacks = new SparseArray<>(); final Scene defaultScene = getNewDefaultScene(); mScenes.add(defaultScene); mCurrentScene = defaultScene; // Make sure we use the default viewport size initially clearOverrideViewportDimensions(); // Make sure we have a texture manager mTextureManager = TextureManager.getInstance(); mTextureManager.setContext(getContext()); // Make sure we have a material manager mMaterialManager = MaterialManager.getInstance(); mMaterialManager.setContext(getContext()); // We are registering now if (registerForResources) { mTextureManager.registerRenderer(this); mMaterialManager.registerRenderer(this); } }
开启渲染
此处开启RequestRenderTask来执行渲染操作,帧率就是RequestRenderTask执行的频率。
public void startRendering() { RajLog.d("startRendering()"); if (!mSceneInitialized) { return; } mRenderStartTime = System.nanoTime(); mLastRender = mRenderStartTime; if (mTimer != null) return; mTimer = Executors.newScheduledThreadPool(1); mTimer.scheduleAtFixedRate(new RequestRenderTask(), 0, (long) (1000 / mFrameRate), TimeUnit.MILLISECONDS); }
下面看一下RequestRenderTask里做了什么
private class RequestRenderTask implements Runnable { public void run() { if (mSurface != null) { mSurface.requestRenderUpdate(); } } }
我们发现这里又回到了Isurface中的方法requestRenderUpdate()。那么,我们的场景是如何传入surface中的呢?好像并没有直接传入场景数据的方法,我们需要再往下看。
onRenderFrame
//轮一遍mFrameTaskQueue中的任务 performFrameTasks(); //Execute any pending frame tasks ... onRender(elapsedRenderTime, deltaTime); ++mFrameCount; if (mFrameCount % 50 == 0) { ... if (mFPSUpdateListener != null) mFPSUpdateListener.onFPSUpdate(mLastMeasuredFPS); //Update the FPS listener }
onRender
protected void onRender(final long ellapsedRealtime, final double deltaTime) { render(ellapsedRealtime, deltaTime); }
找到render方法
protected void render(final long ellapsedRealtime, final double deltaTime) { mCurrentScene.render(ellapsedRealtime, deltaTime, mCurrentRenderTarget); }
可见,最终的渲染其实是调用了mCurrentScene的render方法,参数中mCurrentRenderTarget正是我们场景中的模型。
Scene
我们发现render中真实调用的其实是Scene中的render方法,现在我们来看看这里做了什么。此处开始真正调用Opengl的API来对我们传入数据进行渲染。这里先检查有没有新的模型或者其他物体添加,如果有的话在render线程中同步添加,之后,分别处理每个列表,调用物体本身的render方法进行绘制,这些方法就直接调用Opengl API进行绘制。public void render(long ellapsedTime, double deltaTime, RenderTarget renderTarget) { render(ellapsedTime, deltaTime, renderTarget, null); } public void render(long ellapsedTime, double deltaTime, RenderTarget renderTarget, Material sceneMaterial) { if (mPickerInfo != null) { 最终调用ObjectColorPicker的静态方法pickObject(pickerInfo),查看是否有模型被选中 } performFrameTasks(); //如果有新的物体,同步添加 synchronized (mFrameTaskQueue) { 为表面材料设置光照 } synchronized (mNextSkyboxLock) { 设置天空盒 } synchronized (mNextCameraLock) { 设置Camera切换 } int clearMask = mAlwaysClearColorBuffer? GLES20.GL_COLOR_BUFFER_BIT : 0; ... if (mEnableDepthBuffer) { 设置Opengl深度缓存 } if (mAntiAliasingConfig.equals(ISurface.ANTI_ALIASING_CONFIG.COVERAGE)) { clearMask |= GL_COVERAGE_BUFFER_BIT_NV; } GLES20.glClear(clearMask); final int preCount = mPreCallbacks.size(); if (preCount > 0) { 执行onPreFrame回调 } // Update all registered animations synchronized (mAnimations) { 执行动画 } //更新Camera的MVP矩阵 // We are beginning the render process so we need to update the camera matrix before fetching its values mCamera.onRecalculateModelMatrix(null); // Get the view and projection matrices in advance mVMatrix = mCamera.getViewMatrix(); mPMatrix = mCamera.getProjectionMatrix(); // Pre-multiply View and Projection matrices once for speed mVPMatrix.setAll(mPMatrix).multiply(mVMatrix); mInvVPMatrix.setAll(mVPMatrix).inverse(); mCamera.updateFrustum(mInvVPMatrix); // Update frustum plane // 更新光源的模型矩阵 synchronized (mLights) { final int numLights = mLights.size(); for (int i = 0; i < numLights; ++i) { mLights.get(i).onRecalculateModelMatrix(null); } } // Execute onPreDraw callbacks // We explicitly break out the steps here to help the compiler optimize final int preDrawCount = mPreDrawCallbacks.size(); if (preDrawCount > 0) { 执行onPreFrame回调 } if (mSkybox != null) { 绘制天空盒 } if(sceneMaterial != null) { 绑定场景的材质 } synchronized (mChildren) { 为每个模型传入Camera以及模型矩阵,视图矩阵,投影矩阵,场景的材质 绘制每个模型,此处调用Object3D类中的render方法现实 } if (mDisplaySceneGraph) { //绘制场景图,mDisplaySceneGraph一般为false mSceneGraph.displayGraph(mCamera, mVPMatrix, mPMatrix, mVMatrix); } if(sceneMaterial != null) { sceneMaterial.unbindTextures(); } synchronized (mPlugins) { 执行Plugins的render方法 } if(renderTarget != null) { renderTarget.unbind(); } // Execute onPostFrame callbacks // We explicitly break out the steps here to help the compiler optimize final int postCount = mPostCallbacks.size(); if (postCount > 0) { 执行onPostFrame回调 } }
回头来看Scane,每个renderer中都包含一个Scene mCurrentScene变量,用来标识当前的场景。下面我们就来看一下Scene中做了些什么。下面我们先大概看看其中的变量。
首先,有一个renderer的引用,用来记录对应的renderer。
protected Renderer mRenderer;
接下来时四个矩阵,物体绘制时需要用到这些矩阵
protected Matrix4 mVMatrix = new Matrix4(); protected Matrix4 mPMatrix = new Matrix4(); protected Matrix4 mVPMatrix = new Matrix4(); protected Matrix4 mInvVPMatrix = new Matrix4();
之后定义了天空盒和雾霾参数
protected volatile ColorPickerInfo mPickerInfo;
再之后就是我们在前文中向场景中添加的各种对象的列表
private final List<Object3D> mChildren; private final List<ASceneFrameCallback> mPreCallbacks; private final List<ASceneFrameCallback> mPreDrawCallbacks; private final List<ASceneFrameCallback> mPostCallbacks; private final List<Animation> mAnimations; private final List<IRendererPlugin> mPlugins; private final List<ALight> mLights;
最后当然还少不了摄像机
protected Camera mCamera;
以上是目前能大概知道意思的成员变量,可以推测Scene的主要作用是用于管理和绘制各种模型,结合上文分析我们可以印证renderer中真正执行Opengl绘制的功能,其实是在Scene中。
AFrameTask
AFrameTask是Scane频繁出现的一个类,下面我们看看它的用途。
我们发现在Scene中操作模型,光等对象时,都先New一个AFrameTask,之后返回internalOfferTask(task);
看来要搞清楚Scene的工作,必须先搞明白这个的原理。
AFrameTask的代码很简单,就是一个实现了Runnable的类。
/** * Adds a task to the frame task queue. * * @param task AFrameTask to be added. * @return boolean True on successful addition to queue. */ private boolean internalOfferTask(AFrameTask task) { synchronized (mFrameTaskQueue) { return mFrameTaskQueue.offer(task); } }
可见这里是用来同步添加物体的。
private void performFrameTasks() { synchronized (mFrameTaskQueue) { //Fetch the first task AFrameTask task = mFrameTaskQueue.poll(); while (task != null) { task.run(); //Retrieve the next task task = mFrameTaskQueue.poll(); } } }
在这里依次执行表中的每个任务,并删除相应节点。这个方法在render方法的开始调用,也就是说在执行render方法时,先检查有没有新添加的物体或者回到接口等,如果有的话,先同步将它们添加,再做下一步操作。
相关文章推荐
- OpenGLES入门笔记:Rajawali学习(3)模型绘制的基本流程
- OpenGLES入门笔记:Rajawali学习(3.1)球体的绘制
- OpenGLES入门笔记:Rajawali学习(1)基本功能初探
- OpenGLES入门笔记:Rajawali学习(4)物体点击事件的实现
- H.264 MV 学习笔记1:Inter Prediction 基本流程
- SAP学习笔记(MM的基本流程)
- 【D3D11游戏编程】学习笔记十一:基本几何体绘制
- 【Java学习笔记】ThreadPoolExecutor 基本概念入门
- Direct3D 9学习笔记(3)基本顶点绘制
- 韩顺平_php从入门到精通_视频教程_第2讲_html运行原理②_html文件基本结构_html元素和属性_学习笔记_源代码图解_PPT文档整理
- Direct3D 9学习笔记(4)基本顶点绘制呈现
- PJSIP学习笔记——从simple_pjsua.c示例程序了解PJSUA-LIB的基本使用流程
- MonoRail学习笔记四:MonoRail基本流程分析
- java 学习笔记(入门篇)_程序流程控制结构和方法
- PJSIP学习笔记——从simple_pjsua.c示例程序了解PJSUA-LIB的基本使用流程
- cocos2d-x学习笔记03:绘制基本图元
- H.264 MV 学习笔记1:Inter Prediction 基本流程
- Oracle RAC学习笔记:基本概念及入门
- Oracle RAC学习笔记:基本概念及入门
- Oracle RAC学习笔记:基本概念及入门