美文网首页
Google Filament学习-从Android官方项目分析

Google Filament学习-从Android官方项目分析

作者: Lucky胡 | 来源:发表于2019-11-25 13:56 被阅读0次

    上一节将Filament编译完成,并成功运行了Google官方的Android实例程序。
    Filament初探-For Android

    接下来从分析Android的官方项目,分析Filament的整体架构和原理。
    hello-camera工程比较简单,就一个MainActivity和相机工具类CameraHelper。

    一、引入Filament库

    先分析app子目录里的build.gradle:

    1. 引入各种工具库

    apply from: '../../../build/filament-tasks.gradle'
    
    compileMaterials {
        group 'Filament'
        description 'Compile materials'
    
        inputDir = file("src/main/materials")
        outputDir = file("src/main/assets/materials")
    }
    
    preBuild.dependsOn compileMaterials
    

    在filament-tasks.gradle里,一共引入了4个工具库(见上一节)。
    可以调用三个方法来预编译某些类。

    task compileMaterials(type: MaterialCompiler)
    
    task generateIbl(type: IblGenerator)
    
    task compileMesh(type: MeshCompiler)
    

    而demo工程里build.gradle使用了compileMaterials方法,对src/main/materials里的lib.mat进行了预编译,生成了src/main/assets/materials里的lit.filamat文件。

    当然也可以不用这种方法生成,可以手动调用matc工具进行编译:

    matc -p mobile -a opengl -o app/src/main/assets/lit.filamat app/src/materials/lit.mat
    

    材质lit.mat分析

    关于Filament的材质,可以在Filament Materials里看使用说明。
    Filament Materials

    2.引入Filament的Android使用库

    在项目的setting.gradle里引入了filament-android项目。

    includeBuild '../../filament-android'
    

    在build.gradle里进行了依赖

        // Depend on Filament
        implementation 'com.google.android.filament:filament-android'
    

    在项目中调用了filament-android项目来使用filament for android。

    二、使用Filament库

    1. 初始化

    调用Filament.init()
    实际就做了两件事:

    //初始化平台相关,主要是跟渲染界面相关
            Platform.get();
    //引入filament-android里生成的filament-jni库
            System.loadLibrary("filament-jni");
    

    看看Platform类做了什么:

    //返回Platform实例,如果是Android就返回AndroidPlatform实现。
    Platform get(){}
    
    //判断平台类型
    ...省略
    
    //打印log
    ...省略
    
    //判断渲染窗口相关的类是否合规
        abstract boolean validateStreamSource(Object object);
        abstract boolean validateSurface(Object object);
        abstract boolean validateSharedContext(Object object);
    
    //获取渲染sharedContext
         * @param sharedContext  A platform-dependant OpenGL context used as a shared context
         *                       when creating filament's internal context. On Android this parameter
         *                       <b>must be</b> an instance of {@link android.opengl.EGLContext}.
    
     abstract long getSharedContextNativeHandle(Object sharedContext);
    

    AndroidPlatform类是Platform类的实现类。就是给下面提供EGLContext句柄,用于OpenGL渲染。

    2. UI工具类 UiHelper - 桥接SurfaceView和Filament

    首先看官方提供的UiHelper使用示例:

    public class FilamentActivity extends Activity {
        static {
            Filament.init();
        }
        private UiHelper mUiHelper;
        private SurfaceView mSurfaceView;
    
        // Filament specific APIs
        private Engine mEngine;
        private Renderer mRenderer;
        
        // com.google.android.filament.View, not android.view.View
        private View mView; 
        private SwapChain mSwapChain;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            // Create a SurfaceView and add it to the activity
            mSurfaceView = new SurfaceView(this);
            setContentView(mSurfaceView);
    
            // Create the Filament UI helper
            mUiHelper = new UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK);
    
            // Attach the SurfaceView to the helper, you could do the same with a TextureView
            mUiHelper.attachTo(mSurfaceView);
    
            // Set a rendering callback that we will use to invoke Filament
            mUiHelper.setRenderCallback(new UiHelper.RendererCallback() {
                @Override
                public void onNativeWindowChanged(Surface surface) {
                    if (mSwapChain != null) {
                        mEngine.destroySwapChain(mSwapChain);
                    }
                    mSwapChain = mEngine.createSwapChain(surface, mUiHelper.getSwapChainFlags());
                }
    
                // The native surface went away, we must stop rendering.
                @Override
                public void onDetachedFromSurface() {
                    if (mSwapChain != null) {
                        mEngine.destroySwapChain(mSwapChain);
    
                        // Required to ensure we don't return before Filament is done executing the
                        // destroySwapChain command, otherwise Android might destroy the Surface
                        // too early
                        mEngine.flushAndWait();
    
                        mSwapChain = null;
                    }
                }
    
                // The native surface has changed size. This is always called at least once
                // after the surface is created (after onNativeWindowChanged() is invoked).
                @Override
                public void onResized(int width, int height) {
                    // Compute camera projection and set the viewport on the view
                }
            });
    
            mEngine = Engine.create();
            mRenderer = mEngine.createRenderer();
            mView = mEngine.createView();
            // Create scene, camera, etc.
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            // Always detach the surface before destroying the engine
            mUiHelper.detach();
    
            mEngine.destroy();
        }
    
        // This is an example of a render function. You will most likely invoke this from
        // a Choreographer callback to trigger rendering at vsync.
        public void render() {
            if (mUiHelper.isReadyToRender()) {
                // If beginFrame() returns false you should skip the frame
                // This means you are sending frames too quickly to the GPU
                if (mRenderer.beginFrame(mSwapChain)) {
                    mRenderer.render(mView);
                    mRenderer.endFrame();
                }
            }
        }
    }
    

    UiHelper就是封装了SurfaceTexture和TextureView,将Android的渲染界面的变化统一为一个回调RendererCallback供外界调用。
    其中有一个变量mOpaque,用来控制渲染背景是黑色的还是白色的。

    还提供了一个渲染的简单使用示例,提到了需要在编舞者Choreographer的回调里使用触发渲染。

        // This is an example of a render function. You will most likely invoke this from
        // a Choreographer callback to trigger rendering at vsync.
        public void render() {
            if (mUiHelper.isReadyToRender()) {
                // If beginFrame() returns false you should skip the frame
                // This means you are sending frames too quickly to the GPU
                if (mRenderer.beginFrame(mSwapChain)) {
                    mRenderer.render(mView);
                    mRenderer.endFrame();
                }
            }
        }
    

    编舞者Choreographer

    编舞者Choreographer的作用之一是配合VSync使用,统一动画、绘制、输入的时机,介绍的文章挺多。
    简单来说,在本例中用到了doFrame()回调,用于知道目前可以绘制了,Choreographer配合VSync的默认渲染时间是16ms,也就是60fps。当可以绘制时,会产生doFrame()回调,在这个回调里进行Filament的绘制更新。

    Choreographer详解

    Choreographer编舞者时序图

    3. Filament的渲染过程

    先利用Filament官方文档里的C++使用说明进行简要介绍。

    第一步:首先需要3个类,Engine+SwapChain+Renderer,这构建了画布。

    Engine* engine = Engine::create();
    SwapChain* swapChain = engine->createSwapChain(nativeWindow);
    Renderer* renderer = engine->createRenderer();
    

    第二步:要渲染Frame需要3个类,View+Camera+Scene。

    Camera* camera = engine->createCamera();
    View* view = engine->createView();
    Scene* scene = engine->createScene();
    
    view->setCamera(camera);
    view->setScene(scene);
    

    第三步:如何将一个东西渲染到Scene呢?

    //构造一个可渲染类Entity
    Entity renderable = EntityManager::get().create();
    // build a quad
    RenderableManager::Builder(1)
            .boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
            .material(0, materialInstance)
            .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vertexBuffer, indexBuffer, 0, 6)
            .culling(false)
            .build(*engine, renderable);
    scene->addEntity(renderable);
    

    第四步:如何生成渲染材质?
    其中,
    Builder& package(const void* payload, size_t size);方法传递两个参数,分别是材质的data和材质data文件的大小。
    材质如何生成呢?就是在文章开头介绍的matc生成的。

    Material* material = Material::Builder()
            .package((void*) BAKED_MATERIAL_PACKAGE, sizeof(BAKED_MATERIAL_PACKAGE))
            .build(*engine);
    MaterialInstance* materialInstance = material->createInstance();
    

    第五步:将view传递给renderer

    // beginFrame() returns false if we need to skip a frame
    if (renderer->beginFrame(swapChain)) {
        // for each View
        renderer->render(view);
        renderer->endFrame();
    }
    

    渲染步骤
    生成渲染材质 --> 给可渲染的Entity --> 将Entity给scene -->scene添加到了View上 --> 每一帧调用renderre->render(view)

    渲染窗口是用swapchain持有并传递到渲染引擎里,是否略过帧用renderer->begineFrame(swapchain)进行判断。

    所有的view+renderer+swapchain+scene都是Engine生成的。

    Android项目里使用Filament就是用NDK在java层调用上面的native代码。

    4. 分析Android项目Filament

    见下一节
    Filament分析-从Android官方项目分析2

    相关文章

      网友评论

          本文标题:Google Filament学习-从Android官方项目分析

          本文链接:https://www.haomeiwen.com/subject/fdodwctx.html