如何使用虛幻4來制作一個VR視頻播放器呢,簡單來講,使用虛幻4的視頻采集插件來獲取圖像,并對圖像進行處理,就能獲取我們需要的圖像了,下一步,你可以把圖像投影到一個360度球體上,就能形成球形的播放器了。
1.插件測試—采集單幀雙眼圖像
打開Epic Games Launcher,啟動引擎(我使用的版本為4.14.0)。在彈出的對話框中點擊New Project標(biāo)簽欄,再選擇C++標(biāo)簽頁,選擇Vehicle Advanced模板,并將項目命名為STEREOSCOPIC。最后點擊CreateProject。
在打開的編輯器菜單欄依次點擊Edit→Plugins,然后選擇左側(cè)的Movie Capture,在右側(cè)Stereo Panoramic Movie Capture條目中勾選Enabled。然后重啟編輯器。
當(dāng)編輯器重啟后,再次點擊Edit→Plugins→Movie Capture,再次檢查Stereo Panoramic Movie Capture是否已啟用。
在工具欄,依次點擊Blueprints→OpenLevelBlueprint。在Event BeginPlay事件后,新建兩個(具體依據(jù)需求而定)Execute Console Command節(jié)點保存我們需要執(zhí)行的命令。
這里先進行采集測試,將下面這兩條命令分別放入Execute Console Command節(jié)點中:
[代碼]:
1 |
<font size= "3" >SP.OutputDir D:/StereoCaptureFrames |
3 |
SP.PanoramicScreenshot</font> |
如下圖所示:
然后就可以點擊工具欄的Play按鈕了。此時系統(tǒng)可能會長時間沒有響應(yīng)(一分鐘左右),然后將會有兩幀圖像存儲到先前用SP.OutputDir指定的目錄中(實際是在改目錄中的一個日期與時間目錄下,點擊一次Play生成一個),一個是左眼圖像,一個是右眼圖像。
2.在項目中集成插件
首先將引擎中的全景采集插件(Stereo Panomic Movie Capture)備份,再將整個插件目錄剪切(注意是剪切,而不是復(fù)制)出來,一是供我們修改,二是防止和我們自己編譯有沖突。Unreal引擎中的插件在路徑\Epic Games\4.14\Engine\Plugins下,在這里我們需要將其中的StereoPanorama(\Plugins\Experimental\StereoPanorama)剪切出來。
然后打開Stereo項目文件夾,在文件夾根目錄下新建一個Plugins文件夾,將上一步剪切的StereoPanorama文件夾粘貼到這里。目錄結(jié)構(gòu)示例如下(限于篇幅這里只列出了必要的文件):
打開項目的場景編輯器,依次點擊Editor→Plugins→Project→MovieCaputure,啟用Stereo Panoramic Movie Caputure,然后重啟項目。再次檢查Stereo Panoramic Movie Capture是否被啟用。
項目編輯器中,依次點擊File→OpenVisualStudio,在VS工程中,依次點擊Solution→Games→STEREOSCOPIC→Config,打開DefaultEngine.ini文件,在該文件末尾添加如下文字。
[代碼]:
1 |
<font size= "3" >[Plugins] |
2 |
+EnabledPlugins=StereoPanorama</font> |
如下所示(截圖是添加插件后的工程,默認(rèn)沒有Plugins):
為了強制打包項目的時候插件能夠和項目相連,在VS工程中,依次點擊Solution→Games→STEREOSCOPIC→Source→STEREOSCOPIC→STEREOSCOPIC.Build.cs文件中添加模塊依賴項。
[代碼]:
1 |
<font size= "3" > PrivateDependencyModuleNames.AddRange( new string [] { "StereoPanorama" });</font> |
如下所示:
關(guān)閉Visual Studio和UE4編輯器,并重啟。重啟之后可以發(fā)現(xiàn)VS工程中已經(jīng)添加了Plugins文件夾和StereoPanorama插件。
3將左右眼圖像自動組合成單一圖像
在VS工程中,依次打開Solution→Games→STEREOSCOPIC→Plugins→StereoPahorama→Source→StereoPahorama\Private,打開文件SceneCapture.cpp文件。全部修改工作均在次完成。
為了使我們能夠方便地控制合成的開關(guān),我們需要定義一個bool常量在文件的頭部,這樣,在我們不需要開啟合并的時候修改該常量的值即可,不必再修改其余的代碼。
[代碼]:
2 |
const bool CombineAtlasesOnOutput = true ;</font> |
現(xiàn)在我們需要在代碼中有條件地禁用每只眼睛的輸出(通過上面定義的CombineAtlasesOnOutput來控制)。然后找到USceneCapturer::SaveAtlas()的底部,找到這樣一段代碼:
[代碼]:
1 |
<font size= "3" >IImageWrapperPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper( EImageFormat::PNG ); |
2 |
ImageWrapper->SetRaw(SphericalAtlas.GetData(), SphericalAtlas.GetAllocatedSize(), SphericalAtlasWidth, SphericalAtlasHeight, ERGBFormat::BGRA, 8); |
3 |
const TArray<uint8>& PNGData = ImageWrapper->GetCompressed(100); |
4 |
FFileHelper::SaveArrayToFile( PNGData, *AtlasName );</uint8></font> |
這幾行代碼就是控制左右眼輸出的,如果我們定義的CombineAtlasesOnOutput為true,就意味這我們需要合并兩張眼睛的圖像,那么我們就需要禁掉它(左右單獨輸出),如果為false則我們需要輸出左右眼的單獨序列幀,所以就需要執(zhí)行它。
綜上,可以寫一個if語句來判斷CombineAtlasesOnOutput的值:
[代碼]:
1 |
<font size= "3" >IImageWrapperPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper( EImageFormat::PNG ); |
2 |
if (!CombineAtlasesOnOutput) |
4 |
ImageWrapper->SetRaw(SphericalAtlas.GetData(), SphericalAtlas.GetAllocatedSize(), SphericalAtlasWidth, SphericalAtlasHeight, ERGBFormat::BGRA, 8); |
5 |
const TArray<uint8>& PNGData = ImageWrapper->GetCompressed(100); |
6 |
FFileHelper::SaveArrayToFile(PNGData, *AtlasName); |
這樣會導(dǎo)致一個錯誤,因為PNGData是在if的作用域內(nèi)定義的,如果執(zhí)行到了if后(被釋放掉)或者根本沒有執(zhí)行到(if判斷為false(!true))就會導(dǎo)致后面對PNGData的使用造成錯誤。
在上面if語句之后的代碼塊中對PNGData的使用處為:
[代碼]:
01 |
<font size= "3" > if (FStereoPanoramaManager::GenerateDebugImages->GetInt() != 0) |
03 |
FString FrameStringUnprojected = FString::Printf(TEXT( "%s_%05d_Unprojected.png" ), *Folder, CurrentFrameCount); |
04 |
FString AtlasNameUnprojected = OutputDir / Timestamp / FrameStringUnprojected; |
06 |
ImageWrapper->SetRaw(SurfaceData.GetData(), SurfaceData.GetAllocatedSize(), UnprojectedAtlasWidth, UnprojectedAtlasHeight, ERGBFormat::BGRA, 8); |
07 |
const TArray<uint8>& PNGDataUnprojected = ImageWrapper->GetCompressed(100); |
09 |
FFileHelper::SaveArrayToFile(PNGDataUnprojected, *AtlasNameUnprojected); |
對禁用左右眼單幀輸出部分,如果只寫這部分代碼,現(xiàn)在再執(zhí)行采集是不會有任何有意義圖像輸出的(因為現(xiàn)在已經(jīng)把左右眼輸出禁用了)。下面繼續(xù)搞將兩張合并到一塊的方法。
查找代碼:
[代碼]:
1 |
<font size= "3" >TArray<fcolor> SphericalLeftEyeAtlas = SaveAtlas( TEXT( "Left" ), UnprojectedLeftEyeAtlas ); |
2 |
TArray<fcolor> SphericalRightEyeAtlas = SaveAtlas(TEXT( "Right" ), UnprojectedRightEyeAtlas);</fcolor></fcolor></font> |
在其后添加:
[代碼]:
02 |
if (CombineAtlasesOnOutput) |
04 |
TArray<fcolor> CombinedAtlas; |
05 |
CombinedAtlas.Append(SphericalLeftEyeAtlas); |
06 |
CombinedAtlas.Append(SphericalRightEyeAtlas); |
07 |
IImageWrapperPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::JPEG); |
08 |
ImageWrapper->SetRaw(CombinedAtlas.GetData(), CombinedAtlas.GetAllocatedSize(), SphericalAtlasWidth, SphericalAtlasHeight * 2, ERGBFormat::BGRA, 8); |
09 |
const TArray<uint8>& PNGData = ImageWrapper->GetCompressed(100); |
11 |
FString FrameString = FString::Printf(TEXT( "Frame_%05d.jpg" ), CurrentFrameCount); |
12 |
FString AtlasName = OutputDir / Timestamp / FrameString; |
13 |
FFileHelper::SaveArrayToFile(PNGData, *AtlasName); |
此時在VS工程中編譯項工程STEREOSCOPIC,并重啟UE4編輯器和VS,就會采集并將左右眼合并成一張圖片了。
參考連接:https://imzlp.me/2016/09/05/capturing-stereoscopic-360-screenshots-videos-movies-unreal-engine-4/
翻譯整理:zhuxiaoyang2000