OpenGLES入门笔记:Rajawali学习(2)场景绘制基本流程

背景

上一篇文章中我们简单体验了一下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();
  • 1

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();
     ...
  • 1

其中mEglHelper.start()的作用是初始化了如下OpenGL变量

        EGL10 mEgl;
        EGLDisplay mEglDisplay;
        EGLSurface mEglSurface;
        EGLConfig mEglConfig;
        EGLContext mEglContext;
  • 1

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);
    }
  • 1

构造函数中,我们将帧率等属性进行赋值。注意获取属性列表的方式:

TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TextureView);
  • 1

下面看一下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;
        }
  • 1

此处执行我们上文提到过的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

上面一堆主要做了以下几件事

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;
  • 1

构造方法

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);
        }
    }
  • 1

开启渲染
此处开启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);
    }
  • 1

下面看一下RequestRenderTask里做了什么

private class RequestRenderTask implements Runnable {
        public void run() {
            if (mSurface != null) {
                mSurface.requestRenderUpdate();
            }
        }
    }
  • 1

我们发现这里又回到了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
        }
  • 1

onRender

protected void onRender(final long ellapsedRealtime, final double deltaTime) {
        render(ellapsedRealtime, deltaTime);
    }
  • 1

找到render方法

protected void render(final long ellapsedRealtime, final double deltaTime) {
        mCurrentScene.render(ellapsedRealtime, deltaTime, mCurrentRenderTarget);
    }
  • 1

可见,最终的渲染其实是调用了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回调
        }
    }
  • 1

回头来看Scane,每个renderer中都包含一个Scene mCurrentScene变量,用来标识当前的场景。下面我们就来看一下Scene中做了些什么。下面我们先大概看看其中的变量。

首先,有一个renderer的引用,用来记录对应的renderer。

protected Renderer mRenderer;
  • 1

接下来时四个矩阵,物体绘制时需要用到这些矩阵

    protected Matrix4 mVMatrix = new Matrix4();
    protected Matrix4 mPMatrix = new Matrix4();
    protected Matrix4 mVPMatrix = new Matrix4();
    protected Matrix4 mInvVPMatrix = new Matrix4();
  • 1

之后定义了天空盒和雾霾参数

protected volatile ColorPickerInfo      mPickerInfo;
  • 1

再之后就是我们在前文中向场景中添加的各种对象的列表

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;
  • 1

最后当然还少不了摄像机

protected Camera mCamera;
  • 1

以上是目前能大概知道意思的成员变量,可以推测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);
        }
    }
  • 1

可见这里是用来同步添加物体的。

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();
            }
        }
    }
  • 1

在这里依次执行表中的每个任务,并删除相应节点。这个方法在render方法的开始调用,也就是说在执行render方法时,先检查有没有新添加的物体或者回到接口等,如果有的话,先同步将它们添加,再做下一步操作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是原来的你吗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值