void ReinitializeBody()
{
Log("Settings changed, calling Reinitialize()");
initializeFailed = true;
if (!SystemInfo.supportsComputeShaders)
{
Debug.LogWarning("CapturePanorama requires compute shaders. Your system does not support them. " +
"On PC, compute shaders require DirectX 11, Windows Vista or later, and a GPU capable of Shader Model 5.0.");
return;
}
lastConfiguredCaptureStereoscopic = captureStereoscopic;
lastConfiguredPanoramaWidth = panoramaWidth;
lastConfiguredInterpupillaryDistance = interpupillaryDistance;
lastConfiguredNumCirclePoints = numCirclePoints;
lastConfiguredSsaaFactor = ssaaFactor;
lastConfiguredAntiAliasing = antiAliasing;
lastConfiguredSaveCubemap = saveCubemap;
lastConfiguredUseGpuTransform = useGpuTransform;
Cleanup();
faces = new CubemapFace[] {
CubemapFace.PositiveX, CubemapFace.NegativeX,
CubemapFace.PositiveY, CubemapFace.NegativeY,
CubemapFace.PositiveZ, CubemapFace.NegativeZ };
for (int i = 0; i < faces.Length; i++)
Debug.Assert((int)faces[i] == i); // Required for ConvertPanoramaShader
panoramaHeight = panoramaWidth / 2;
// We have a tower of 3 nested GameObjects. First gets the original camera position,
// second gets the eye rotation/position relative to it, and third holds the camera with default position/rotation.
camGos = new GameObject[3];
for (int i = 0; i < 3; i++)
{
camGos[i] = new GameObject("PanoramaCaptureCamera" + i);
camGos[i].hideFlags = HideFlags.HideAndDontSave;
if (i > 0) camGos[i].transform.parent = camGos[i - 1].transform;
}
camGos[2].AddComponent<Camera>();
cam = camGos[2].GetComponent<Camera>();
cam.enabled = false;
camGos[2].AddComponent<ImageEffectCopyCamera>();
copyCameraScript = camGos[2].GetComponent<ImageEffectCopyCamera>();
copyCameraScript.enabled = false;
numCameras = faces.Length;
hFov = vFov = 90.0f;
if (captureStereoscopic)
{
// For stereoscopic rendering, there are a set of points lying on a horizontal circle around the origin.
// Will have four cameras per circle point, one turned left 45 deg, one turned right 45 deg,
// one turned up 45 deg, one turned down 45 deg. Allows covering >= 180 deg horizontal and 180 deg vertical.
// To be able to resolve all rays, we need to choose a large enough horizontal FOV for each camera.
float maxAngleError = 360.0f / numCirclePoints;
// TODO: Use different hFov/vFov for top/bottom cameras and left/right cameras (vFov of top/bottom cameras especially is a lot less)
// Given our ipd adjustment scaling f(x), the IPD range of the top/bottom cameras will be up to f(0.5)
// of the original IPD. Hence the necessary hFov is given by 2*(pi/2 - acos(f(0.5))), usually in the 90-115 deg range.
float extraFovForRoundingErrors = 0.001f;
float hFovTopBottom = 2.0f * (Mathf.PI / 2.0f - Mathf.Acos(IpdScaleFunction(0.5f))) * 360.0f / (2.0f * Mathf.PI);
hFov = Mathf.Max(90f + maxAngleError, hFovTopBottom) + extraFovForRoundingErrors; // These will usually be similar so just use max for simplicity
vFov = 90.0f;
numCameras = 2 + numCirclePoints * CamerasPerCirclePoint; // 2 + for top/bottom
circleRadius = interpupillaryDistance / 2.0f;
hFovAdjustDegrees = hFov / 2.0f;
vFovAdjustDegrees = vFov / 2.0f;
}
double ppd90 = panoramaWidth * 90.0 / 360.0;
// Match PPD at 90 degrees - if it's larger, central 90 degree section should match PPD
cameraWidth = (int)Math.Ceiling(Math.Tan(hFov * (2.0f*Mathf.PI)/360.0f / 2.0f) * ppd90 * ssaaFactor);
cameraHeight = (int)Math.Ceiling(Math.Tan(vFov * (2.0f * Mathf.PI) / 360.0f / 2.0f) * ppd90 * ssaaFactor);
Log("Number of cameras: " + numCameras);
Log("Camera dimensions: " + cameraWidth + "x" + cameraHeight);
usingGpuTransform = useGpuTransform && convertPanoramaShader != null;
cubemapRenderTexture = new RenderTexture(cameraWidth, cameraHeight, /*depth*/24, RenderTextureFormat.ARGB32);
cubemapRenderTexture.antiAliasing = (int)antiAliasing;
cubemapRenderTexture.Create();
if (usingGpuTransform)
{
convertPanoramaKernelIdx = convertPanoramaShader.FindKernel("CubeMapToEquirectangular");
convertPanoramaYPositiveKernelIdx = convertPanoramaShader.FindKernel("CubeMapToEquirectangularPositiveY");
convertPanoramaYNegativeKernelIdx = convertPanoramaShader.FindKernel("CubeMapToEquirectangularNegativeY");
convertPanoramaKernelIdxs = new int[] { convertPanoramaKernelIdx, convertPanoramaYPositiveKernelIdx, convertPanoramaYNegativeKernelIdx };
convertPanoramaShader.SetInt("equirectangularWidth", panoramaWidth);
convertPanoramaShader.SetInt("equirectangularHeight", panoramaHeight);
convertPanoramaShader.SetInt("ssaaFactor", ssaaFactor);
convertPanoramaShader.SetInt("cameraWidth", cameraWidth);
convertPanoramaShader.SetInt("cameraHeight", cameraHeight);
int sliceHeight = (panoramaHeight + ResultBufferSlices - 1) / ResultBufferSlices;
int bitmapWidth = panoramaWidth;
int bitmapHeight = (captureStereoscopic ? 2 * panoramaHeight : sliceHeight);
resultPixels = new uint[bitmapWidth * bitmapHeight + 1]; // + 1 for sentinel
}
textureToBufferIdx = textureToBufferShader.FindKernel("TextureToBuffer");
textureToBufferShader.SetInt("width", cameraWidth);
textureToBufferShader.SetInt("height", cameraHeight);
textureToBufferShader.SetFloat("gamma", QualitySettings.activeColorSpace == ColorSpace.Linear ? 1.0f/2.2f : 1.0f);
renderStereoIdx = convertPanoramaStereoShader.FindKernel("RenderStereo");
if ((saveCubemap || !usingGpuTransform) &&
(cameraPixels == null || cameraPixels.Length != numCameras * cameraWidth * cameraHeight))
{
// Allocate once to avoid GC fragmentation
cameraPixels = new uint[numCameras * cameraWidth * cameraHeight + 1]; // + 1 for sentinel
}
tanHalfHFov = Mathf.Tan(hFov * (2 * Mathf.PI) / 360.0f / 2.0f);
tanHalfVFov = Mathf.Tan(vFov * (2 * Mathf.PI) / 360.0f / 2.0f);
hFovAdjust = hFovAdjustDegrees * (2 * Mathf.PI) / 360.0f;
vFovAdjust = vFovAdjustDegrees * (2 * Mathf.PI) / 360.0f;
if (captureStereoscopic && usingGpuTransform)
{
convertPanoramaStereoShader.SetFloat("tanHalfHFov", tanHalfHFov);
convertPanoramaStereoShader.SetFloat("tanHalfVFov", tanHalfVFov);
convertPanoramaStereoShader.SetFloat("hFovAdjust", hFovAdjust);
convertPanoramaStereoShader.SetFloat("vFovAdjust", vFovAdjust);
convertPanoramaStereoShader.SetFloat("interpupillaryDistance", interpupillaryDistance);
convertPanoramaStereoShader.SetFloat("circleRadius", circleRadius);
convertPanoramaStereoShader.SetInt("numCirclePoints", numCirclePoints);
convertPanoramaStereoShader.SetInt("equirectangularWidth", panoramaWidth);
convertPanoramaStereoShader.SetInt("equirectangularHeight", panoramaHeight);
convertPanoramaStereoShader.SetInt("cameraWidth", cameraWidth);
convertPanoramaStereoShader.SetInt("cameraHeight", cameraHeight);
convertPanoramaStereoShader.SetInt("ssaaFactor", ssaaFactor);
}
initializeFailed = false;
}