2025-06-08 00:39:11 +09:00

249 lines
9.2 KiB
C#

//------------------------------------------------------------------------------------------------------------------
// Volumetric Fog & Mist 2
// Created by Kronnect
//------------------------------------------------------------------------------------------------------------------
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace VolumetricFogAndMist2 {
public partial class VolumetricFog : MonoBehaviour {
const string SURFACE_CAM_NAME = "SurfaceCam";
public enum HeightmapCaptureResolution {
_64 = 64,
_128 = 128,
_256 = 256,
_512 = 512,
_1024 = 1024
}
RenderTexture rt;
Camera surfaceCam;
Matrix4x4 camMatrix;
Vector3 lastCamPos;
LayerMask lastTerrainLayerMask;
void DisposeSurfaceCapture() {
if (rt != null) {
rt.Release();
DestroyImmediate(rt);
}
}
void CheckSurfaceCapture() {
if (surfaceCam == null) {
Transform childCam = transform.Find(SURFACE_CAM_NAME);
if (childCam != null) {
surfaceCam = childCam.GetComponent<Camera>();
if (surfaceCam == null) {
DestroyImmediate(childCam.gameObject);
}
}
}
}
void SurfaceCaptureSupportCheck() {
if (!activeProfile.terrainFit) {
DisposeSurfaceCapture();
return;
}
Transform childCam = null;
if (surfaceCam == null) {
childCam = transform.Find(SURFACE_CAM_NAME);
if (childCam != null) {
surfaceCam = childCam.GetComponent<Camera>();
}
}
bool needsSurfaceCapture = forceTerrainCaptureUpdate;
if (surfaceCam == null) {
if (childCam != null) {
DestroyImmediate(childCam.gameObject);
}
if (surfaceCam == null) {
GameObject camObj = new GameObject(SURFACE_CAM_NAME, typeof(Camera));
camObj.transform.SetParent(transform, false);
surfaceCam = camObj.GetComponent<Camera>();
surfaceCam.depthTextureMode = DepthTextureMode.None;
surfaceCam.clearFlags = CameraClearFlags.Depth;
surfaceCam.allowHDR = false;
surfaceCam.allowMSAA = false;
}
needsSurfaceCapture = true;
if (GraphicsSettings.currentRenderPipeline == null) {
surfaceCam.stereoTargetEye = StereoTargetEyeMask.None;
}
surfaceCam.orthographic = true;
surfaceCam.nearClipPlane = 1f;
}
surfaceCam.enabled = false;
if (rt != null && rt.width != (int)activeProfile.terrainFitResolution) {
if (surfaceCam.targetTexture == rt) {
surfaceCam.targetTexture = null;
}
rt.Release();
DestroyImmediate(rt);
}
if (rt == null) {
rt = new RenderTexture((int)activeProfile.terrainFitResolution, (int)activeProfile.terrainFitResolution, 24, RenderTextureFormat.Depth);
rt.antiAliasing = 1;
needsSurfaceCapture = true;
}
int thisLayer = 1 << gameObject.layer;
if ((activeProfile.terrainLayerMask & thisLayer) != 0) {
activeProfile.terrainLayerMask &= ~thisLayer; // exclude fog layer
}
surfaceCam.cullingMask = activeProfile.terrainLayerMask;
surfaceCam.targetTexture = rt;
if (activeProfile.terrainLayerMask != lastTerrainLayerMask) {
lastTerrainLayerMask = activeProfile.terrainLayerMask;
needsSurfaceCapture = true;
}
if (activeProfile.terrainFit && needsSurfaceCapture) {
// Ensure depth cam only renders depth and is correctly assigned to custom renderer
UniversalAdditionalCameraData camData = surfaceCam.GetComponent<UniversalAdditionalCameraData>();
if (camData == null) {
camData = surfaceCam.gameObject.AddComponent<UniversalAdditionalCameraData>();
}
if (camData != null) {
camData.dithering = false;
camData.renderPostProcessing = false;
camData.renderShadows = false;
camData.requiresColorTexture = false;
camData.requiresDepthTexture = false;
camData.stopNaN = false;
#if UNITY_2021_3_OR_NEWER
CheckAndAssignDepthRenderer(camData);
#endif
}
PerformHeightmapCapture();
}
}
#if UNITY_2021_3_OR_NEWER
UniversalRendererData depthRendererData;
void CheckAndAssignDepthRenderer(UniversalAdditionalCameraData camData) {
UniversalRenderPipelineAsset pipe = (UniversalRenderPipelineAsset)GraphicsSettings.currentRenderPipeline;
if (pipe == null) return;
if (depthRendererData == null) {
depthRendererData = Resources.Load<UniversalRendererData>("Shaders/VolumetricFogDepthRenderer");
if (depthRendererData == null) {
Debug.LogError("Volumetric Fog Depth Renderer asset not found.");
return;
}
depthRendererData.postProcessData = null;
}
int depthRendererIndex = -1;
for (int k = 0; k < pipe.m_RendererDataList.Length; k++) {
if (pipe.m_RendererDataList[k] == depthRendererData) {
depthRendererIndex = k;
break;
}
}
if (depthRendererIndex < 0) {
depthRendererIndex = pipe.m_RendererDataList.Length;
System.Array.Resize<ScriptableRendererData>(ref pipe.m_RendererDataList, depthRendererIndex + 1);
pipe.m_RendererDataList[depthRendererIndex] = depthRendererData;
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(pipe);
#endif
}
camData.SetRenderer(depthRendererIndex);
}
#endif
/// <summary>
/// Updates terrain heightmap on this volumetric fog
/// </summary>
public void PerformHeightmapCapture() {
if (surfaceCam != null) {
surfaceCam.Render();
surfaceCam.enabled = false;
if (!fogMat.IsKeywordEnabled(ShaderParams.SKW_SURFACE)) {
fogMat.EnableKeyword(ShaderParams.SKW_SURFACE);
}
forceTerrainCaptureUpdate = false;
}
}
void SetupCameraCaptureMatrix() {
Vector3 camPos = transform.position + new Vector3(0, transform.lossyScale.y * 0.51f, 0);
surfaceCam.farClipPlane = 10000;
surfaceCam.transform.position = camPos;
surfaceCam.transform.eulerAngles = new Vector3(90, 0, 0);
Vector3 size = transform.lossyScale;
surfaceCam.orthographicSize = Mathf.Max(size.x * 0.5f, size.z * 0.5f);
ComputeSufaceTransform(surfaceCam.projectionMatrix, surfaceCam.worldToCameraMatrix);
fogMat.SetMatrix(ShaderParams.SurfaceCaptureMatrix, camMatrix);
fogMat.SetTexture(ShaderParams.SurfaceDepthTexture, surfaceCam.targetTexture);
fogMat.SetVector(ShaderParams.SurfaceData, new Vector4(camPos.y, activeProfile.terrainFogHeight, activeProfile.terrainFogMinAltitude, activeProfile.terrainFogMaxAltitude));
}
void SurfaceCaptureUpdate() {
if (surfaceCam == null) return;
SetupCameraCaptureMatrix();
if (!surfaceCam.enabled && lastCamPos != surfaceCam.transform.position) {
lastCamPos = surfaceCam.transform.position;
PerformHeightmapCapture();
requireUpdateMaterial = true;
}
}
static Matrix4x4 identityMatrix = Matrix4x4.identity;
void ComputeSufaceTransform(Matrix4x4 proj, Matrix4x4 view) {
// Currently CullResults ComputeDirectionalShadowMatricesAndCullingPrimitives doesn't
// apply z reversal to projection matrix. We need to do it manually here.
if (SystemInfo.usesReversedZBuffer) {
proj.m20 = -proj.m20;
proj.m21 = -proj.m21;
proj.m22 = -proj.m22;
proj.m23 = -proj.m23;
}
Matrix4x4 worldToShadow = proj * view;
var textureScaleAndBias = identityMatrix;
textureScaleAndBias.m00 = 0.5f;
textureScaleAndBias.m11 = 0.5f;
textureScaleAndBias.m22 = 0.5f;
textureScaleAndBias.m03 = 0.5f;
textureScaleAndBias.m23 = 0.5f;
textureScaleAndBias.m13 = 0.5f;
// Apply texture scale and offset to save a MAD in shader.
camMatrix = textureScaleAndBias * worldToShadow;
}
}
}