7 进阶功能¶
7.1 配置EyeBuffer大小¶
SDK支持响应Unreal的“vr.pixeldensity”命令,开发者可以通过该命令来配置EyeBuffer(RenderTexture)的大小。
使用方法:
方式一:打开文件 Project/Config/DefaultEngine.ini,然后在“[/Script/Engine.RendererSettings]”标签下添加字符串“vr.pixeldensity=1”,如果该命令等于一或者没有该命令,则EyeBuffer大小为Pico的默认大小,如果该值不等于一,则会在Pico默认的EyeBuffer大小的基础上进行缩放。 如下图所示:
图7.1 设置EyeBuffer大小
方式二:可以通过蓝图调用控制台命令实现该效果。使用节点“Execute Console Command”节点,调用“vr.pixeldensity 1”命令(命令中字符串和数字之间有一个空格)。同样数字部分为EyeBuffer的倍数。但是要保证该命令的执行必须足够早,建议放在关切蓝图的EventBeginPlay后。
图7.2 设置EyeBuffer大小
原通过项目设置 > 插件 > PicoMobile中的“Multiples of RT size”参数配置EyeBuffer大小的功能已废弃。
Tips: 建议开发者不做修改,仅在特殊需求场合使用自定义RenderTexture大小。对于该选项开发者必须了解以下两点:
- RT设置过小,会带来性能的提升,减少延迟,但同时也导致了分辨率降低;
- RT设置过大,会带来性能的降低,增加延迟,因此不建议RT设置超过硬件建议纹理尺寸;
- 目前RT可设置参数大小范围为0.5 - 2
7.2 眼球追踪¶
Pico Neo2 Eye设备支持眼球追踪, 眼球追踪可以追踪眼球注视位置,配合注视点渲染可以优化渲染性能。眼球追踪功能的开关在 项目设置 > 插件 > PicoMobile,勾选“EnableEyeTracking”即可打开眼球追踪功能。如图:
图7.3 眼球追踪
可以通过以下阶段获取眼球的位置与方向。
功能 | 获取眼球的位置和方向(仅Neo2 Eye支持, 需在项目设置中勾选”Enable Eye Tracking”) | |
输入 | 无 | |
输出 | 眼球的位置和方向 | |
返回值 | True:成功,False:失败。 |
7.3 注视点渲染¶
注视点渲染(Foveation Rendering)可以优化VR场景的渲染,该技术通过为视野中心提供全分辨率(无损),降低周边视野(人眼焦点区域之外)分辨率的方式来达到优化渲染的目的。 这里提供了两种使用注视点渲染的方式:
方法一:在打包之前,可以在“ 项目设置 > 插件 > PicoMobile”中勾选“Enable FoveationRendering”开启注视点渲染功能,并在“Foveation Level”下拉框指定Level即可(SDK默认提供了三个Level:Low/Med/High)。
图7.4 FoveationRendering设置
方法二:打包之后,可以通过蓝图节点动态的修改Foveation Rendering的level(注意:打包前,必须勾选“Enable FoveationRendering”开启注视点渲染功能)。
功能 | 获取Foveation Rendering等级 | |
输入 | 无 | |
输出 | Foveation Rendering等级 | |
返回值 | True:成功,False:失败。 |
功能 | 设置Foveation Rendering等级 | |
输入 | Foveation Rendering等级 | |
输出 | 无 | |
返回值 | True:成功,False:失败。 |
功能 | 设置Foveation Rendering参数 | |
输入 | Foveation Rendering控制参数 | |
输出 | 无 | |
返回值 | True:成功,False:失败。 |
- Foveation Gain:Vector2类型,X/Y轴方向外围像素的缩减率,值越大缩减的越多。
- Foveation Area:float类型,以注视点为中心,以Foveation Area值为半径的范围内保持全分辨率。
- Foveation Minimum:float类型,最小像素密度限制。
- Foveation Level:提供了Low,Med,High,Top High四种预定义选择。每种级别对应了不同的Foveation参数。
注:除非开发者对“Foveation Rendering”技术有深入的了解,可以使用自定义参数来调整FFR优化细节,否则建议开发者使用官方预定义Level。SDK提供四种种Level:
Low [3.0f, 3.0f, 1.0f, 0.125f]
Med [4.0f, 4.0f, 1.0f, 0.125f]
High [6.0f, 6.0f, 1.0f, 0.0625f]
Top High [7.0f, 7.0f, 0.0f, 0.0625f]
7.4 用户权限验证¶
为了保护开发者内容的版权,SDK提供了用户权限验证功能,分别在内容研发调试阶段和正式发布到Pico应用商店提供用户权限验证。
功能入口:Project Setting -> Plugins -> PicoMobile -> Platform,勾选 User Entitlement Check选项。
用户权限验证不要求用户联网,SDK提供验证结果的返回码,但不会做额外处理,开发者需要在程序中自行处理验证失败的情况。 比如,如果收到“未成功”的结果,向用户展示失败原因并退出应用,或者进入Demo模式,提示用户去Pico应用商店购买完整版内容。
7.4.1 开发调试阶段模拟权限验证¶
在研发阶段,开发者通过在配置界面填入开发机SN号模拟用户权限验证过程,并增加验证失败后的处理逻辑,以测试效果。
确保User Entitlement Check选项勾选的前提下,勾选 Entitlement Check Simulation选项,开启开发调试阶模拟权限验证。 Device SN通过Project Setting->Plugins->PicoMobile->Platform中的Device SN Code List传入。
图7.5 用户权限验证模拟
7.4.2 正式上线后的权限保护¶
通过AppID获取用户是否具有应用要求的权限。APPID是Pico开发者平台分配给应用的唯一识别ID,可通过 https://developer.pico-interactive.com/developer/overview 申请和查看。
在最新的SDK中,通过Project Setting->Plugins->PicoMobile->Platform下的App ID传入App ID,SDK在应用启动时执行权限验证。 开发者可以通过“Pico Entitlement Verify Set Callback Delegates”接口绑定代理来获取校验结果。
权限验证回调结果蓝图:
功能 | 返回验证结果code | |
输入 | 无 | |
输出 | 无 | |
返回值 | 无 |
权限验证结果如下表所示:
Code | 含义 |
0 | 成功 |
-2 | 服务不存在 |
-3 | 服务绑定失败 |
-4 | 捕获异常代码 |
-5 | 超时未收到服务返回值 |
10 | 单击Home键 |
11 | APPID缺失 |
13 | 包名和APPID不匹配 |
20 | 用户未登录 |
21 | 用户未购买 |
31 | 未查询到此应用 |
32 | 购买SN号与本机SN号不匹配 |
注意: 模拟验证中填入SN号的开发机,在内容上线到Pico应用商店后,默认通过用户权限验证验证,不会再进行正式的权限验证。 如果需要开发机在应用上线后也进行正式的用户权限验证,请在编译正式版本时,关闭“Entitlement Check Simulation”。
7.5 MultiView¶
如果想要开启MultiView,需要进入Unreal Editor > Settings > Project Settings > Engine > Rendering > VR,然后选择Mobile Multi-View和Mobile Multi-View Direct两个。必须同时勾选以上两项才能起效。注意 : MultiView功能开启需要在OpenGL ES3.1条件下,OpenGL ES2 不支持MultiView。
图7.6 开启MultiView
7.6 Pico VR Compositor Layers(StereoLayer)¶
StereoLayer是UE4引擎提供的Layer组件,在VR中,可将单独的纹理发送至VR头戴,并将其重新投射到和不同于项目其他内容的单独渲染通道中。
图7.7 场景中的overlay
图7.8 Stereo Layer相关属性
目前仅支持该组件的以下属性:Texture、Stereo Layer Type、Stereo Layer Shape、Priority、Left Texture、Quad Size。其他属性会在后续版本中逐渐实现。
如何创建一个StereoLayer:
1、 创建一个Pawn,可以拖入到level中。
2、 选择Pawn或者打开Pawn蓝图,添加Component中的Stereo Layer。
3、 Stereo Layer的属性中可以选择Stereo Layer Type有FaceLocked、WorldLocked、TrackerLocked,暂时FaceLocked与TrackerLocked效果相同。
4、 设置Stereo Layer Shape为Quad Layer。
5、 设置Property来确定多个layer的层级关系。
注:
建议不要把Stereo Layer添加到相机组件下。
建议使用正确的坐标位置关系放置模型和叠加层,否则会存在景深冲突,引起视觉不适。
7.7 Pico VR Splash Screen¶
SDK提供了一个在切换场景时显示一张贴图的方式,成为SplashScreen。开启SplashScreen共有两种方式,分别是通过项目设置和使用蓝图。
方式一:使用项目设置来开启SplashScreen:
打开项目设置,依次进入:Plugins->PicoMobile->SplashScreen(配置界面如下):
图7.9 SplashScreen相关属性
属性名 | 属性作用 |
Enable Auto Show | 是否自动显示(只有勾选了切场景是才会显示) |
Texture Path | 贴图 |
Transform in Maters | 贴图显示的Transform属性,单位为米 |
Quad Size in Maters | 显示贴图四边形的大小 |
表7.1Pico VRSplashScreen属性解读
方式二:使用蓝图来开启SplashScreen:
功能 | 清除SplashScreen当前的属性配置(会设置为不自动显示) | |
输入 | 无 | |
输出 | 无 | |
返回值 | 无 |
功能 | 设置是否自动显示SplashScreen | |
输入 | 是否自动显示 | |
输出 | 无 | |
返回值 | 无 |
功能 | 设置SplashScreen属性 | |
输入 | Texture、Translation in Meters、Rotation、Size in Maters、 Auto Show(属性含义见表7.1) | |
输出 | 无 | |
返回值 | 无 |
7.8 Platform系统¶
SDK继承了Unreal的OnlineSubsystem模块,实现了其中的部分功能,并开放给开发者。该模块的功能,暂时只支持成就系统。
需要使用Pico提供的OnlineSubSystem模块,需要进行以下步骤
- Step1:启用Online SubSystem Pico 插件
图7.10 开启Online Subsystem Pico 插件
- Step2:关闭Plugin->Online Platform 下除Online Subsystem、Online Subsystem Utils插件外的其他插件
- Step3:在[ProjectDirectory]/Config/Android/ 路径下新建AndroidEngine.ini文件,并添加以下内容:
[OnlineSubsystem]
DefaultPlatformService=Pico
- Step4:在[ProjectDirectory]/Source/[ProjectName]/ProjectName.Build.cs文件中添加以下内容来加载必要引擎模块
PublicDependencyModuleNames.AddRange(new string[]{"OnlineSubsystem", "OnlineSubsystemPico" });
- 7.8.1 成就系统
开发者需要登录开发者平台https://developer.pico-interactive.com/,在管理中心中进行成就的定义,具体步骤如下:
- 点击“查看”进入应用详情页。如果您还没有任何应用,请先“创建应用”。
图7.11 开发者平台
- 在详情页面底部找到并点击“平台服务配置 – 成就”。
图7.12 开发者平台
- 在成就页面可以看到您已创建的所有成就。如果您还没有任何成就,请点击“创建”。
图7.13 开发者平台
- 按要求填写成就的信息。填入的API 名称必须代码中的保持一致。
图7.14 开发者平台
以下为成就部分的接口说明:
1、 初始化接口:
FOnlineSubsystemPico::InitWithAndroidPlatform(FPicoInitOnCompleteDelegate&& Delegate)
该接口为成就系统的初始化接口,调用该接口,可对成就系统进行初始化,接口传入一个Delegate,返回初始化是否成功。调用该接口需要用户在PUI中登录自己的账号,并需要开发者在Project -> PicoMobile -> Platform中填入其中的对应参数(详见8.11章节)。为防止应用在后台是用户切换账户,该接口要求开发者在每次Resume都需要调用一次。 调用示例如下:
void UAchievementWidgetClass::InitPicoOnlineSubSystem()
{
OnlineSubsystemPico*PicoSubsystem=static_cast<FOnlineSubsystemPico*>(IOnlineSubsystem::Get());
if (PicoSubsystem)
{
PicoSubsystem->InitWithAndroidPlatform(FPicoInitOnCompleteDelegate::CreateUObject(this, &UAchievementWidgetClass::OnInitPicoSubSystemComplete));
}
}
void UAchievementWidgetClass::OnInitPicoSubSystemComplete(bool Result)
{
}
注意:以下接口,部分接口不要求先调用初始化接口,部分接口要求初始化,对于需要初始化的接口,仅需整体初始化一次。
2、 向服务器请求所有成就定义并缓存到本地
IOnlineAchievements::QueryAchievementDescriptions(const FUniqueNetId& PlayerId, const FOnQueryAchievementsCompleteDelegate& Delegate)将定义的所有成就的信息缓存到本地,Delegate返回请求是否成功。调用该接口,不需要事先调用初始化接口。调用实例如下:
void UAchievementWidgetClass:: QueryAchievement()
{
auto AchievementInterface = Online::GetAchievementsInterface();
auto UserID = new FUniqueNetIdPico(0);
if (AchievementInterface)
{
AchievementInterface->QueryAchievements(*UserID, FOnQueryAchievementsCompleteDelegate::CreateUObject(this, &UAchievementWidgetClass::OnQueryAchievementComplete));
}
}
void UAchievementWidgetClass:: OnQueryAchievementComplete (const FUniqueNetId& PlayerId, const bool bWasSuccessful)
{
}
3、 向服务器请求所有成就进度并缓存到本地
IOnlineAchievements::QueryAchievements( const FUniqueNetId& PlayerId, const FOnQueryAchievementsCompleteDelegate& Delegate)接口可以将所有成就的进度信息缓存到本地,Delegate返回请求是否成功。调用该接口,需要事先调用初始化接口和QueryAchievementDescriptions接口。调用示例如下:
void UAchievementWidgetClass:: QueryAchievement()
{
auto AchievementInterface = Online::GetAchievementsInterface();
auto UserID = new FUniqueNetIdPico(0);
if (AchievementInterface)
{
AchievementInterface->QueryAchievements(*UserID, FOnQueryAchievementsCompleteDelegate::CreateUObject(this, &UAchievementWidgetClass::OnQueryAchievementComplete));
}
}
void UAchievementWidgetClass:: OnQueryAchievementComplete (const FUniqueNetId& PlayerId, const bool bWasSuccessful)
{
}
4、 修改成就进度
IOnlineAchievements::WriteAchievements(const FUniqueNetId& PlayerId, FOnlineAchievementsWriteRef& WriteObject, const FOnAchievementsWrittenDelegate& Delegate) 接口可以修改成就的完成进度。通过WriteObject传入需要修改的成就的名字与要修改的进度,Delegate返回请求是否成功。调用该接口,需要事先调用初始化接口和QueryAchievementDescriptions接口。调用示例如下:
void UAchievementWidgetClass::WriteAchievement(FString AchievementName, int32 Value)
{
auto AchievementInterface = Online::GetAchievementsInterface();
auto UserID = new FUniqueNetIdPico(0);
if (AchievementInterface)
{
FOnlineAchievementsWritePtr WriteObject = MakeShareable(new FOnlineAchievementsWrite());
WriteObject->SetIntStat(*AchievementName, Value);
FOnlineAchievementsWriteRef WriteObjectRef = WriteObject.ToSharedRef();
AchievementInterface->WriteAchievements(*UserID, WriteObjectRef,
FOnAchievementsWrittenDelegate::CreateUObject(
this, &UAchievementWidgetClass::OnAchievementsWritten));
}
}
void UAchievementWidgetClass:: OnAchievementsWritten(const FUniqueNetId& PlayerId, const bool bWasSuccessful)
{
}
5、 获取缓存到本地的所有成就的进度
IOnlineAchievements::GetCachedAchievements(const FUniqueNetId& PlayerId, TArray<FOnlineAchievement>& OutAchievements) 接口可以获取缓存到本地的所有成就的进度。调用该接口,需要事先调用初始化接口与QueryAchievements接口。调用示例如下:
auto AchievementInterface = Online::GetAchievementsInterface();
auto UserID = new FUniqueNetIdPico(0);
TArray<FOnlineAchievement>Achievements;
if (AchievementInterface)
{
auto State = AchievementInterface->GetCachedAchievements(*UserID, Achievements);
}
6、 通过成就名称获取缓存到本地的成就进度
IOnlineAchievements:: GetCachedAchievement(const FUniqueNetId& PlayerId, const FString& AchievementId, FOnlineAchievement& OutAchievement)接口可以通过名字获取缓存到本地的成就的进度。调用该接口,需要事先调用初始化接口与QueryAchievements接口。调用示例如下:
auto AchievementInterface = Online::GetAchievementsInterface();
auto UserID = new FUniqueNetIdPico(0);
FOnlineAchievement Achievement;
if (AchievementInterface)
{
auto State = AchievementInterface->GetCachedAchievement(*UserID, AchievementName, Achievement);
}
7、 通过成就名称获取缓存到本地的成就定义
IOnlineAchievements::GetCachedAchievement( const FUniqueNetId& PlayerId, const FString& AchievementId, FOnlineAchievement& OutAchievement)接口可以通过名字获取缓存到本地的成就的进度。调用该接口,需要事先调用初始化接口与QueryAchievements接口。调用示例如下:
auto AchievementInterface = Online::GetAchievementsInterface();
auto UserID = new FUniqueNetIdPico(0);
FOnlineAchievement Achievement;
if (AchievementInterface)
{
auto State = AchievementInterface->GetCachedAchievement(*UserID, AchievementName, Achievement);
}