You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
380 lines
15 KiB
C#
380 lines
15 KiB
C#
|
4 years ago
|
using UnityEngine;
|
||
|
|
using UnityEngine.UI;
|
||
|
|
|
||
|
|
namespace GPUInstancer
|
||
|
|
{
|
||
|
|
public class GPUInstancerHiZOcclusionGenerator : MonoBehaviour
|
||
|
|
{
|
||
|
|
public bool debuggerEnabled = false;
|
||
|
|
public bool debuggerGUIOnTop = false;
|
||
|
|
[Range(0f, 0.1f)]
|
||
|
|
public float debuggerOverlay = 0;
|
||
|
|
[Range(0, 16)]
|
||
|
|
public int debuggerMipLevel;
|
||
|
|
|
||
|
|
[Header("For info only, don't change:")]
|
||
|
|
//[HideInInspector]
|
||
|
|
public RenderTexture hiZDepthTexture;
|
||
|
|
//[HideInInspector]
|
||
|
|
public Texture unityDepthTexture;
|
||
|
|
//[HideInInspector]
|
||
|
|
public Vector2 hiZTextureSize;
|
||
|
|
|
||
|
|
private Camera _mainCamera;
|
||
|
|
private bool _isInvalid;
|
||
|
|
private int _hiZMipLevels = 0;
|
||
|
|
private RenderTexture[] _hiZMipLevelTextures = null;
|
||
|
|
|
||
|
|
private bool _isDepthTex2DArray;
|
||
|
|
|
||
|
|
// Debugger:
|
||
|
|
private RawImage _hiZDebugDepthTextureGUIImage;
|
||
|
|
private GameObject _hiZOcclusionDebuggerGUI;
|
||
|
|
#if UNITY_EDITOR
|
||
|
|
private bool _debuggerGUIOnTopCached;
|
||
|
|
private float _debuggerOverlayCached;
|
||
|
|
#endif
|
||
|
|
private bool _loggedURPWarning;
|
||
|
|
|
||
|
|
#region MonoBehaviour Methods
|
||
|
|
|
||
|
|
private void Awake()
|
||
|
|
{
|
||
|
|
hiZTextureSize = Vector2.zero;
|
||
|
|
GPUInstancerConstants.SetupComputeTextureUtils();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void OnEnable()
|
||
|
|
{
|
||
|
|
if (!GPUInstancerConstants.gpuiSettings.IsStandardRenderPipeline())
|
||
|
|
UnityEngine.Rendering.RenderPipelineManager.endCameraRendering += OnEndCameraRenderingSRP;
|
||
|
|
else
|
||
|
|
Camera.onPostRender += OnEndCameraRendering;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void OnDisable()
|
||
|
|
{
|
||
|
|
if (!GPUInstancerConstants.gpuiSettings.IsStandardRenderPipeline())
|
||
|
|
UnityEngine.Rendering.RenderPipelineManager.endCameraRendering -= OnEndCameraRenderingSRP;
|
||
|
|
else
|
||
|
|
Camera.onPostRender -= OnEndCameraRendering;
|
||
|
|
|
||
|
|
if (hiZDepthTexture != null)
|
||
|
|
{
|
||
|
|
hiZDepthTexture.Release();
|
||
|
|
hiZDepthTexture = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (_hiZMipLevelTextures != null)
|
||
|
|
{
|
||
|
|
for (int i = 0; i < _hiZMipLevelTextures.Length; i++)
|
||
|
|
{
|
||
|
|
if (_hiZMipLevelTextures[i] != null)
|
||
|
|
_hiZMipLevelTextures[i].Release();
|
||
|
|
}
|
||
|
|
_hiZMipLevelTextures = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region Public Methods
|
||
|
|
|
||
|
|
public void Initialize(Camera occlusionCamera = null)
|
||
|
|
{
|
||
|
|
_isInvalid = false;
|
||
|
|
|
||
|
|
_mainCamera = occlusionCamera != null ? occlusionCamera : gameObject.GetComponent<Camera>();
|
||
|
|
|
||
|
|
if (_mainCamera == null)
|
||
|
|
{
|
||
|
|
Debug.LogError("GPUI Hi-Z Occlusion Culling Generator failed to initialize: camera not found.");
|
||
|
|
_isInvalid = true;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (GPUInstancerConstants.gpuiSettings.isVREnabled)
|
||
|
|
{
|
||
|
|
if (_mainCamera.stereoTargetEye != StereoTargetEyeMask.Both)
|
||
|
|
{
|
||
|
|
Debug.LogError("GPUI Hi-Z Occlusion works only for cameras that render to Both eyes. Disabling Occlusion Culling.");
|
||
|
|
_isInvalid = true;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
_mainCamera.depthTextureMode |= DepthTextureMode.Depth;
|
||
|
|
|
||
|
|
CreateHiZDepthTexture();
|
||
|
|
}
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
|
||
|
|
#region Private Methods
|
||
|
|
|
||
|
|
// SRP callback signature is different in 2019.1, but we only need the camera.
|
||
|
|
// Using this method to direct the callback to the main method. Unity 2018 has the same signature with Camera.onPostRender, so this is not relevant.
|
||
|
|
private void OnEndCameraRenderingSRP(UnityEngine.Rendering.ScriptableRenderContext context, Camera camera)
|
||
|
|
{
|
||
|
|
OnEndCameraRendering(camera);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void OnEndCameraRendering(Camera camera)
|
||
|
|
{
|
||
|
|
if (_isInvalid || _mainCamera == null || camera != _mainCamera)
|
||
|
|
return;
|
||
|
|
|
||
|
|
if (hiZDepthTexture == null)
|
||
|
|
{
|
||
|
|
//Debug.LogWarning("GPUI HiZ Depth texture is null where it should not be. Recreating it.");
|
||
|
|
if (!CreateHiZDepthTexture())
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
HandleScreenSizeChange();
|
||
|
|
|
||
|
|
if (unityDepthTexture == null)
|
||
|
|
{
|
||
|
|
unityDepthTexture = Shader.GetGlobalTexture("_CameraDepthTexture");
|
||
|
|
|
||
|
|
#if UNITY_2018_3_OR_NEWER
|
||
|
|
if (unityDepthTexture != null && unityDepthTexture.dimension == UnityEngine.Rendering.TextureDimension.Tex2DArray)
|
||
|
|
_isDepthTex2DArray = true;
|
||
|
|
else
|
||
|
|
_isDepthTex2DArray = false;
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
if (unityDepthTexture != null && hiZDepthTexture != null) // will be null the first time this runs in Unity 2018 SRP since we have to use beginCameraRendering there.
|
||
|
|
{
|
||
|
|
#if GPUI_XR
|
||
|
|
if (GPUInstancerConstants.gpuiSettings.isVREnabled)
|
||
|
|
{
|
||
|
|
if (UnityEngine.XR.XRSettings.stereoRenderingMode == UnityEngine.XR.XRSettings.StereoRenderingMode.MultiPass)
|
||
|
|
{
|
||
|
|
if (_mainCamera.stereoActiveEye == Camera.MonoOrStereoscopicEye.Left || _mainCamera.stereoActiveEye == Camera.MonoOrStereoscopicEye.Mono)
|
||
|
|
UpdateTextureWithComputeShader(0);
|
||
|
|
else if (GPUInstancerConstants.gpuiSettings.IsUseBothEyesVRCulling())
|
||
|
|
UpdateTextureWithComputeShader((int)hiZTextureSize.x / 2);
|
||
|
|
if (_mainCamera.stereoActiveEye == Camera.MonoOrStereoscopicEye.Mono && GPUInstancerConstants.gpuiSettings.IsUseBothEyesVRCulling())
|
||
|
|
UpdateTextureWithComputeShader((int)hiZTextureSize.x / 2);
|
||
|
|
}
|
||
|
|
else if (_isDepthTex2DArray && UnityEngine.XR.XRSettings.stereoRenderingMode == UnityEngine.XR.XRSettings.StereoRenderingMode.SinglePassInstanced)
|
||
|
|
{
|
||
|
|
UpdateTextureWithComputeShader(0);
|
||
|
|
if (GPUInstancerConstants.gpuiSettings.IsUseBothEyesVRCulling())
|
||
|
|
UpdateTextureWithComputeShader((int)hiZTextureSize.x / 2, 1);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
UpdateTextureWithComputeShader(0);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
#endif
|
||
|
|
UpdateTextureWithComputeShader(0);
|
||
|
|
}
|
||
|
|
|
||
|
|
#if UNITY_EDITOR
|
||
|
|
HandleHiZDebugger();
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
private void UpdateTextureWithComputeShader(int offset, int textureArrayIndex = 0)
|
||
|
|
{
|
||
|
|
if (_isDepthTex2DArray)
|
||
|
|
GPUInstancerUtility.CopyTextureArrayWithComputeShader(unityDepthTexture, hiZDepthTexture, offset, textureArrayIndex);
|
||
|
|
else
|
||
|
|
GPUInstancerUtility.CopyTextureWithComputeShader(unityDepthTexture, hiZDepthTexture, offset);
|
||
|
|
|
||
|
|
for (int i = 0; i < _hiZMipLevels - 1; ++i)
|
||
|
|
{
|
||
|
|
RenderTexture tempRT = _hiZMipLevelTextures[i];
|
||
|
|
|
||
|
|
if (i == 0)
|
||
|
|
GPUInstancerUtility.ReduceTextureWithComputeShader(hiZDepthTexture, tempRT, offset);
|
||
|
|
else
|
||
|
|
GPUInstancerUtility.ReduceTextureWithComputeShader(_hiZMipLevelTextures[i - 1], tempRT, offset);
|
||
|
|
|
||
|
|
GPUInstancerUtility.CopyTextureWithComputeShader(tempRT, hiZDepthTexture, offset, 0, i + 1, false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private Vector2 GetScreenSize()
|
||
|
|
{
|
||
|
|
Vector2 screenSize = Vector2.zero;
|
||
|
|
#if GPUI_XR
|
||
|
|
if (GPUInstancerConstants.gpuiSettings.isVREnabled)
|
||
|
|
{
|
||
|
|
screenSize.x = UnityEngine.XR.XRSettings.eyeTextureWidth;
|
||
|
|
screenSize.y = UnityEngine.XR.XRSettings.eyeTextureHeight;
|
||
|
|
if (GPUInstancerConstants.gpuiSettings.IsUseBothEyesVRCulling())
|
||
|
|
screenSize.x *= 2;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
#endif
|
||
|
|
screenSize.x = _mainCamera.pixelWidth;
|
||
|
|
screenSize.y = _mainCamera.pixelHeight;
|
||
|
|
|
||
|
|
#if GPUI_XR
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
#if GPUI_URP
|
||
|
|
if (GPUInstancerConstants.gpuiSettings.isURP)
|
||
|
|
{
|
||
|
|
if (UnityEngine.Rendering.GraphicsSettings.renderPipelineAsset == null)
|
||
|
|
{
|
||
|
|
if (!_loggedURPWarning)
|
||
|
|
{
|
||
|
|
Debug.LogError("GPU Instancer is set up for URP, however it can not find the render pipeline asset." +
|
||
|
|
"\nIf you are using URP, please make sure that render pipeline assets are assigned under Edit->Project Settings->Graphics and Edit->Project Settings->Quality settings." +
|
||
|
|
"\nIf you are not using URP, please remove the URP package from the Package Manager and then remove and reinstall GPU Instancer, so it will be set up for built-in render pipeline when installed.");
|
||
|
|
_loggedURPWarning = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
screenSize *= (UnityEngine.Rendering.GraphicsSettings.renderPipelineAsset as UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset).renderScale;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
return screenSize;
|
||
|
|
}
|
||
|
|
|
||
|
|
private bool CreateHiZDepthTexture()
|
||
|
|
{
|
||
|
|
hiZTextureSize = GetScreenSize();
|
||
|
|
|
||
|
|
_hiZMipLevels = (int)Mathf.Floor(Mathf.Log(hiZTextureSize.x, 2f));
|
||
|
|
|
||
|
|
if (hiZTextureSize.x <= 0 || hiZTextureSize.y <= 0 || _hiZMipLevels == 0)
|
||
|
|
{
|
||
|
|
if (hiZDepthTexture != null)
|
||
|
|
{
|
||
|
|
hiZDepthTexture.Release();
|
||
|
|
hiZDepthTexture = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
//Debug.LogError("Cannot create GPUI HiZ Depth Texture for occlusion culling: Screen size is too small.");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (hiZDepthTexture != null)
|
||
|
|
hiZDepthTexture.Release();
|
||
|
|
|
||
|
|
int width = (int)hiZTextureSize.x;
|
||
|
|
int height = (int)hiZTextureSize.y;
|
||
|
|
|
||
|
|
hiZDepthTexture = new RenderTexture(width, height, 0, RenderTextureFormat.RFloat, RenderTextureReadWrite.Linear);
|
||
|
|
hiZDepthTexture.name = "GPUIHiZDepthTexture";
|
||
|
|
hiZDepthTexture.filterMode = FilterMode.Point;
|
||
|
|
hiZDepthTexture.useMipMap = true;
|
||
|
|
hiZDepthTexture.autoGenerateMips = false;
|
||
|
|
hiZDepthTexture.enableRandomWrite = true;
|
||
|
|
hiZDepthTexture.Create();
|
||
|
|
hiZDepthTexture.hideFlags = HideFlags.HideAndDontSave;
|
||
|
|
hiZDepthTexture.GenerateMips();
|
||
|
|
|
||
|
|
if (_hiZMipLevelTextures != null)
|
||
|
|
{
|
||
|
|
for (int i = 0; i < _hiZMipLevelTextures.Length; i++)
|
||
|
|
{
|
||
|
|
if (_hiZMipLevelTextures[i] != null)
|
||
|
|
_hiZMipLevelTextures[i].Release();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
_hiZMipLevelTextures = new RenderTexture[_hiZMipLevels];
|
||
|
|
|
||
|
|
for (int i = 0; i < _hiZMipLevels; ++i)
|
||
|
|
{
|
||
|
|
width = width >> 1;
|
||
|
|
|
||
|
|
height = height >> 1;
|
||
|
|
|
||
|
|
if (width == 0)
|
||
|
|
width = 1;
|
||
|
|
|
||
|
|
if (height == 0)
|
||
|
|
height = 1;
|
||
|
|
|
||
|
|
_hiZMipLevelTextures[i] = new RenderTexture(width, height, 0, RenderTextureFormat.RFloat, RenderTextureReadWrite.Linear);
|
||
|
|
_hiZMipLevelTextures[i].name = "GPUIHiZDepthTexture_Mip_" + i;
|
||
|
|
_hiZMipLevelTextures[i].filterMode = FilterMode.Point;
|
||
|
|
_hiZMipLevelTextures[i].useMipMap = false;
|
||
|
|
_hiZMipLevelTextures[i].autoGenerateMips = false;
|
||
|
|
_hiZMipLevelTextures[i].enableRandomWrite = true;
|
||
|
|
_hiZMipLevelTextures[i].Create();
|
||
|
|
_hiZMipLevelTextures[i].hideFlags = HideFlags.HideAndDontSave;
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void HandleScreenSizeChange()
|
||
|
|
{
|
||
|
|
Vector2 newScreenSize = GetScreenSize();
|
||
|
|
if (newScreenSize != hiZTextureSize)
|
||
|
|
{
|
||
|
|
CreateHiZDepthTexture();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#if UNITY_EDITOR
|
||
|
|
private void HandleHiZDebugger()
|
||
|
|
{
|
||
|
|
if (debuggerEnabled && _hiZOcclusionDebuggerGUI == null)
|
||
|
|
CreateHiZDebuggerCanvas();
|
||
|
|
|
||
|
|
if (!debuggerEnabled && _hiZOcclusionDebuggerGUI != null)
|
||
|
|
DestroyImmediate(_hiZOcclusionDebuggerGUI);
|
||
|
|
|
||
|
|
if (!debuggerEnabled)
|
||
|
|
return;
|
||
|
|
|
||
|
|
if (debuggerGUIOnTop != _debuggerGUIOnTopCached || debuggerOverlay != _debuggerOverlayCached)
|
||
|
|
{
|
||
|
|
_hiZOcclusionDebuggerGUI.GetComponent<Canvas>().sortingOrder = debuggerGUIOnTop ? 10000 : -10000;
|
||
|
|
_hiZDebugDepthTextureGUIImage.color = new Color(1, 1, 1, 1 - debuggerOverlay);
|
||
|
|
|
||
|
|
_debuggerOverlayCached = debuggerOverlay;
|
||
|
|
_debuggerGUIOnTopCached = debuggerGUIOnTop;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (_hiZOcclusionDebuggerGUI != null && hiZDepthTexture != null)
|
||
|
|
{
|
||
|
|
_hiZDebugDepthTextureGUIImage.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, GPUInstancerConstants.gpuiSettings.isVREnabled
|
||
|
|
&& GPUInstancerConstants.gpuiSettings.IsUseBothEyesVRCulling() ? hiZTextureSize.x * 0.5f : hiZTextureSize.x);
|
||
|
|
_hiZDebugDepthTextureGUIImage.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, hiZTextureSize.y);
|
||
|
|
_hiZDebugDepthTextureGUIImage.texture = debuggerMipLevel == 0 ? hiZDepthTexture : _hiZMipLevelTextures[debuggerMipLevel >= _hiZMipLevels ? _hiZMipLevels - 1 : debuggerMipLevel];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void CreateHiZDebuggerCanvas()
|
||
|
|
{
|
||
|
|
_hiZOcclusionDebuggerGUI = new GameObject("GPUI HiZ Occlusion Culling Debugger");
|
||
|
|
_debuggerGUIOnTopCached = debuggerGUIOnTop;
|
||
|
|
_debuggerOverlayCached = debuggerOverlay;
|
||
|
|
|
||
|
|
// Add and setup the canvas
|
||
|
|
Canvas debuggerCanvas = _hiZOcclusionDebuggerGUI.AddComponent<Canvas>();
|
||
|
|
debuggerCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||
|
|
debuggerCanvas.pixelPerfect = true; // no anti-aliasing
|
||
|
|
debuggerCanvas.sortingOrder = debuggerGUIOnTop ? 10000 : -10000;
|
||
|
|
debuggerCanvas.targetDisplay = 0;
|
||
|
|
|
||
|
|
// Add a raw image to display the HiZ Depth RenderTexture
|
||
|
|
GameObject hiZDepthTextureGUI = new GameObject("HiZ Depth Texture");
|
||
|
|
hiZDepthTextureGUI.transform.parent = _hiZOcclusionDebuggerGUI.transform;
|
||
|
|
_hiZDebugDepthTextureGUIImage = hiZDepthTextureGUI.AddComponent<RawImage>();
|
||
|
|
_hiZDebugDepthTextureGUIImage.color = new Color(1, 1, 1, 1 - debuggerOverlay);
|
||
|
|
|
||
|
|
// Setup the image's RectTransform for anchors, pivot and position
|
||
|
|
Vector2 bottomRight = new Vector2(0, 0);
|
||
|
|
_hiZDebugDepthTextureGUIImage.rectTransform.anchorMin = bottomRight;
|
||
|
|
_hiZDebugDepthTextureGUIImage.rectTransform.anchorMax = bottomRight;
|
||
|
|
_hiZDebugDepthTextureGUIImage.rectTransform.pivot = bottomRight;
|
||
|
|
_hiZDebugDepthTextureGUIImage.rectTransform.position = Vector2.zero;
|
||
|
|
}
|
||
|
|
#endif // UNITY_EDITOR
|
||
|
|
#endregion
|
||
|
|
}
|
||
|
|
}
|