首页 元宇宙 零基础开放元宇宙|如何快速创建虚像

零基础开放元宇宙|如何快速创建虚像

元宇宙是人类利用数字技术构建的虚拟世界,被现实世界映射或超越,并能与现实世界互动,拥有一个新的社会体系的数字生活空间。 …

零基础开启元宇宙|如何快速创建虚拟形象

元宇宙是人类利用数字技术构建的虚拟世界,被现实世界映射或超越,并能与现实世界互动,拥有一个新的社会体系的数字生活空间。

可见,超宇宙的第一步是创建专属虚拟形象,但创建3D虚拟形象需要3D基础知识。对于大部分安卓开发者(包括我自己)来说,没有这方面的积累。我们很难进入超宇宙的世界吗?不,今天有了即时平台提供的头像SDK,只要有安卓基础,就能进入最火热的超宇宙世界!先看效果:

上面的gif压缩的相当苛刻,所以这里是截图:

1免费注册:即时开发者去即时控制台网站注册开发者账号。注册成功后,创建一个项目:

你可以在控制台中得到AppID和AppSign两个数据,这是很重要的凭证,后面会用到。

因为我们使用的是内置的头像功能,但是目前官方并没有提供在线自动打开的方法,所以需要我们主动向客服申请(当然这个是免费的),提供自己项目的包名就可以打开头像权限。打开右下角的“联系我们”,点击即可申请免费获取客服。

注意,如果没有向客服申请头像权限,调用AvatarSDK会失败!

2准备开发环境,从元宇宙开发SDK官方网站下载SDK,得到如下文件列表:

下一个过程如下:

打开SDK目录,将其中的ZegoAvatar.aar复制到app/libs目录。添加SDK引用。打开app/build.gradle文件,导入所有jar和AAR:dependencies { implementation file tree(dir:' libs 'include: ['*。jar '' *。AAR '])//通配符导入//其他缩写}来设置权限。根据实际应用需求,设置应用所需的权限。进入app/src/main/Android manifest . XML文件并添加权限。uses-permission Android:name=' Android . permission . record _ AUDIO '/

uses-permission Android:name=' Android . permission . internet '/

uses-permission Android:name=' Android . permission . access _ WIFI _ STATE '/

uses-permission Android:name=' Android . permission . access _ NETWORK _ STATE '/

uses-permission Android:name=' Android . permission . camera '/

uses-permission Android:name=' Android . permission . bluetooth '/

uses-permission Android:name=' Android . permission . modify _ AUDIO _ SETTINGS '/

uses-permission Android:name=' Android . permission . write _ EXTERNAL _ STORAGE '/

uses-permission Android:name=' Android . permission . read _ EXTERNAL _ STORAGE '/

用途-功能

Android:gles version='0x 00020000 '

android:required='true' /

uses-feature Android:name=' Android . hardware . camera '/

uses-feature Android:name=' Android . hardware . camera . auto focus '/由于Android 6.0要求在一些重要权限上必须申请动态权限,所以不能只通过AndroidMainfest.xml文件申请静态权限。具体的动态请求权限代码,请参考附后的源代码。

3导入资源在上一节下载的zip文件中,有一个资产目录。包含头像形象相关资源,比如衣服,眉毛,鞋子等等。这是政府免费提供的资源,可以满足一般需求。当然,如果你想自定义自己的资源,也可以。资产文件的内容如下:

资源名称

解释

AIModel.bundle

阿凡达的AI模型资源。使用表情跟随、语音跟随、AI捏脸等能力时。必须先将该资源的绝对路径设置为Avatar SDK。

基础包

美术资源,包括基础3D人物模型资源、资源映射表、人物模型默认外观等。

包装

美容化妆品、吊坠、装饰品等资源。每个资源从200 kb到1 MB不等,和资源的复杂程度有关。

注:由于资源文件较大,以上下载的美术资源仅包含少量必要的资源。如果需要所有资源,可以去官网问客服。

以上文件需要保存在Android本地的SDCard上。这里有两个方案供参考:

方案一:将资源先放入到应用程序/服务资源/资产目录内,然后在应用启动时,自动将资产的内容拷贝到SDcard中。方案二:将资源放入到服务器端,运行时自动从服务器端下载。为了简单起见,我们这里采用方案一。参考代码如下,详细代码可以看附件:

资产文件转移。copyassetsdir 2手机(这个。获取应用程序(),

AIModel.bundle '' assets ');

资产文件转移。copyassetsdir 2手机(这个。获取应用程序(),

base.bundle '' assets ');

资产文件转移。copyassetsdir 2手机(这个。获取应用程序(),

包''资产');四创建虚拟形象创建虚拟形象本质上来说就是调用即构的阿凡达SDK,其大致流程如下:

接下来我们逐步实现上面流程。

需要注意的是,上面示意图中采用的是全景图,可以非常方便的直接展示电影《阿凡达》形象,但是不方便后期将画面通过雷达跟踪中心(雷达跟踪中心的缩写)实时传递,因此,我们后面的具体实现视通过TextureView替代全景图。

4.1 申请权鉴这里再次强调一下,一定要打开点击右下角有\”联系我们\”,向客服申请免费开通电影《阿凡达》权限。否则无法使用阿凡达SDK。

申请权鉴代码如下:

公共类密钥中心{

//控制台地址:https://console . zego . im/仪表板

公共静态长APP_ID=这里值可以在控制台查询,参考第一节;//这里填写APPID

公共静态字符串APP_SIGN=这里值可以在控制台查询,参考第一节;

//鉴权服务器的地址

公开决赛静态字符串后端_ API _ URL=' https://ai效果-API。泽戈。im?action=DescribeAvatarLicense '

公共静态字符串avatarLicense=null

公共静态字符串getURL(字符串authInfo) {

尤里builder builder=uri。parse(后端API URL).构建于();

建筑商。appendqueryparameter(' AppId '字符串。(APP _ ID)的值;

建筑商。appendqueryparameter(' AuthInfo 'AuthInfo);

返回builder.build().toString();

}

公共接口IGetLicenseCallback {

void onGetLicense(int代码,字符串消息,ZegoLicense许可证);

}

/**

* 在线拉取许可证

* @param上下文

* @param回调

*/

公共静态void get许可证(上下文Context,final IGetLicenseCallback回调){

请求许可(zegoavatarservice。getauthinfo(APP _ SIGN,context),回调);

}

/**

* 获取许可证

* */

公共静态void请求许可证(字符串authInfo,最终IGetLicenseCallback回调){

string URL=getURL(authInfo);

HttpRequest.asyncGet(url,ZegoLicense.class,(代码,消息,responseJsonBean) – {

如果(回调!=null) {

callback.onGetLicense(代码、消息、响应JSON bean);

}

});

}

公共类ZegoLicense

@SerializedName('许可证)

私有字符串许可证;

公共字符串getLicense() {

归还许可证;

}

公共作废setLicense(字符串许可证){

this.license=许可证;

}

}

}在获取林肯斯时,只需调用获取许可证函数,例如在活动类中只需如下调用:

KeyCenter.getLicense(this,(代码,消息,响应)- {

if (code==0) {

钥匙中心。头像授权=响应。获取许可证();

showLoading('正在初始化.');

avatarMngr=avatarMngr。getinstance(get application());

avatarmngr。设置许可证(密钥中心。头像执照,这个);

}否则{

土司('执照获取失败,代码:'代码);

}

});4.2 初始化AvatarService初始化AvatarService过程比较漫长(可能要几秒),通过开启工人线程后台加载以避免主线程阻塞。因此我们定义一个回调函数,待完成初始化后回调通知:

公共接口OnAvatarServiceInitSucced {

void onInitSucced();

}

公共void setLicense(字符串许可证,OnAvatarServiceInitSucced侦听器){

this.listener=监听器

zegoavatarservice。addserviceobserver(this);

string ai path=file utils。getphonepath(mApp,' AIModel.bundle '' assets ');//AI模型的绝对路径

ZegoServiceConfig config=new ZegoServiceConfig(许可证,ai路径);

ZegoAvatarService.init(mApp,config);

}

@覆盖

公共void onStateChange(ZegoAvatarServiceState状态){

if(state==ZegoAvatarServiceState .InitSucceed) {

Log.i('ZegoAvatar '' Init success ');

//要记得及时移除通知

zegoavatarservice。removeserviceobserver(this);

如果(听者!=null)侦听器。oninitsucced();

}

}这里设置许可证函数内完成初始化AvatarService,初始化完成后会回调onStateChange函数。但是要注意,在初始化之前必须把资源文件拷贝到本地南达科他州卡,即完成资源导入:

私有void initRes(应用程序){

//先把资源拷贝到南达科他州卡,注意:线上使用时,需要做一下判断,避免多次拷贝。资源也可以做成从网络下载。

如果(!FileUtils.checkFile(app,' AIModel.bundle '' assets '))

fileutils。copyassetsdir 2手机(app,' AIModel.bundle '' assets ');

如果(!FileUtils.checkFile(app,' base.bundle '' assets '))

文件实用程序。copyassetsdir 2手机(app,' base.bundle '' assets ');

如果(!FileUtils.checkFile(app,' human.bundle '' assets '))

fileutils。copyassetsdir 2手机(app,' human.bundle '' assets ');

如果(!FileUtils.checkFile(应用程序、\”包\”、\”资产\”))

fileutils。copyassetsdir 2手机(应用程序,包''资产');

}4.3 创建虚拟形象前面在下载软件开发工具包(软件开发工具包)包含了助手目录,这个目录里面有非常重要的两个文件:

其中ZegoCharacterHelper文件是个接口定义类,即虽然是个类,但具体的实现全部在ZegoCharacterHelperImpl中。我们先一睹为快,看看ZegoCharacterHelper包含了哪些可处理的属性:

公共类ZegoCharacterHelper {

公共静态最终字符串MODEL _ ID _ MALE='男性

public static final String MODEL _ ID _ FEMALE=' FEMALE '

//****************************** 捏脸维度的键值******************************/

公共静态最终字符串脸型_眉毛_大小_ Y='脸型_眉毛_大小_ Y '//眉毛厚度,取值范围0.0-1.0,默认值0.5

公共静态最终字符串脸型_眉毛_大小_ X='脸型_眉毛_大小_ X '//眉毛长度,取值范围0.0-1.0,默认值0.5

public static final String face shape _ BROW _ ALL _ Y=' face shape _ BROW _ ALL _ Y '//眉毛高度,取值范围0.0-1.0,默认值0.5

public static final String face shape _ BROW _ ALL _ ROLL _ Z=' face shape _ BROW _ ALL _ ROLL _ Z '//眉毛旋转,取值范围0.0-1.0,默认值0.5

公共静态最终字符串脸型_眼睛_大小='脸型_眼睛_大小'//眼睛大小,取值范围0.0-1.0,默认值0.5

公共静态最终字符串脸型_眼睛_大小_ Y='脸型_眼睛_大小_ Y '

公共静态最终字符串脸型_眼_卷_ Y='脸型_眼_卷_ Y '//眼睛高度,取值范围0.0-1.0,默认值0.5

公共静态最终字符串脸型_眼_卷_ Z='脸型_眼_卷_ Z '//眼睛旋转,取值范围0.0-1.0,默认值0.5

公共静态最终字符串脸型_眼睛_ X='脸型_眼睛_ X '//双眼眼距,取值范围0.0-1.0,默认值0.5

公共静态最终字符串脸型_鼻子_所有_ X='脸型_鼻子_所有_ X '//鼻子宽度,取值范围0.0-1.0,默认值0.5

公共静态最终字符串脸型_鼻子_所有_ Y='脸型_鼻子_所有_ Y '//鼻子高度,取值范围0.0-1.0,默认值0.5

公共静态最终字符串脸型_鼻子_大小_ Z='脸型_鼻子_大小_ Z '

public static final String face shape _ NOSE _ ALL _ ROLL _ Y=' face shape _ NOSE _ ALL _ ROLL _ Y '//鼻头旋转,取值范围0.0-1.0,默认值0.5

public static final String face shape _ narrow _ ROLL _ Y=' face shape _ narrow _ ROLL _ Y '//鼻翼旋转,取值范围0.0-1.0,默认值0.5

公共静态最终字符串脸型_鼻孔_ X=' faceshape _鼻孔_ X '

public static final String face shape _ MOUTH _ ALL _ Y=' face shape _ MOUTH _ ALL _ Y '//嘴巴上下,取值范围0.0-1.0,默认值0.5

公共静态最终字符串face shape _ LIP _ ALL _ SIZE _ Y=' face shape _ LIP _ ALL _ SIZE _ Y '//嘴唇厚度,取值范围0.0-1.0,默认值0.5

公共静态最终字符串脸型_唇角_ Y='脸型_唇角_ Y '//嘴角旋转,取值范围0.0-1.0,默认值0.5

公共静态最终字符串face shape _ LIP _ UPPER _ SIZE _ X=' face shape _ LIP _ UPPER _ SIZE _ X '//上唇宽度,取值范围0.0-1.0,默认值0.5

公共静态最终字符串face shape _ LIP _ LOWER _ SIZE _ X=' face shape _ LIP _ LOWER _ SIZE _ X '//下唇宽度,取值范围0.0-1.0,默认值0.5

公共静态最终字符串脸型_下巴_所有_尺寸_ X='脸型_下巴_所有_尺寸_ X '//下巴宽度,取值范围0.0-1.0,默认值0.5

公共静态最终字符串脸型_下巴_ Y='脸型_下巴_ Y '//下巴高度,取值范围0.0-1.0,默认值0.5

公共静态最终字符串脸型_脸颊_全部_大小_ X='脸型_脸颊_全部_大小_ X '//脸颊宽度,取值范围0.0-1.0,默认值0.5

//其他函数略

}可以看到,上面数据基本包含所有人脸属性了,基本具备了捏脸能力,篇幅原因,我们这里不具体去实现。有这方面需求的读者,可以通过在界面上调整上面相关属性来实现。

接下来我们开始创建虚拟形象,首先创建一个用户实体类:

公共类用户{

公共字符串用户名;//用户名

公共字符串userId//用户身份证明

公共布尔值isMan//性别

公共(同Internationalorganizations)国际组织宽度;//预览宽度

公共(同Internationalorganizations)国际组织高度;//预览高度

public int bgColor//背景颜色

public int shirtIdx=0;//T恤资源身份证明(识别)

public int浏览器idx=0;//眉毛资源身份证明(识别)

公共用户(字符串用户名,字符串用户Id,整数宽度,整数高度){

this .用户名=用户名;

this.userId=用户标识

this.width=宽度;

this.height=高度;

this.isMan=true

bgColor=Color.argb(255,33,66,99);

}

}示例作用,为了简单起见,我们这里只针对眉毛和衣服资源做选取。接下来创建一个活动:

公共类可用性活动扩展基础活动{

private int vWidth=720

私有int vHeight=1080

私有用户用户=新用户(' C_0001 '' C_0001 'vWidth,vHeight);

私有纹理查看mtextreview//用于显示电影《阿凡达》形象

private AvatarMngr mAvatarMngr//用于维护管理电影《阿凡达》

private ColorPickerDialog ColorPickerDialog;//用于背景色选取

@覆盖

受保护的void onCreate(Bundle saved instancestate){

超级棒。oncreate(savedInstanceState);

setContentView(r . layout。活动_头像);

//.

//其他初始化界面相关代码略.

//.

user.isMan=true

mtextreview=findViewById(r . id。头像_查看);

mZegoMngr=zegomngr。getinstance(get application());

//开启虚拟形象预览

mavatarmngr。开始(mtextreview,用户);

}最后一行代码中开启了虚拟形象预览,那么这里开启虚拟形象预览具体做了哪些工作呢?给我看看代码:

公共类AvatarMngr实现ZegoAvatarServiceDelegate .捕获侦听器{

私有静态最终字符串标记=' AvatarMngr

私有静态AvatarMngr实例;

私有布尔型失误=错误

私有用户mUser=null

私有纹理bgrender mbg render=null

私有OnAvatarServiceInitSucced侦听器;

private ZegoCharacterHelper mCharacterHelper;

私有应用mApp

公共接口OnAvatarServiceInitSucced {

void onInitSucced();

}

公共void setLicense(字符串许可证,OnAvatarServiceInitSucced侦听器){

this.listener=监听器

zegoavatarservice。addserviceobserver(this);

string ai path=file utils。getphonepath(mApp,' AIModel.bundle '' assets ');//AI模型的绝对路径

ZegoServiceConfig config=new ZegoServiceConfig(许可证,ai路径);

ZegoAvatarService.init(mApp,config);

}

公共无效站点(){

mIsStop=true

mUser=null

停止表达式();

}

公共void updateUser(用户用户){

mUser=用户;

if (user.shirtIdx==0) {

mcharacterhelper。设置包(' m-shirt 01 ');

}否则{

mcharacterhelper。套装(' m-shirt 02 ');

}

if (user.browIdx==0) {

mcharacterhelper。设置包(' brows _ 1 ');

}否则{

mcharacterhelper。设置包(' brows _ 2 ');

}

}

/**

* 启动阿凡达,调用此函数之前,请确保已经调用过设置许可证

*

* @param avatarView

*/

公共void start(纹理视图头像视图,用户用户){

mUser=用户;

失误=错误

initAvatar(avatarView,user);

开始表达式();

}

私有void init avatar(纹理视图avatar视图,用户user) {

String sex=ZegoCharacterHelper .型号_ ID _男性

如果(!用户。isman)sex=ZegoCharacterHelper .型号_ ID _女性;

//创建助手简化调用

//base.bundle是头模,human.bundle是全身人模

mCharacterHelper=new ZegoCharacterHelper(file utils。getphonepath(mApp,' human.bundle '' assets ');

mcharacterhelper。setextendpackagepath(文件实用程序。getphonepath(mApp,' Packages '' assets ');

//设置形象配置

mcharacterhelper。setdefaultavatar(性);

更新用户(用户);

//获取当前妆容数据,可以保存到用户资料中

string JSON=mcharacterhelper。getavatarjson();

}

//启动表情检测

私有void startExpression() {

//启动表情检测前要申请摄像头权限,这里是在主要活动已经申请过了

zegoavatarservice。getinteractengine().startDetectExpression(ZegoExpressionDetectMode .相机,表情- {

//表情直接塞给阿凡达驱动

mCharacterHelper.setExpression(表达式);

});

}

//停止表情检测

私有void stopExpression() {

//不用的时候记得停止

zegoavatarservice。getinteractengine().stopdetexpression();

}

//获取到阿凡达纹理后的处理

capturevatar上的公共void(int texture id,int width,int height) {

if (mIsStop || mUser==null) { //rtc的onStop是异步的,可能活动已经运行到onStop了,rtc还没

返回;

}

布尔函数useFBO=true

if (mBgRender==null) {

mbg render=new texture bgrender(texture id,useFBO,width,height,Texture2dProgram .程序类型。纹理_ 2D _ BG);

}

mbg渲染。设置输入纹理(纹理id);

浮动r=颜色。红色(muser。bgcolor)/255 f;

浮动g=颜色。绿色(muser。bgcolor)/255 f;

浮动b=颜色。蓝色(muser。bgcolor)/255 f;

浮动a=颜色。阿尔法(muser。bgcolor)/255 f;

mBgRender.setBgColor(r,g,b,a);

mbgrender。draw(use fbo);//画到fbo上需要反向的

ZegoExpressEngine.getEngine().sendCustomVideoCaptureTextureData(mbg render。getoutputtextureid()、宽度、高度、系统。当前时间毫秒());

}

@覆盖

public void onStartCapture() {

if (mUser==null)返回;

////收到回调后,开发者需要执行启动视频采集相关的业务逻辑,例如开启摄像头等

AvatarCaptureConfig=新的AvatarCaptureConfig(muser。宽度,缪瑟。身高);

////开始捕获纹理

mcharacterhelper。startcaptureavatar(config,this:onCaptureAvatar);

}

@覆盖

public void onStopCapture() {

mcharacterhelper。stopcaptureavatar();

停止表达式();

}

私有void initRes(应用程序){

//先把资源拷贝到南达科他州卡,注意:线上使用时,需要做一下判断,避免多次拷贝。资源也可以做成从网络下载。

如果(!FileUtils.checkFile(app,' AIModel.bundle '' assets '))

fileutils。copyassetsdir 2手机(app,' AIModel.bundle '' assets ');

如果(!FileUtils.checkFile(app,' base.bundle '' assets '))

文件实用程序。copyassetsdir 2手机(app,' base.bundle '' assets ');

如果(!FileUtils.checkFile(app,' human.bundle '' assets '))

fileutils。copyassetsdir 2手机(app,' human.bundle '' assets ');

如果(!FileUtils.checkFile(应用程序、\”包\”、\”资产\”))

fileutils。copyassetsdir 2手机(应用程序,包''资产');

}

@覆盖

公共void on error(ZegoAvatarErrorCode,字符串desc) {

Log.e(标签,' errorcode : ' code.getErrorCode()'desc:' desc ');

}

@覆盖

公共void onStateChange(ZegoAvatarServiceState状态){

if(state==ZegoAvatarServiceState .InitSucceed) {

Log.i('ZegoAvatar '' Init success ');

//要记得及时移除通知

zegoavatarservice。removeserviceobserver(this);

如果(听者!=null)侦听器。oninitsucced();

}

}

私有AvatarMngr(应用程序){

mApp=app

initRes(app);

}

公共静态AvatarMngr getInstance(应用程序app) {

if(null==最小实例){

同步(AvatarMngr.class) {

if(null==最小实例){

min instance=new AvatarMngr(app);

}

}

}

回报;

}

}

以上代码完成了整个虚拟形象的创建,关键代码全部展示。

免责声明:文章内容不代表本站立场,本站不对其内容的真实性、完整性、准确性给予任何担保、暗示和承诺,仅供读者参考,文章版权归原作者所有。如本文内容影响到您的合法权益(内容、图片等),请及时联系本站,我们会及时删除处理。

作者: 董方卓

为您推荐

上海战略研究院|上海加快布局元宇宙“新赛道”的对策建议

一、全球元宇宙“新赛道”布局的“六大趋势” (1)顶层设计:国家争相出台扶持政策,重点成立行业协会等。 2020年以来,...

苹果进军元宇宙可能会撼动蓬勃发展的市场 但这不会在一夜之间发生

Apple 近十年来首款突破性的硬件产品,预计将于6 月推出的混合现实耳机,应该会为疲软的Metaverse 市场振作起...

超宇宙在线展厅软件:足不出户享受3D数字世界

。 近日,重庆市文化和旅游发展委员会联合主办“文化赋能乡村振兴——两岸青年创意设计展”,展示两岸青年以设计创意助力乡村振...

科幻作家刘洋推出《井中之城》 思考《虚拟宇宙》中庄子梦蝶的哲学问题

封面新闻记者张杰 近日,电影《流浪地球2》的上映和电视剧《三体》的播出,再次将科幻话题推向新的热潮。 AI机器人参与原创...

美媒:超宇宙公司再次裁员1万人

据美联社报道,社交媒体脸书的母公司Metauniverse将再次裁员1万人,并且不会填补5000个空缺。这家先锋社交媒体...

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

返回顶部