UE4 VR官方教程学习总结-项目设置
https://docs.unrealengine.com/4.26/zh-CN/SharingAndReleasing/XRDevelopment/VR/DevelopVR/ContentSetup/
Epic Games
- 可缩放3D或2D(Scalable 3D or 2D)
- 已禁用光线追踪(Raytracing Disabled)
- 移动设备/平板电脑(Mobile / Tablet)
- 不带初学者内容包(No Starter Content)
- 进入 编辑->项目设置->描述,并启用 以VR启动。
- 在 编辑->项目设置->渲染->前向渲染器 中,启用前向着色。
- 在 编辑->项目设置->渲染->默认设置 中,将 抗锯齿方法 设置为多重采样抗锯齿。
- 在 编辑->项目设置->渲染(Rendering)->VR 中,启用实例化立体。 * 对于移动设备VR体验,在 **编辑->项目设置->渲染->VR** 中启用移动多视图。
[SystemSettings]vr.PixelDensity=1
r.SeparateTranslucency=0
r.HZBOcclusion=0
r.MotionBlurQuality=0
r.PostProcessAAQuality=3
r.BloomQuality=1
r.EyeAdaptationQuality=0
r.AmbientOcclusionLevels=0
r.SSR.Quality=1
r.DepthOfFieldQuality=0
r.SceneColorFormat=2
r.TranslucencyVolumeBlur=0
r.TranslucencyLightingVolumeDim=4
r.MaxAnisotropy=8
r.LensFlareQuality=0
r.SceneColorFringeQuality=0
r.FastBlurThreshold=0
r.SSR.MaxRoughness=0.1
r.rhicmdbypass=0
sg.EffectsQuality=2
sg.PostProcessQuality=0
![](/assets/blank.gif)
Why we chose forward rendering
- 由于是在移动端上,而延迟渲染使用GBuffer会大量占用内存,越高分辨率影响越大
- 前向渲染的渲染管线速度更快,更能满足vr场景的需求
- 前向渲染能够使用msaa
- 打开oculus VR插件 和 online subsystem oculus插件(无法开启就先关闭别的online插件)
- 打开前向渲染 forward Rendering(更快更高效)
- 关闭动态模糊(前向渲染不支持)
- 开启instance stereo(实例化立体),在VR场景中会有两个摄像头代表左眼和右眼,is可以让两个摄像头共享特定比例的渲染数据,缩短绘制线程花费的时间
- 开启MSAA,可以提供更好的平滑效果,注意会占用显存,可能会影响性能
- 支持平台选为安卓(看设备)
- 取消使用平滑帧率(fixed frame Rate),因为VR需要锁帧
- 关闭自定义时间步Custom TimeStep,因为VR项目并不需要
- 如果是在一体机设备上的项目,关闭mobile HDR,开启移动多视图mobile multi-view
- 在工程项目config中,DefaultEngine.ini加入如下代码【个人使用中未使用,说是不加appid构建会警告,如果是使用quest这种一体机设备,还要将这些代码配置到AndroidEngine.ini,在引擎文件夹Engine-config-android里】
[OnlineSubsystem]
DefaultPlatformService=Oculus[OnlineSubsystemOculus]
bEnabled=true
OculuesAppId=
RiftAppId=
- 使用oculus的节点verify Entitlement 节点来验证应用权限,确保已与设备链接,用户有权使用该app
https://learn.unrealengine.com/course/3746907/module/7254773?moduletoken=UHxxnDLPW8Rp-7N-q4JkIjEQEsOt3snTmaKK-rCvMCbzh~y0iaykoFQik9vT6VYT&LPId=117565
- 增加一个光源,都会对前向渲染产生很多开销,要控制光源数量和半径
- MSAA只能解决源锯齿的问题
- 纹理锯齿需要通过mipmap 和 高品质过滤解决【注意 虚幻里会禁用非二次幂纹理产生Mipmap,这不仅会导致锯齿,还是让渲染变慢】
- MSAA无法解决镜面微光锯齿,使用复合纹理技术解决
- 在移动端需要关闭这些功能,因为可能造成长时间的编译和性能损耗,前向渲染会受到更大的影响
![](/assets/blank.gif)
- 使用较少种类的材质并提高使用他们的频率
- 减少动态光源,使用时避免动态阴影,性能开销非常大
- 图像API(DX, opengl)也会影响耗能耗电
- 对于复杂场景Occlusion Culling可以进行很好的优化,但是对于VR应用建议关闭,作为替代,可以在场景中使用“预计算可视性体积pre computed visibility volume”,它会计算光照和可视性数据,会增加内存消耗和构建时间,体积越小剔除效率越高
- 在Engine/Source中搜索ShaderPipelineCache.cpp,改成如下
bool FShaderPipelineCache::Open(FString const& Name, EShaderPlatform Platform)
{......if(bReady){uint64 PreCompileMask = (uint64)CVarPSOFileCachePreCompileMask.GetValueOnAnyThread();//FPipelineFileCache::SetGameUsageMaskWithComparison(PreCompileMask, PreCompileMaskComparison);FShaderPipelineCache::SetGameUsageMaskWithComparison(PreCompileMask, PreCompileMaskComparison);......
}
- 打开引擎,在Project Settings -> Packaging 中勾选 【共享着色器代码】Share Material Shader Code 和【共享材质原生库】 Shared Material Native Libraries
- 打开window->developer Tools->Device Profiles->Android->Rendering 加入 r.ShaderPipelineCache.Enabled 值设为1
![](/assets/blank.gif)
- 关闭编辑器,打开引擎文件夹 Engine\Config\Android\AndroidEngine.ini 加入 如下内容后保存重启编辑器
[DevOptions.Shaders]
NeedsShaderStableKeys=true
- 淦,好复杂,后续需要再说,链接
https://learn.unrealengine.com/course/3746970/module/7254916?moduletoken=UHxxnDLPW8Rp-7N-q4JkIuBOIC4F1jL4A39qvRh5ceQMXjghbjAvwPyS~Pd5rj0G&LPId=117565
- 使用Opaque rendering,不透明效果渲染实心物体,这是性能最好的材质,进行深度计算避免被剔除像素的ps计算
- 使用Masked Rendering,遮罩渲染来生成完全透明和完全非透明的像素
- Translucent Rendering,透明渲染会造成很大的性能开销,不推荐使用
- Alpha Transparency, 更推荐实现透明效果的方法,additive使材质明亮,modulate使其暗淡
- Distance Field fonts 距离场字体有着更好的抗锯齿高分辨率表现去渲染3d文本(Text Render) SDF(Signed Distance Fonts)字体,被编码到纹理表中以使用GPU的纹理采样去使边缘更平滑
- 传统的3D渲染会假设图像平面是一个2D矩形平面,为了给VR头显创建图像,需要扭曲图像,Oculus Quest支持固定凹陷渲染(Fixed Foveated Rendering FFR),可以节省头显视野外的ps计算和内存。这个节点在蓝图中调用Set Fixed Oculus Level,设置FFR水平,Level越高性能越好,但会牺牲设备的分辨率【注,今天发现好像4.27.1已经有支持了】
- 开启移动多视图优化mobile Multi-View,可以很好的节约CPU开销,在典型的立体渲染中,必须按顺序渲染每个眼睛缓冲区,从而使开销加倍。启用“多视图”时,对象将渲染一次到左眼缓冲区,然后自动复制到右眼缓冲区,并对顶点位置和视图相关变量进行适当修改。(要先确保关闭mobile HDR)
- 避免渲染靠近摄像机或者与摄像机相交的大型贴图,在3d空间中很容易就看出是贴图是平面的
- 使用更小的粒子
- 尽量不要去主动移动摄像头或重新设置其FOV等参数
- 是画面和灯光更加暗淡
- 垂直上下移动的话使用电梯而不是台阶
- 移动的加速是瞬时的
- 不要使用景深和后处理
- 静止
- 轨道移动系统(如过山车)
- 目光传送
- 传送移动(抛物线)
![](/assets/blank.gif)
- 很难管理,并且会与3d内容进行混合(因为是立体融合)
- 很容易造成眼睛疲劳
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
传送前在传送点位置处进行一条垂直200的射线检测,优化传送点位置
![](/assets/blank.gif)
- 淡出到纯色
- 使用VR splash 画面
- 显示一个或多个立体图层
![](/assets/blank.gif)
- 头部相关的传输函数head related transfer function,简称HRTF,描述了人耳如何从空间中的一点听到声音
- 空间建模,包括遮挡和混响
- 在Plugins中开启 Oculus Audio插件
- 在Project Settings->Platforms 中选择项目对应的平台打开,如果是quest就是Android->Audio-> 将Spatialization Plugin 和 Reverb Plugin 设置为Oculus Audio,保存重启编辑器
- 将声音素材sound wave拖入场景,将这个sound wave Attach to 一个自己指定的actor上
- 在content目录中,选择需要的目录,右键Sound -> Sound Attenuation, 创建一个声音衰减资源,将这个衰减资源指定到上一步拖入场景中的那个sound wave -> Attenuation -> Attenuation Settings 中
![](/assets/blank.gif)
- 打开这个衰减资源,找到Spatialization Method 空间化方法,改为Binaural、找到Spatialization Plugin Settings 指定一个OculusAudioSourceSettings(没有就新建一个)
![](/assets/blank.gif)
- 打开这个OculusAudioSourceSettings,确保Early Reflections Enabled 和 Attenuation Enabled 是开启的
![](/assets/blank.gif)
- 为actor添加一个Oculus Audio Geometry组件
- 然后再添加一个OculusAudioMaterial声学材料组件,然后在Settings->Material Preset中设置材料类型如玻璃、瓷砖等
![](/assets/blank.gif)
- 针对不能有组件的Landshape和BSP对象,可以在任意或者空actor上附加OculusAudioGeometryLandscape组件或者OculusAudioGeometry BSP组件
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
// Fill out your copyright notice in the Description page of Project Settings.using UnrealBuildTool;public class Darts : ModuleRules
{public Darts(ReadOnlyTargetRules Target) : base(Target){PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;PublicDependencyModuleNames.AddRange(new string[] {"Core","CoreUObject","Engine","InputCore","LibOVRPlatform"});PrivateDependencyModuleNames.AddRange(new string[] {"OnlineSubsystem","OnlineSubsystemOculus",// "LibOVRAvatar", this is useless});// Uncomment if you are using Slate UI// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });// Uncomment if you are using online features// PrivateDependencyModuleNames.Add("OnlineSubsystem", "OnlineSubsystemOculus","LibOVRAvatar",);// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to trueif ((Target.Platform == UnrealTargetPlatform.Win32) || (Target.Platform == UnrealTargetPlatform.Win64)){//PrivateDependencyModuleNames.Add("LibOVRPlatform");PublicDelayLoadDLLs.Add("LibOVRPlatform64_1.dll");}}
}
OculusOSS.h
// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Online.h"
#include "OVR_Platform.h"
#include "OnlineSubsystemOculus.h"
#include "GameFramework/Actor.h"
#include "OculusOSS.generated.h"UCLASS()
class DARTS_API AOculusOSS : public AActor
{GENERATED_BODY()public:// Sets default values for this actor's propertiesAOculusOSS(const FObjectInitializer& ObjectInitializer);//AOculusOSS(); //previous constructorFOnlineSubsystemOculus* OSS;private:FString MyPlayerName;FString MyPlayerID;FOnSessionUserInviteAcceptedDelegate OnSessionUserInviteAcceptedDelegate;FOnJoinSessionCompleteDelegate OnJoinSessionCompleteDelegate;FOnCreateSessionCompleteDelegate OnCreateSessionCompleteDelegate;FOnStartSessionCompleteDelegate OnStartSessionCompleteDelegate;FOnDestroySessionCompleteDelegate OnDestroySessionCompleteDelegate;static FString AcceptedInviteFrom;static FOnlineSessionSearchResult AcceptedInvite;//matchmaking:TSharedPtr<FOnlineSessionSearch> SearchSettings;FOnMatchmakingCompleteDelegate OnMatchmakingCompleteDelegate;FOnCancelMatchmakingCompleteDelegate OnCancelMatchmakingCompleteDelegate;FDelegateHandle ovrMessage_Notification_ApplicationLifecycle_LaunchIntentChangedHandle;void OnApplicationLifecycle_LaunchIntentChanged(ovrMessageHandle Message, bool bIsError);ovrRichPresenceOptionsHandle ovr_RichPresenceOptions_Handle = NULL;protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;FDelegateHandle OnLoginCompleteDelegateHandle;FDelegateHandle OnJoinSessionCompleteDelegateHandle;FDelegateHandle OnDestroySessionCompleteDelegateHandle;FDelegateHandle OnCreateSessionCompleteDelegateHandle;FDelegateHandle OnStartSessionCompleteDelegateHandle;public:// Called every framevirtual void Tick(float DeltaTime) override;virtual void OnLoginComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& Error);void OnPrivilegeCheck(const FUniqueNetId& UserId, EUserPrivileges::Type Privilege, uint32 CheckResult);void OnSessionUserInviteAccepted(const bool bWasSuccessful, const int32 ControllerId, TSharedPtr<const FUniqueNetId> UserId, const FOnlineSessionSearchResult& InviteResult);void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type JoinResult);void OnCreateSessionComplete(FName SessionName, bool bWasSuccessful);void OnStartOnlineGameComplete(FName SessionName, bool bWasSuccessful);void OnDestroySessionComplete(FName SessionName, bool bWasSuccessful);UFUNCTION(BlueprintImplementableEvent, Category = Identity)void OnPlayerNameUpdate(const FString& PlayerName);UFUNCTION(BlueprintImplementableEvent, Category = Identity)void OnPlayerIDUpdate(const FString& PlayerID);UFUNCTION(BlueprintCallable, Category = OculusSession)void CreateSession();UFUNCTION(BlueprintImplementableEvent, Category = OculusSession)void OnCreateSessionCompleteBP(FName SessionName, bool bWasSuccessful);UFUNCTION(BlueprintImplementableEvent, Category = OculusSession)void OnJoinSessionCompleteBP(FName SessionName, bool bWasSuccessful);UFUNCTION(BlueprintImplementableEvent, Category = OculusSession)void OnLoginCompleteBP(bool bWasSuccessful);UFUNCTION(BlueprintCallable, Category = OculusSession)void LaunchUserInviteFlow();UFUNCTION(BlueprintCallable, Category = OculusSession)void LeavingMap();UFUNCTION(BlueprintCallable, Category = OculusSession)void PlayerLoggedOut(const FString& PlayerID);UFUNCTION(BlueprintCallable, Category = OculusSession)void TestSessionInvite();UFUNCTION(BlueprintCallable, Category = OculusSession)bool StartMatchmaking(const FString& PoolName);UFUNCTION(BlueprintCallable, Category = OculusSession)bool CancelMatchmaking(const FName SessionName);void OnMatchmakingComplete(FName SessionName, bool bWasSuccessful);UFUNCTION(BlueprintImplementableEvent, Category = OculusSession)void OnMatchmakingCompleteBP(FName SessionName, bool bWasSuccessful);//UFUNCTION(BlueprintImplementableEvent, Category = OculusSession)void OnCancelMatchmakingComplete(FName SessionName, bool bWasSuccessful);UFUNCTION(BlueprintImplementableEvent, Category = OculusSession)void OnCancelMatchmakingCompleteBP(FName SessionName, bool bWasSuccessful);UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = OculusSession)FString MatchmakingStatus;//Rich Presence:UFUNCTION(BlueprintCallable, Category = OculusRichPresence)void RichPresenceOptions_Destroy();UFUNCTION(BlueprintCallable, Category = OculusRichPresence)void SetRichPresenceAPIName(FString RichPresenceAPIName);UFUNCTION(BlueprintCallable, Category = OculusRichPresence)void SetRichPresenceArgsString(FString RichPresenceArgsKey, FString RichPresenceArgsValue);UFUNCTION(BlueprintCallable, Category = OculusRichPresence)void RichPresenceClearArgs();UFUNCTION(BlueprintCallable, Category = OculusRichPresence)void SetRichPresenceCurrentCapacity(uint8 CurrentCapacity);UFUNCTION(BlueprintCallable, Category = OculusRichPresence)void SetRichPresenceDeeplinkMessageOverride(FString DeeplinkMessageOverride);UFUNCTION(BlueprintCallable, Category = OculusRichPresence)void SetRichPresenceSetEndTime(FString EndTime);/*//$TODO need to create UENUM to map to this structure//UFUNCTION(BlueprintCallable, Category = OculusRichPresence)//bool SetRichPresenceExtraContext(FString ExtraContext);*/UFUNCTION(BlueprintCallable, Category = OculusRichPresence)void SetRichPresenceIsIdle(bool IsIdle);UFUNCTION(BlueprintCallable, Category = OculusRichPresence)void SetRichPresenceIsJoinable(bool IsJoinable);UFUNCTION(BlueprintCallable, Category = OculusRichPresence)void SetRichPresenceJoinableId(FString JoinableId);UFUNCTION(BlueprintCallable, Category = OculusRichPresence)void SetRichPresenceMaxCapacity(uint8 MaxCapacity);UFUNCTION(BlueprintCallable, Category = OculusRichPresence)void SetRichPresenceStartTime(FString StartTime);//Destinations:void OnReceivedDeepLinkMessage(ovrLaunchDetailsHandle LaunchDetails);UFUNCTION(BlueprintImplementableEvent, Category = OculusRichPresence)void OnReceivedDeepLinkMessageBP(const FString& DLMessage);
};
OculusOSS.cpp
// Fill out your copyright notice in the Description page of Project Settings.#include "OculusOSS.h"
#include "Darts.h"
#include "Runtime/Engine/Classes/GameFramework/GameModeBase.h"FString AOculusOSS::AcceptedInviteFrom;
FOnlineSessionSearchResult AOculusOSS::AcceptedInvite;// Sets default values
AOculusOSS::AOculusOSS(const FObjectInitializer& ObjectInitializer): Super(ObjectInitializer)
{UE_LOG(LogTemp, Verbose, TEXT("In the AOculusOSS constructor"));if (IsRunningCommandlet()){FModuleManager::Get().LoadModule(TEXT("OnlineSubsystem"));}OSS = static_cast<FOnlineSubsystemOculus*>(IOnlineSubsystem::Get());// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;AcceptedInviteFrom.Empty(); // empty signified no invites are outstandingOnCreateSessionCompleteDelegate = FOnCreateSessionCompleteDelegate::CreateUObject(this, &AOculusOSS::OnCreateSessionComplete);OnSessionUserInviteAcceptedDelegate = FOnSessionUserInviteAcceptedDelegate::CreateUObject(this, &AOculusOSS::OnSessionUserInviteAccepted);OnJoinSessionCompleteDelegate = FOnJoinSessionCompleteDelegate::CreateUObject(this, &AOculusOSS::OnJoinSessionComplete);OnStartSessionCompleteDelegate = FOnStartSessionCompleteDelegate::CreateUObject(this, &AOculusOSS::OnStartOnlineGameComplete);OnDestroySessionCompleteDelegate = FOnDestroySessionCompleteDelegate::CreateUObject(this, &AOculusOSS::OnDestroySessionComplete);ovrMessage_Notification_ApplicationLifecycle_LaunchIntentChangedHandle =OSS->GetNotifDelegate(ovrMessage_Notification_ApplicationLifecycle_LaunchIntentChanged).AddUObject(this, &AOculusOSS::OnApplicationLifecycle_LaunchIntentChanged);if (!ovr_RichPresenceOptions_Handle){ovr_RichPresenceOptions_Handle = ovr_RichPresenceOptions_Create();}
}// Called when the game starts or when spawned
void AOculusOSS::BeginPlay()
{Super::BeginPlay();if (Online::GetSessionInterface().IsValid()){Online::GetSessionInterface()->AddOnDestroySessionCompleteDelegate_Handle(OnDestroySessionCompleteDelegate);Online::GetSessionInterface()->AddOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegate);Online::GetSessionInterface()->AddOnSessionUserInviteAcceptedDelegate_Handle(OnSessionUserInviteAcceptedDelegate);}auto OculusIdentityInterface = Online::GetIdentityInterface();if (!OculusIdentityInterface.IsValid()){UE_LOG(LogTemp, Error, TEXT("No OculusIdentityInterface found!"));return;}OnLoginCompleteDelegateHandle = OculusIdentityInterface->AddOnLoginCompleteDelegate_Handle(0, FOnLoginCompleteDelegate::CreateUObject(this, &AOculusOSS::OnLoginComplete));if (OculusIdentityInterface->AutoLogin(0)){UE_LOG(LogTemp, Verbose, TEXT("Waiting for login response from oculus...."));}}// Called every frame
void AOculusOSS::Tick(float DeltaTime)
{Super::Tick(DeltaTime);}void AOculusOSS::OnApplicationLifecycle_LaunchIntentChanged(ovrMessageHandle Message, bool bIsError)
{UE_LOG(LogTemp, Verbose, TEXT("In OnApplicationLifecycle_LaunchIntentChanged"));if (bIsError){//errorUE_LOG(LogTemp, Verbose, TEXT("Got an error retrieving the launch intent"));return;}//do something//const char*FString launchMessage;const char* tesmps = ovr_Message_GetString(Message);//UE_LOG(LogTemp, Verbose, TEXT("LaunchMessage: %s"), launchMessage);if (launchMessage.Find(TEXT("LaunchType"), ESearchCase::IgnoreCase, ESearchDir::FromStart, 0)){UE_LOG(LogTemp, Verbose, TEXT("Found LaunchType "));}auto LaunchDetails = ovr_ApplicationLifecycle_GetLaunchDetails();auto LaunchType = ovr_LaunchDetails_GetLaunchType(LaunchDetails);switch (LaunchType){case ovrLaunchType_Unknown:UE_LOG(LogTemp, Verbose, TEXT("Found LaunchType Unknown "));break;case ovrLaunchType_Normal:UE_LOG(LogTemp, Verbose, TEXT("Found LaunchType Normal "));break;case ovrLaunchType_Invite:UE_LOG(LogTemp, Verbose, TEXT("Found LaunchType Invite "));break;case ovrLaunchType_Coordinated:UE_LOG(LogTemp, Verbose, TEXT("Found LaunchType Coordinated "));break;case ovrLaunchType_Deeplink:{UE_LOG(LogTemp, Verbose, TEXT("Found LaunchType Deep link "));OnReceivedDeepLinkMessage(LaunchDetails);break;}default:UE_LOG(LogTemp, Verbose, TEXT("Found LaunchType DEFAULT CASE "));break;}
}void AOculusOSS::OnReceivedDeepLinkMessage(ovrLaunchDetailsHandle LaunchDetails)
{UE_LOG(LogTemp, Verbose, TEXT("Made it to OnReceivedDeepLinkMessage"));//Parse the deep link message here and then call the BP eventauto DeepLinkMessage = ovr_LaunchDetails_GetDeeplinkMessage(LaunchDetails);FString DeepLinkMessageFS(DeepLinkMessage);UE_LOG(LogTemp, Verbose, TEXT("OnReceivedDeepLinkMessage: DeepLink Message: %s"), *DeepLinkMessageFS);//TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject());//TSharedRef< TJsonReader<> > JsonReader = TJsonReaderFactory<>::Create()//const TSharedRef< TJsonReader<> >& Reader = TJsonReaderFactory<>::Create(DeepLinkMessageFS);//if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())//{//UE_LOG(LogTemp, Verbose, TEXT("Parsed the JSON"));//}//else//UE_LOG(LogTemp, Verbose, TEXT("Failed to parse JSON. Error: '%s'"), *Reader->GetErrorMessage());int32 index = DeepLinkMessageFS.Find(TEXT("command"), ESearchCase::IgnoreCase, ESearchDir::FromStart);UE_LOG(LogTemp, Verbose, TEXT("Index: %i"), index);FString CommandString;if (index > -1){UE_LOG(LogTemp, Verbose, TEXT("Index: %i"), index);CommandString = DeepLinkMessageFS.RightChop(index + 8); //skip the 'command:' char and get the next wordUE_LOG(LogTemp, Verbose, TEXT("Command: %s"), *CommandString);UE_LOG(LogTemp, Verbose, TEXT("Calling OnReceivedDeepLinkMessageBP"));OnReceivedDeepLinkMessageBP(CommandString);}//auto DeepLinkAPIName = ovr_LaunchDetails_GetDestinationApiName(LaunchDetails); //this requires an updated header and .lib that isn't in 4.24 so commenting out for now.}void AOculusOSS::OnLoginComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& Error)
{auto OculusIdentityInterface = Online::GetIdentityInterface();OculusIdentityInterface->ClearOnLoginCompleteDelegate_Handle(0, OnLoginCompleteDelegateHandle);OculusIdentityInterface->GetUserPrivilege(UserId,EUserPrivileges::CanPlay,IOnlineIdentity::FOnGetUserPrivilegeCompleteDelegate::CreateUObject(this, &AOculusOSS::OnPrivilegeCheck));if (!bWasSuccessful){UE_LOG(LogTemp, Warning, TEXT("Unable to login with oculus! %s"), *Error);return;}UE_LOG(LogTemp, Verbose, TEXT("Logged in successfully to oculus!"));// Get the Oculus IDMyPlayerName = OculusIdentityInterface->GetPlayerNickname(UserId);MyPlayerID = UserId.ToString();UE_LOG(LogTemp, Verbose, TEXT("Welcome %s!"), *MyPlayerName);OnPlayerNameUpdate(MyPlayerName);OnPlayerIDUpdate(MyPlayerID);OnLoginCompleteBP(bWasSuccessful); //notify Blueprint of the call completion
}void AOculusOSS::OnPrivilegeCheck(const FUniqueNetId& UserId, EUserPrivileges::Type Privilege, uint32 CheckResult)
{if (CheckResult != (uint32)IOnlineIdentity::EPrivilegeResults::NoFailures){UE_LOG(LogTemp, Error, TEXT("Arrg, you failed the entitlement check!"));// Developers may want to just quit the game here.MyPlayerName = TEXT("FAILED ENTITLEMENT CHECK");OnPlayerNameUpdate(MyPlayerName);return;}UE_LOG(LogTemp, Verbose, TEXT("You passed the entitlement check!"));
}void AOculusOSS::OnSessionUserInviteAccepted(const bool bWasSuccessful, const int32 ControllerId, TSharedPtr<const FUniqueNetId> UserId, const FOnlineSessionSearchResult& InviteResult)
{UE_LOG(LogTemp, Verbose, TEXT("User has accepted an invitation with success = %d"), bWasSuccessful);if (!bWasSuccessful){UE_LOG(LogTemp, Error, TEXT("Did not successfully accept user invitation!"));return;}//Check if I am in a session already and destroy it if so.auto OculusSessionInterface = Online::GetSessionInterface();auto Session = OculusSessionInterface->GetNamedSession(TEXT("Game"));if (Session){AcceptedInvite = InviteResult;AcceptedInviteFrom = *UserId->ToString();UE_LOG(LogTemp, Verbose, TEXT("Destroying existing session before trying to join new one"));Online::GetSessionInterface()->DestroySession(TEXT("Game"), OnDestroySessionCompleteDelegate);return; //exit so OnDestroySessionComplete will handle the join session call in this case}UE_LOG(LogTemp, Verbose, TEXT("Not in an existing session. Trying to join session from invitation"));OculusSessionInterface->JoinSession(ControllerId, TEXT("Game"), InviteResult);
}//TODO: legacy
void AOculusOSS::TestSessionInvite()
{const bool bWasSuccessful = true;TSharedPtr<const FUniqueNetId> UserId = 0;const int32 ControllerId = 0;//const FOnlineSessionSearchResult & InviteResult;if (!bWasSuccessful){UE_LOG(LogTemp, Error, TEXT("Did not successfully invited user to the session!"));return;}UE_LOG(LogTemp, Verbose, TEXT("Accepted invite to session. Joining session...."));//Check if I am in a session already and destroy it if so.auto OculusSessionInterface = Online::GetSessionInterface();auto Session = OculusSessionInterface->GetNamedSession(TEXT("Game"));if (Session){OculusSessionInterface->DestroySession(TEXT("Game"), OnDestroySessionCompleteDelegate);}UE_LOG(LogTemp, Verbose, TEXT("Would call Join Session here"));
}void AOculusOSS::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type JoinResult)
{UE_LOG(LogTemp, Verbose, TEXT("In OnJoinSessionComplete"));auto OculusSessionInterface = Online::GetSessionInterface();auto Session = OculusSessionInterface->GetNamedSession(SessionName);FString TravelURL;APlayerController* PlayerController = NULL;//AGameModeBase *GameMode = NULL;UWorld* const TheWorld = GetWorld();if (!TheWorld){UE_LOG(LogTemp, Warning, TEXT("The World Does Not Exist."));return;}else{PlayerController = GetWorld()->GetFirstPlayerController();auto gamemode = (AGameModeBase*)GetWorld()->GetAuthGameMode();//gamemode->bUseSeamlessTravel = false;UE_LOG(LogTemp, Verbose, TEXT("Seamless Travel Set to : %s"), gamemode->bUseSeamlessTravel ? TEXT("True") : TEXT("False"));}if (Session){OculusSessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegateHandle);UE_LOG(LogTemp, Verbose, TEXT("Got back %s's session: %s"), *Session->OwningUserName, *SessionName.ToString());if (*Session->OwningUserId == *Online::GetIdentityInterface()->GetUniquePlayerId(0)) // I am the owner{UE_LOG(LogTemp, Verbose, TEXT("I am the session owner and will host"));//GetWorld()->ServerTravel(TEXT("/Game/Maps/Minimal_Default3?listen"));}else{UE_LOG(LogTemp, Verbose, TEXT("Not the session owner"));if (PlayerController && OculusSessionInterface->GetResolvedConnectString(SessionName, TravelURL)){TravelURL = TravelURL + TEXT("?multiplayer=true"); //so the level blueprint will see that it was loaded in MP mode.UE_LOG(LogTemp, Verbose, TEXT("Calling ClientTravel to: %s"), *TravelURL);// Finally call the ClienTravelPlayerController->ClientTravel(TravelURL, ETravelType::TRAVEL_Absolute);}}auto gamemode = (AGameModeBase*)GetWorld()->GetAuthGameMode();//gamemode->bUseSeamlessTravel = true; //after first travel, start using seamless travel.UE_LOG(LogTemp, Verbose, TEXT("Seamless Travel Set to : %s"), gamemode->bUseSeamlessTravel ? TEXT("True") : TEXT("False"));auto OculusVoiceInterface = Online::GetVoiceInterface();auto OculusIdentityInterface = Online::GetIdentityInterface();auto UserId = OculusIdentityInterface->GetUniquePlayerId(0);OculusSessionInterface->StartSession(SessionName);//auto RegisteredPlayers = Session->RegisteredPlayers; //get list of players in the sessionfor (auto RegisteredPlayer : RegisteredPlayers){//don't register the local player, only the remoteif (RegisteredPlayer.Get() != *UserId.Get()){OculusVoiceInterface->RegisterRemoteTalker(RegisteredPlayer.Get());OculusVoiceInterface->StartNetworkedVoice(0);UE_LOG(LogTemp, Verbose, TEXT("Registered a Talker: %s"), *RegisteredPlayer.Get().ToString());}}}//call the BP event:OnJoinSessionCompleteBP(SessionName, (JoinResult == EOnJoinSessionCompleteResult::Success));
}bool AOculusOSS::StartMatchmaking(const FString& PoolName)
{auto OculusSessionInterface = Online::GetSessionInterface();if (OculusSessionInterface->IsPlayerInSession(TEXT("Game"), *Online::GetIdentityInterface()->GetUniquePlayerId(0).Get())){//need to kill the existing sessionOculusSessionInterface->DestroySession(TEXT("Game"));}UE_LOG(LogTemp, Verbose, TEXT("Starting Matchmaking"));MatchmakingStatus = TEXT("Looking for a Match, X to cancel");TArray< TSharedRef<const FUniqueNetId> > LocalPlayers;// Create a matchmaking for two peopleauto SessionSettings = new FOnlineSessionSettings();SessionSettings->NumPublicConnections = 2;SearchSettings = MakeShareable(new FOnlineSessionSearch());// Add the delegateif (!OnMatchmakingCompleteDelegate.IsBound()){OnMatchmakingCompleteDelegate = FOnMatchmakingCompleteDelegate::CreateUObject(this, &AOculusOSS::OnMatchmakingComplete);OculusSessionInterface->AddOnMatchmakingCompleteDelegate_Handle(OnMatchmakingCompleteDelegate);}// Search with this poolnameSearchSettings->QuerySettings.Set(FName(TEXT("OCULUSPOOL")), PoolName, EOnlineComparisonOp::Equals);TSharedRef<FOnlineSessionSearch> SearchSettingsRef = SearchSettings.ToSharedRef();// Do the searchreturn OculusSessionInterface->StartMatchmaking(LocalPlayers, TEXT("Game"), *SessionSettings, SearchSettingsRef);
}bool AOculusOSS::CancelMatchmaking(FName SessionName)
{auto OculusSessionInterface = Online::GetSessionInterface();// Add the delegateif (!OnCancelMatchmakingCompleteDelegate.IsBound()){OnCancelMatchmakingCompleteDelegate = FOnCancelMatchmakingCompleteDelegate::CreateUObject(this, &AOculusOSS::OnCancelMatchmakingComplete);OculusSessionInterface->AddOnCancelMatchmakingCompleteDelegate_Handle(OnCancelMatchmakingCompleteDelegate);}UE_LOG(LogTemp, Verbose, TEXT("Cancelling Matchmaking"));MatchmakingStatus = TEXT("Cancelling Matchmaking");return OculusSessionInterface->CancelMatchmaking(0, TEXT("Game"));}void AOculusOSS::OnCancelMatchmakingComplete(FName SessionName, bool bWasSuccessful)
{UE_LOG(LogTemp, Verbose, TEXT("Matchmaking Cancel returned: %s"), bWasSuccessful ? TEXT("true") : TEXT("false"));MatchmakingStatus = TEXT("Matchmaking Cancelled");OnCancelMatchmakingCompleteBP(SessionName, bWasSuccessful);}void AOculusOSS::OnMatchmakingComplete(FName SessionName, bool bWasSuccessful)
{if (!(bWasSuccessful && SearchSettings->SearchResults.Num() > 0)){UE_LOG(LogTemp, Error, TEXT("Did not successfully find a matchmaking session!"));MatchmakingStatus = TEXT("Did not find a matchmaking session!");return;}UE_LOG(LogTemp, Verbose, TEXT("Found a matchmaking session. Joining session...."));MatchmakingStatus = TEXT("Round a matchmaking session!, Trying to join");auto OculusSessionInterface = Online::GetSessionInterface();OculusSessionInterface->JoinSession(0, SessionName, SearchSettings->SearchResults[0]);OnMatchmakingCompleteBP(SessionName, bWasSuccessful); //tell the blueprint Matchmaking completed
}void AOculusOSS::LaunchUserInviteFlow()
{UE_LOG(LogTemp, Verbose, TEXT("Calling LaunchUserInviteFlow"));//TODO: Confirm user is in a valid session/room before launching invite flow.auto OculusIdentityInterface = Online::GetIdentityInterface();auto UserId = OculusIdentityInterface->GetUniquePlayerId(0);//test ovrSystemVoipStatus v_status = ovr_Voip_GetSystemVoipStatus();switch (v_status){case ovrSystemVoipStatus::ovrSystemVoipStatus_Unknown:UE_LOG(LogTemp, Verbose, TEXT("ovrSystemVoipStatus_Unknown"));break;case ovrSystemVoipStatus::ovrSystemVoipStatus_Unavailable:UE_LOG(LogTemp, Verbose, TEXT(" ovrSystemVoipStatus_Unavailable"));break;case ovrSystemVoipStatus::ovrSystemVoipStatus_Suppressed:UE_LOG(LogTemp, Verbose, TEXT(" ovrSystemVoipStatus_Suppressed"));break;case ovrSystemVoipStatus::ovrSystemVoipStatus_Active:UE_LOG(LogTemp, Verbose, TEXT(" ovrSystemVoipStatus_Active"));break;};ovrRequest RequestId1 = ovr_Room_GetCurrent();OSS->AddRequestDelegate(RequestId1, FOculusMessageOnCompleteDelegate::CreateLambda([this](ovrMessageHandle Message, bool bIsError){// ovrMessageHandle can be handled just like in the native SDK here to get the roomif (!ovr_Message_IsError(Message)){ovrRoomHandle room = ovr_Message_GetRoom(Message);UE_LOG(LogTemp, Verbose, TEXT("Called ovr_Room_GetCurrent()"));ovrRequest RequestId = ovr_Room_LaunchInvitableUserFlow(ovr_Room_GetID(room));UE_LOG(LogTemp, Verbose, TEXT("RoomID: %llu"), ovr_Room_GetID(room));}else{const ovrErrorHandle error = ovr_Message_GetError(Message);UE_LOG(LogTemp, Verbose, TEXT("Received get room failure: %s"), ovr_Error_GetMessage(error));printf("Received get room failure: %s\n", ovr_Error_GetMessage(error));}}));
}void AOculusOSS::LeavingMap()
{auto OculusVoiceInterface = Online::GetVoiceInterface();auto OculusIdentityInterface = Online::GetIdentityInterface();auto UserId = OculusIdentityInterface->GetUniquePlayerId(0);if (OculusVoiceInterface.IsValid()){//OculusVoiceInterface->RegisterRemoteTalker(RegisteredPlayer.Get());OculusVoiceInterface->StopNetworkedVoice(0);OculusVoiceInterface->RemoveAllRemoteTalkers();//Online::GetVoiceInterface()->StartNetworkedVoice(0);UE_LOG(LogTemp, Verbose, TEXT("Ending VoIP"));}
}void AOculusOSS::PlayerLoggedOut(const FString& PlayerID)
{//UE_LOG(LogTemp, Verbose, TEXT("AOculusOSS::PlayerLoggedOut - %i"), UserID);auto OculusVoiceInterface = Online::GetVoiceInterface();auto OculusIdentityInterface = Online::GetIdentityInterface();auto UserId = OculusIdentityInterface->GetUniquePlayerId(0); //not right if called on server when a different player leaves?UE_LOG(LogTemp, Verbose, TEXT("AOculusOSS::PlayerLoggedOut - %s"), *PlayerID);}void AOculusOSS::CreateSession()
{auto OculusSessionInterface = Online::GetSessionInterface();if (!OculusSessionInterface.IsValid()){return;}UE_LOG(LogTemp, Verbose, TEXT("Trying to create a session"));OnCreateSessionCompleteDelegateHandle = OculusSessionInterface->AddOnCreateSessionCompleteDelegate_Handle(OnCreateSessionCompleteDelegate);TSharedPtr<class FOnlineSessionSettings> SessionSettings = MakeShareable(new FOnlineSessionSettings());SessionSettings->NumPublicConnections = 4;SessionSettings->bShouldAdvertise = true;OculusSessionInterface->CreateSession(/* Hosting Player Num*/ 0, TEXT("Game"), *SessionSettings);
}void AOculusOSS::OnCreateSessionComplete(FName SessionName, bool bWasSuccessful)
{auto OculusSessionInterface = Online::GetSessionInterface();if (!OculusSessionInterface.IsValid()){return;}OculusSessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(OnCreateSessionCompleteDelegateHandle);UE_LOG(LogTemp, Verbose, TEXT("CreateSession Call complete and was: %d"), bWasSuccessful);auto Session = OculusSessionInterface->GetNamedSession(SessionName);if (Session){UE_LOG(LogTemp, Verbose, TEXT("Session owned by %s"), *Session->OwningUserName);UE_LOG(LogTemp, Verbose, TEXT("Session state: %s"), EOnlineSessionState::ToString(Session->SessionState));}if (bWasSuccessful){// Set the StartSession delegate handleOnStartSessionCompleteDelegateHandle = OculusSessionInterface->AddOnStartSessionCompleteDelegate_Handle(OnStartSessionCompleteDelegate);// Our StartSessionComplete delegate should get called after thisOculusSessionInterface->StartSession(SessionName);}//Tell the BP we have tried to create a sessionOnCreateSessionCompleteBP(SessionName, bWasSuccessful);
}void AOculusOSS::OnStartOnlineGameComplete(FName SessionName, bool bWasSuccessful)
{GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::Printf(TEXT("OnStartSessionComplete %s, %d"), *SessionName.ToString(), bWasSuccessful));// Get the Online Subsystem so we can get the Session InterfaceIOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();if (OnlineSub){// Get the Session Interface to clear the DelegateIOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();if (Sessions.IsValid()){// Clear the delegate, since we are done with this callSessions->ClearOnStartSessionCompleteDelegate_Handle(OnStartSessionCompleteDelegateHandle);}}// If the start was successful, we can open a NewMap if we want. Make sure to use "listen" as a parameter!if (bWasSuccessful){UE_LOG(LogTemp, Verbose, TEXT("Started the Session"));}
}void AOculusOSS::OnDestroySessionComplete(FName SessionName, bool bWasSuccessful)
{GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::Printf(TEXT("OnDestroySessionComplete %s, %d"), *SessionName.ToString(), bWasSuccessful));UE_LOG(LogTemp, Verbose, TEXT("OnDestroySessionComplete()"));// Get the OnlineSubsystem we want to work withIOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();if (OnlineSub){// Get the SessionInterface from the OnlineSubsystemIOnlineSessionPtr OculusSessionInterface = OnlineSub->GetSessionInterface();if (OculusSessionInterface.IsValid()){// Clear the DelegateOculusSessionInterface->ClearOnDestroySessionCompleteDelegate_Handle(OnDestroySessionCompleteDelegateHandle);if (bWasSuccessful){//check if there is a pending invite and attempt to join the invited session if soif (!AcceptedInviteFrom.IsEmpty()){UE_LOG(LogTemp, Verbose, TEXT("Found a pending invite and trying to join %s's game"), *AcceptedInviteFrom);OnJoinSessionCompleteDelegateHandle = OculusSessionInterface->AddOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegate);OculusSessionInterface->JoinSession(0, FName(*AcceptedInviteFrom), AcceptedInvite);AcceptedInviteFrom.Empty();}UE_LOG(LogTemp, Verbose, TEXT("Destroy Session success"));//close the VoIP channel when leaving the session.Online::GetVoiceInterface()->StopNetworkedVoice(0);Online::GetVoiceInterface()->RemoveAllRemoteTalkers();}else{UE_LOG(LogTemp, Verbose, TEXT("Destroy Session fail"));}}}
}void AOculusOSS::RichPresenceOptions_Destroy()
{UE_LOG(LogTemp, Verbose, TEXT("RichPresenceOptions_Destroy"));ovr_RichPresenceOptions_Destroy(ovr_RichPresenceOptions_Handle);
}void AOculusOSS::SetRichPresenceAPIName(FString RichPresenceAPIName)
{UE_LOG(LogTemp, Verbose, TEXT("SetRichPresenceAPIName"));ovr_RichPresenceOptions_SetApiName(ovr_RichPresenceOptions_Handle, TCHAR_TO_ANSI(*RichPresenceAPIName));
}void AOculusOSS::SetRichPresenceArgsString(FString RichPresenceArgsKey, FString RichPresenceArgsValue)
{UE_LOG(LogTemp, Verbose, TEXT("SetRichPresenceArgsString"));ovr_RichPresenceOptions_SetArgsString(ovr_RichPresenceOptions_Handle, TCHAR_TO_ANSI(*RichPresenceArgsKey), TCHAR_TO_ANSI(*RichPresenceArgsValue));
}void AOculusOSS::RichPresenceClearArgs()
{UE_LOG(LogTemp, Verbose, TEXT("RichPresenceClearArgs"));ovr_RichPresenceOptions_ClearArgs(ovr_RichPresenceOptions_Handle);
}void AOculusOSS::SetRichPresenceCurrentCapacity(uint8 CurrentCapacity)
{UE_LOG(LogTemp, Verbose, TEXT("SetRichPresenceCurrentCapacity"));ovr_RichPresenceOptions_SetCurrentCapacity(ovr_RichPresenceOptions_Handle, CurrentCapacity);
}void AOculusOSS::SetRichPresenceDeeplinkMessageOverride(FString DeeplinkMessageOverride)
{UE_LOG(LogTemp, Verbose, TEXT("SetRichPresenceDeeplinkMessageOverride"));ovr_RichPresenceOptions_SetDeeplinkMessageOverride(ovr_RichPresenceOptions_Handle, TCHAR_TO_ANSI(*DeeplinkMessageOverride));
}void AOculusOSS::SetRichPresenceSetEndTime(FString EndTime)
{UE_LOG(LogTemp, Verbose, TEXT("SetRichPresenceSetEndTime"));ovr_RichPresenceOptions_SetEndTime(ovr_RichPresenceOptions_Handle, FCString::Atoi64(*EndTime));
}void AOculusOSS::SetRichPresenceIsIdle(bool IsIdle)
{UE_LOG(LogTemp, Verbose, TEXT("SetRichPresenceIsIdle"));ovr_RichPresenceOptions_SetIsIdle(ovr_RichPresenceOptions_Handle, IsIdle);
}void AOculusOSS::SetRichPresenceIsJoinable(bool IsJoinable)
{UE_LOG(LogTemp, Verbose, TEXT("SetRichPresenceIsJoinable"));ovr_RichPresenceOptions_SetIsJoinable(ovr_RichPresenceOptions_Handle, IsJoinable);
}void AOculusOSS::SetRichPresenceJoinableId(FString JoinableId)
{UE_LOG(LogTemp, Verbose, TEXT("SetRichPresenceJoinableId"));ovr_RichPresenceOptions_SetJoinableId(ovr_RichPresenceOptions_Handle, TCHAR_TO_ANSI(*JoinableId));
}void AOculusOSS::SetRichPresenceMaxCapacity(uint8 MaxCapacity)
{UE_LOG(LogTemp, Verbose, TEXT("SetRichPresenceMaxCapacity"));ovr_RichPresenceOptions_SetMaxCapacity(ovr_RichPresenceOptions_Handle, MaxCapacity);
}void AOculusOSS::SetRichPresenceStartTime(FString StartTime)
{UE_LOG(LogTemp, Verbose, TEXT("SetRichPresenceStartTime"));ovr_RichPresenceOptions_SetStartTime(ovr_RichPresenceOptions_Handle, FCString::Atoi64(*StartTime));
}
GameName.exe -mixedreality
![](/assets/blank.gif)
![](/assets/blank.gif)
adb shell "am broadcast -a android.intent.action.RUN -e cmd 'stat unit'"
- stat unit 以毫秒为单位提供帧数信息:Frame 总帧数时间 、Game 游戏线程帧数时间、Draw 渲染线程帧数时间、GPU 渲染一帧所需时间【如果GPU时间低于Game+Draw,而接近Frame,可能是GPU存在瓶颈】
- Stat Unitgraph 以毫秒为单位,显示unit前10s内的动态图表
- Stat FPS 只显示每秒帧数和一帧数时间
- Stat Game 显示与游戏线程相关的CPU时间
- Stat Engine 显示网格体三角形数量,帧数时间,帧同步时间【游戏线程等待渲染线程完成的时间】
- Stat Scenerendering 显示CPU渲染线程成本
- Stat GPU 显示GPU工作成本【Quest不支持,架构问题】
- ProfileGPU 捕获GPU渲染成本
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
- Sence组表示整个帧的绘制调用,这个组之外的调用可能与调试系统有关
- PrePass DDM_AllOpaque表示ue用于准备深度缓冲区的z预通道【是优化和延迟阴影的先决条件】
- ShadowDepths包含生成阴影贴图的所有绘制调用
- BasePass渲染不透明和遮盖的材质,向 GBuffer 输出材质属性。光照图贡献和天空光照也会在此计算并加入场景颜色。
- 如果材质使用光照着色器,那就不需要镜面高光,勾选完全粗糙 选项【而不是将材质粗糙度设为1】,慎用High Quality Reflections【会引入大量计算】
- 确保关闭移动HDR,启用单通道线性渲染Single-Pass linear rendering
- 先解决GPU再解决CPU问题
- 可以将多个网格体合并为单个
- 最大的两个开销:顶点处理和片元处理,可以选择降低数量和优化着色器
- 纹理会造成很大的开销,推荐在Quest上每个材质不要超过两种纹理
- 使用好法线贴图,对于平面材质不要使用法线贴图
- 减少动态光源数量
- 使用FFR降低周边视觉分辨率从而降低实际片元处理数量【pc还不支持】
- 确保开启移动多视图
- Rift可以使用虚幻的一些高级功能
- Rift和Quest如果要跨平台,需要注意使用的材质质量和光照质量
- Rift和Quest应该使用不同的材质以及不同的网格体
- 使用RenderDoc
- 合并 使用简单版本的顶点着色器去计算图元位置,并分配到RenderTarget上相应的部分
- 渲染 在这个过程中被分配到合并计算调用过程的每个顶点着色器都会重新执行,从而计算片元计算中所需的插值,然后执行片元着色器
- 因为顶点着色器会计算两次,所以要控制顶点的数量
- 兼容性(ue已实现)
- 性能(快速启动,视角正确,保持帧率Quest 72fpx)
- 安全性(权限验证【在sdk】)
- 功能性(正常游戏功能【无崩溃,能暂停等】,正常的硬件输入要求【右手手柄菜单键功能不应该被覆盖】,精确匹配手柄控制器)
UE4 VR官方教程学习总结-项目设置相关推荐
- PCL学习笔记(二):PCL官方教程学习
PCL学习笔记(二):PCL官方教程学习 PCD文件制作 Features 表面法线提取 Keypoints 提取NARF关键点 KdTree Range Image How to create a ...
- 大白小课程-跟着官方教程学习Scratch3.0-P04制作音乐
本文是视频教程:大白小课堂:跟着官方教程学习Scratch3.0系列的讲义版本,方便大家查阅 视频地址:https://www.bilibili.com/video/av54055338/ 我们今天一 ...
- 【从零开始的大数据学习】Flink官方教程学习笔记(一)
Flink官方教程学习笔记 学习资源 基础Scala语法 Scala数据结构专题 声明变量 代码块 函数(function) 方法(methods) Traits (接口) class(类) tupl ...
- GTSAM 官方教程学习
GTSAM官方教程学习 0. 前言 1. 因子图 1.1 序 1.2 因子图 1.2.1 贝叶斯网络 1.2.2 因子图 2. 运动建模 2.1 因子图建模 2.2 创建因子图 2.3 因子图与变量 ...
- TensorFlow2.0 Guide官方教程 学习笔记20 -‘Effective TensorFlow 2‘
本笔记参照TensorFlow Guide官方教程,主要是对'Effictive TensorFlow 2'教程内容翻译和内容结构编排,原文链接:Effictive TensorFlow 2 高效的T ...
- TensorFlow2.0 Guide官方教程 学习笔记17 -‘Using the SavedModel format‘
本笔记参照TensorFlow官方教程,主要是对'Save a model-Training checkpoints'教程内容翻译和内容结构编排,原文链接:Using the SavedModel f ...
- Dynamic Quantization PyTorch官方教程学习笔记
诸神缄默不语-个人CSDN博文目录 本文是PyTorch的教程Dynamic Quantization - PyTorch Tutorials 1.11.0+cu102 documentation的学 ...
- WGCNA:官方教程学习
官方教程地址:Tutorials for WGCNA R package 数据地址:FemaleLiver 表达数据格式: 芯片数据 options(stringsAsFactors = FALSE) ...
- TensorFlow2.0 Guide官方教程 学习笔记10- Eager execution
本笔记参照TensorFlow官方教程,主要是对'Eager execution'教程内容翻译和内容结构编排,原文链接:Eager execution 目录 一.创建环境和基本使用 二.动态控制流 三 ...
- uni-app官方教程学习手记
本人微信公众号:前端修炼之路,欢迎关注 背景介绍 大概在今年的十月份左右,我了解到Dcloud推出了uni-app.当时下载了一个Hbuilder X,下载了官方提供的hello示例教程.经过一番努力 ...
最新文章
- (IT/互联网行业)你给自己当前的职位拼几分?(评分标准,个人看法,勿喷~)...
- GitHub, Google Code, and other
- 华为说绝不造车,但轮值董事长却忙着看车展,未来华为会造车吗?
- 在收购 Sun 的六年后,Oracle 终于瞄准了 Java 的非付费用户
- java 文件上传 servlet_java文件上传-原始的Servlet方式
- oracle异机恢复 open resetlogs 报:ORA-00392
- C语言 strspn函数实现
- 免费在线PHP加密、解密、混淆源代码工具-toolfk.com
- 华为数据之道_DT|华为数据之道,怕你学不会,这次干脆出了本书
- 单片机音频谱曲软件_单片机音乐代码转换软件(Music Encode)
- 多文件云传输系统框架
- 有哪些芯片可以测量交流信号?可以测量哪些参数?
- ps编辑工具:渐隐/合并拷贝
- qq空间留言板删除 php,怎么批量删除QQ空间的说说
- rasp 系统_浅谈RASP技术攻防之基础篇
- 小众软件:画简洁风格的原型图
- 如何把原型保存为图片?
- Vue element 下拉框 可输入可选择(无bug)
- 框架 day74 涛涛商城项目整合ssm,分页插件pagehelper,商品列表查询
- 【Codecs系列】MPEG4解码器可以解码H.263的码流吗?
热门文章
- 解决Error: Call to undefined function eregi() 报错方法
- KiCad坐标文件(.pos)转表格(.xlsx)工具
- pod、pvc删不掉怎么办?
- 单片机课程设计题目及要求——电风扇模拟控制系统(仿真图加代码加原理图都有)
- 用 MeGUI 压制 BDrip
- 润乾报表导出pdf问题
- Linux查看目录busy,Linux中遇到device is busy的处理方法
- QQ群发精灵V3.2
- php union用法,php编程SQL语句union all的使用编程
- 钢笔工具(贝塞尔曲线)