Target Environment
- Unity Version: 2022.3.20f1
- Render Pipeline: Universal Render Pipeline (URP)
- Capturing the 3D environment and rendering it into a source Cubemap.
- Extracting the six individual faces from the Cubemap.
- Stitching these faces into a single equirectangular panorama.
Step 1: Cubemap Capture
The initial phase requires capturing the scene from a specific point of view into a standard 6-face Cubemap. The following script automates this process by creating a temporary camera object, positioning it, and rendering the scene into the target Cubemap asset.
using UnityEngine;
using UnityEditor;
public class SceneCaptureTool : ScriptableWizard
{
public Transform anchorPoint;
public Cubemap targetCubemap;
private void OnWizardUpdate()
{
helpString = "Assign a transform for the camera position and a target cubemap.";
isValid = (anchorPoint != null) && (targetCubemap != null);
}
private void OnWizardCreate()
{
// Instantiate a temporary camera
GameObject tempCamObj = new GameObject("TempCubemapCam");
Camera captureCam = tempCamObj.AddComponent<Camera>();
// Align with the target transform
tempCamObj.transform.position = anchorPoint.position;
tempCamObj.transform.rotation = Quaternion.identity;
// Perform the render
captureCam.RenderToCubemap(targetCubemap);
// Cleanup
DestroyImmediate(tempCamObj);
}
[MenuItem("Tools/Capture Scene to Cubemap")]
private static void InitWizard()
{
ScriptableWizard.DisplayWizard<SceneCaptureTool>("Capture Scene", "Render");
}
}
Step 2: Extracting Cubemap Faces
Once the Cubemap is generated, the individual faces must be extracted as standard image files. This script iterates through the six faces of the selected Cubemap, converts the pixel data into Texture2D objects, and saves them as JPG files in the same directory as the source asset.
using UnityEngine;
using UnityEditor;
using System.IO;
public class FaceExtractor : MonoBehaviour
{
[MenuItem("Assets/Extract Cubemap Faces")]
private static void ExtractFaces()
{
Cubemap source = Selection.activeObject as Cubemap;
if (source == null)
{
Debug.LogError("Selection is not a Cubemap.");
return;
}
string assetPath = AssetDatabase.GetAssetPath(source);
string folderPath = Path.GetDirectoryName(assetPath);
string baseName = Path.GetFileNameWithoutExtension(assetPath);
foreach (CubemapFace faceType in System.Enum.GetValues(typeof(CubemapFace)))
{
// Create a temporary texture for the face
Texture2D tempTex = new Texture2D(source.width, source.height, TextureFormat.RGB24, false);
Color[] facePixels = source.GetPixels(faceType);
tempTex.SetPixels(facePixels);
tempTex.Apply();
// Encode and save to disk
byte[] jpgData = tempTex.EncodeToJPG();
string outputPath = Path.Combine(folderPath, $"{baseName}_{faceType}.jpg");
File.WriteAllBytes(outputPath, jpgData);
Object.DestroyImmediate(tempTex);
}
Debug.Log("All faces extracted successfully to: " + folderPath);
AssetDatabase.Refresh();
}
}
Step 3: Combining Faces into a Panorama
The final step involves transforming the six orthogonal images into a single continuous equirectangular projection. While external tools like "Cubemap to Panorama" utilities can handle this, it is also possible to implement this entirely within Unity. Developers can write a custom Shader that performs the spherical projection logic. By applying this shader to a material and rendering it to a RenderTexture, the output can be saved as the final 360-degree panoramic image.