【Sceneform-EQR】在安卓中使用filament(sceneform)材质(二)

本文介绍了基于谷歌存档的Sceneform开发的EQ - R,它是用于安卓端的三维AR渲染器,解决了内存泄漏等问题。同时详细阐述了Filament材质,包括核心概念、模型、定义、编译及调用方法,还给出了材质示例,可帮助开发者在Android Studio中进行AR开发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前面的话

Sceneform源于谷歌,现已存档。之后ThomasGorisse基于sceneform1.16之后维护过一段时间。现已停止更新。
本文档中的sceneform是基于谷歌存档的。自2020年6月起,一直在持续迭代中,相关描述可能与上述版本有变化,但官方文档依然可做重要参考。

  • 借助 Sceneform,可以轻松地在 AR 应用和非 AR 应用中渲染逼真的 3D 场景,而无需学习 OpenGL。

  • Sceneform基于filament,了解filament有助于使用Scenefrom。

本人从sceneform1.10的版本开始使用,至1.16版本存档。参考了众多私人开源的sceneform版本。基于此做了 EQ-Renderer,EQ-R是sceneform1.16版本的延续。

它包含sceneform_v1.16.0中九成接口,剔除了如sfb资源加载等已弃用的内容,扩展了视频背景视图(在实际使用中,我接入了无人机实时画面,替换ARCore、AREngine获取的相机画面)。
此外,解决了sceneform模型加载的内存泄漏问题、集成了AREngine和ORB-SLAM3、添加了场景坐标与地理坐标系(CGCS-2000)的转换方法。

本专栏主要介绍如何在AndroidStudio中使用它快速地进行AR开发。
抽空更新中…

Sceneform-EQR

简介

EQ-Renderer是EQ基于sceneform(filament)扩展的一个用于安卓端的三维AR渲染器。

主要功能

它包含sceneform_v1.16.0中九成接口(剔除了如sfb资源加载等已弃用的内容),扩展了视频背景视图、解决了sceneform模型加载的内存泄漏问题、集成了AREngine和ORB-SLAM3、添加了场景坐标与地理坐标系(CGCS-2000)的转换方法。

注:由于精力有限,文档和示例都不完善。sceneform相关请直接参考谷歌官方文档,扩展部分接口说明请移步git联系。

相关链接

Git仓库
码云
EQ-R相关文档

Filament材质

注:本节内容源于filament材质文档,更多详情请转至原链接查看。

相关文档

概述

Filament是一个基于物理的渲染(PBR)引擎, 用于Android. Filament提供了一个可定制的材质系统, 你可以用它来创建简单材质和复杂材质. 本文档介绍材质支持的所有功能以及如何创建自己的材质.

核心概念

材质: 材质定义了表面的视觉外观. 为完整描述和渲染表面, 材质提供以下信息:

  • 材质模型
  • 一组用户可控制的命名参数
  • 光栅状态(混合模式, 背面剔除等)
  • 顶点着色器代码
  • 片段着色器代码

材质模型: 也称 着色模型 或 光照模型, 材质模型定义表面的内在特性. 这些特性直接影响光照的计算方式, 从而影响表面的外观.

材质定义: 描述材质所需的所有信息的文本文件. 这是你可以直接编写以创建新材质的文件.

材质包: 在运行时, 从 材质包 中加载材质, 材质包是使用matc工具根据材质的定义编译得到的. 材质包含了描述材质所需的所有信息, 以及为目标运行时平台生成的着色器. 这是必要的, 因为不同的平台(Android, macOS, Linux等)使用不同的图形API, 或相似图形API的不同变体(例如OpenGL与OpenGL ES).

材质实例: 一个材质实例是对材质的引用, 以及对应该材质的不同参数的一组值. 本文档不包括材质实例, 因为可以直接使用Filament代码创建和操控它们。

材质模型

Filament支持以下材质模型:

  • 光亮(或标准)
  • 次表面
  • 布料
  • 无光亮
  • 镜面光泽(用于以前的模型)

材质定义

一个材质定义是描述材质所需的所有信息的一个文本文件:

  • 名称
  • 用户参数
  • 材质模型
  • 必需属性
  • 插值(称为 变量)
  • 光栅状态(混合模式等)
  • 着色器代码(片段着色器, 可选的顶点着色器)

材质编译

matc工具

可以使用名为matc的命令行工具从材质定义编译材质包. 使用matc的最简单的方法是, 指定一个输入材质定义(下面示例中的car_paint.mat)和一个输出材质包(下面示例中的car_paint.filamat):

matc.exe工具编译过程请参考在安卓中使用filament(sceneform)材质(一)

matc -o ./materials/bin/car_paint.filamat ./materials/src/car_paint.mat
批量编译
  1. 在window电脑创建一个"genfilamat.bat"文件,使用编辑器打开,输入以下内容。
@echo off
if not exist out mkdir out
for /R %%G in (*.mat) do matc --optimize-size --platform=mobile -o "out\%%~nG.filamat" "%%G"

@echo off
echo success!
pause

  1. 将多个材质文件(*.mat) 拷贝至 “genfilamat.bat” 的同级目录。
  2. 运行 “genfilamat.bat” ,输出结果在同级目录的/out文件夹中。

材质调用

下文将通过一个材质示例,来讲述如何在【EQ-R】中调用filament材质实现法线贴图

编写材质

现要创建一个支持法线贴图的材质文件,步骤如下:

  1. 创建名为“eq_opaque_mat.mat”的文件
  2. 通过编辑器(记事本)打开mat文件,填写如下内容。
material {
    name : "EQ Opaque Mat",

    parameters : [
        {
           type : sampler2d,
           name : texture
        },
        {
           type : sampler2d,
           name : normalMap
        },
        {
           type : float,
           name : metallic
        },
        {
            type : float,
            name : roughness
        },
        {
            type : float,
            name : reflectance
        },
        {
            type : float4,
            name : emissive
        }
    ],
    requires : [
        position,
        uv0
    ],
    shadingModel : lit,
    blending : opaque
}
fragment {
    void material(inout MaterialInputs material) {
        // 获取切线空间中的法线,normal属性必须在prepareMaterial()前调用
        vec3 normal = texture(materialParams_normalMap, getUV0()).xyz;
        material.normal = normal * 2.0 - 1.0;
        prepareMaterial(material);
        //基色
        material.baseColor = texture(materialParams_texture, getUV0());
        //金属度
        material.metallic = materialParams.metallic;
        //粗糙度
        material.roughness = materialParams.roughness;
        //反射率
        material.reflectance = materialParams.reflectance;
        //自发光
        material.emissive = materialParams.emissive;
    }
}

上述的文本内容可以根据filament材质文档做修改,标准模型的材质参数范围和类型如下:

baseColor 基色float4[0…1]预乘的线性RGB
metallic 金属度float[0…1]应为0或1
roughness 粗糙度float[0…1]
reflectance 反射率float[0…1]首选值 > 0.35
clearCoat 涂层float[0…1]应为0或1
clearCoatRoughness 涂层粗糙度float[0…1]重新映射到[0…0.6]
anisotropy 各向异性度float[−1…1]当此值为正时, 各向异性位于切线方向
anisotropyDirection 各向异性方向float3[0…1]线性RGB, 编码切线空间中的方向向量
ambientOcclusion 环境光遮蔽float[0…1]
normal 法线float3[0…1]线性RGB, 编码切线空间中的方向向量
clearCoatNormal 涂层法线float3[0…1]线性RGB, 编码切线空间中的方向向量
emissive 自发光float4rgb=[0…1], a=[-n…n]Alpha为是曝光补偿
postLightingColor 后处理光照颜色float4[0…1]预乘的线性RGB

编译材质

通过matc工具编译得到(“eq_opaque_mat.filamat”)文件

调用材质

以下内容均在AndroidStudio中操作,使用Java接口

  1. 将“eq_opaque_mat.filamat“文件拷贝至项目模块的/res/raw目录下(这里我在res目录下创建了名为raw的文件夹,创建其它名称也可,只要能通过资源ID找到这个文件即可)
  2. 编写代码调用材质。
        Bitmap normalMap = loadBitmapFromAsstes("MATERIALS/org_fur/normal.png");
        Bitmap srcBm = loadBitmapFromAsstes("MATERIALS/org_fur/basecolor.png");

        Material.builder()
                .setSource(context, LoadHelper.rawResourceNameToIdentifier(context, "eq_opaque_mat"))
                .build().thenAccept(new Consumer<Material>() {
                    @Override
                    public void accept(Material mat) {
                        material = mat;
                        material.setFloat("reflectance",0.1f);
                        material.setFloat("roughness",0.6f);
                        material.setFloat("metallic",0.0f);
                        Texture.builder()
                                .setSource(normalMap)
                                .build().thenAccept(new Consumer<Texture>() {
                                    @Override
                                    public void accept(Texture normalMapTexture) {
                                        material.setTexture("normalMap", normalMapTexture);
                                    }
                                });
                        Texture.builder()
                                .setSource(srcBm)
                                .build().thenAccept(new Consumer<Texture>() {
                                    @Override
                                    public void accept(Texture srcMapTexture) {
                                        material.setTexture("texture",srcMapTexture);
                                    }
                                });
                    }
                });

此外,之前的“eq_opaque_mat.filamat“文件也可以放于其它能访问到的路径。

通过以下方式进行访问。

Material.builder().setSource(this,Uri.fromFile(new File(path)));
  1. 修改材质参数

​ 在材质build之后,可以通过material.setXX相关方法动态地对参数进行修改。参数名为“编写材质”步骤中的“parameters”对应的name

材质示例

Opaque Colored

material {
    "name" : "Opaque Colored",

    "parameters" : [
        {
           "type" : "float3",
           "name" : "color"
        },
        {
           "type" : "float",
           "name" : "metallic"
        },
        {
            "type" : "float",
            "name" : "roughness"
        },
        {
            "type" : "float",
            "name" : "reflectance"
        },
        {
            "type" : "float4",
            "name" : "emissive"
        }
    ],
    "requires" : [
        "position",
        "uv0"
    ],
    "shadingModel" : "lit",
    "blending" : "opaque",
    doubleSided : true
}
fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        material.baseColor.rgb = materialParams.color;
        material.metallic = materialParams.metallic;
        material.roughness = materialParams.roughness;
        material.reflectance = materialParams.reflectance;
        material.emissive = materialParams.emissive;
    }
}

Opaque Textured

material {
    "name" : "Opaque Textured",

    "parameters" : [
        {
           "type" : "sampler2d",
           "name" : "texture"
        },
        {
           "type" : "float",
           "name" : "metallic"
        },
        {
            "type" : "float",
            "name" : "roughness"
        },
        {
            "type" : "float",
            "name" : "reflectance"
        },
        {
            "type" : "float4",
            "name" : "emissive"
        }
    ],
    "requires" : [
        "position",
        "uv0"
    ],
    "shadingModel" : "lit",
    "blending" : "opaque",
    doubleSided : true
}
fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        material.baseColor = texture(materialParams_texture, getUV0());
        material.metallic = materialParams.metallic;
        material.roughness = materialParams.roughness;
        material.reflectance = materialParams.reflectance;
        material.emissive = materialParams.emissive;
    }
}

Transparent Colored

material {
    "name" : "Transparent Colored",

    "parameters" : [
        {
           "type" : "float4",
           "name" : "color"
        },
        {
           "type" : "float",
           "name" : "metallic"
        },
        {
            "type" : "float",
            "name" : "roughness"
        },
        {
            "type" : "float",
            "name" : "reflectance"
        },
        {
            "type" : "float4",
            "name" : "emissive"
        }
    ],
    "requires" : [
        "position",
        "uv0"
    ],
    "shadingModel" : "lit",
    "blending" : "transparent"
}
fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        material.baseColor = materialParams.color;
        material.metallic = materialParams.metallic;
        material.roughness = materialParams.roughness;
        material.reflectance = materialParams.reflectance;
        material.emissive = materialParams.emissive;
    }
}

Transparent Textured

material {
    "name" : "Transparent Textured",

    "parameters" : [
        {
           "type" : "sampler2d",
           "name" : "texture"
        },
        {
           "type" : "float",
           "name" : "metallic"
        },
        {
            "type" : "float",
            "name" : "roughness"
        },
        {
            "type" : "float",
            "name" : "reflectance"
        },
        {
            "type" : "float4",
            "name" : "emissive"
        }
    ],
    "requires" : [
        "position",
        "uv0"
    ],
    "shadingModel" : "lit",
    "blending" : "transparent"
}
fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);
        material.baseColor = texture(materialParams_texture, getUV0());
        material.metallic = materialParams.metallic;
        material.roughness = materialParams.roughness;
        material.reflectance = materialParams.reflectance;
        material.emissive = materialParams.emissive;
    }
}

View

material {
    "name" : "View",
    "parameters" : [
        {
           "type" : "samplerExternal",
           "name" : "viewTexture"
        },
        {
            "type" : "float2",
            "name" : "offsetUv"
        },
        {
            "type" : "float4",
            "name" : "emissive"
        }
    ],
    "requires" : [
        "position",
        "uv0"
    ],
    "shadingModel" : "unlit",
    "blending" : "transparent",
    "doubleSided" : true
}

fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);

        vec2 uv = getUV0();

        if (!gl_FrontFacing) {
          uv.x = 1.0 - uv.x;
        }

        // Set offsetUv if we want to invert around an axis.
        // In front facing camera, set offsetUv.x to 1 and offsetUv.y to 0.
        uv.x = uv.x + materialParams.offsetUv.x * (1.0 - 2.0 * uv.x);
        uv.y = uv.y + materialParams.offsetUv.y * (1.0 - 2.0 * uv.y);

        material.baseColor = texture(materialParams_viewTexture, uv);
        material.baseColor.rgb = inverseTonemapSRGB(material.baseColor.rgb);
        material.emissive = materialParams.emissive;
    }
}

Camera

material {
    "name" : "Camera",

    "parameters" : [
        {
           "type" : "samplerExternal",
           "name" : "cameraTexture"
        }
    ],
    "requires" : [
        "uv0"
    ],
    "vertexDomain" : "device",
    "depthWrite" : false,
    "shadingModel" : "unlit",
    "doubleSided" : true
}
fragment {
    void material(inout MaterialInputs material) {
        prepareMaterial(material);

        vec4 color = texture(materialParams_cameraTexture, getUV0());
        material.baseColor.rgb = inverseTonemapSRGB(color.rgb);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

EQ-雪梨蛋花汤

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

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

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

打赏作者

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

抵扣说明:

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

余额充值