mirror of
https://github.com/maxartz15/VolumetricLighting.git
synced 2025-06-27 05:06:03 +02:00
Added the project.
This commit is contained in:
9
Assets/VolumetricFog/Editor.meta
Normal file
9
Assets/VolumetricFog/Editor.meta
Normal file
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d612dacdb3ca214c9322d7f9c288f22
|
||||
folderAsset: yes
|
||||
timeCreated: 1457126022
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
73
Assets/VolumetricFog/Editor/FogLightEditor.cs
Normal file
73
Assets/VolumetricFog/Editor/FogLightEditor.cs
Normal file
@ -0,0 +1,73 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
[CustomEditor(typeof(FogLight))]
|
||||
[CanEditMultipleObjects]
|
||||
public class FogLightEditor : Editor
|
||||
{
|
||||
SerializedProperty m_IntensityMult;
|
||||
SerializedProperty m_RangeMult;
|
||||
SerializedProperty m_ForceOnForFog;
|
||||
SerializedProperty m_Shadows;
|
||||
SerializedProperty m_ShadowmapRes;
|
||||
SerializedProperty m_BlurIterations;
|
||||
SerializedProperty m_BlurSize;
|
||||
SerializedProperty m_Bounded;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
m_IntensityMult = serializedObject.FindProperty ("m_IntensityMult");
|
||||
m_RangeMult = serializedObject.FindProperty ("m_RangeMult");
|
||||
m_ForceOnForFog = serializedObject.FindProperty ("m_ForceOnForFog");
|
||||
m_Shadows = serializedObject.FindProperty ("m_Shadows");
|
||||
m_ShadowmapRes = serializedObject.FindProperty ("m_ShadowmapRes");
|
||||
m_BlurIterations = serializedObject.FindProperty ("m_BlurIterations");
|
||||
m_BlurSize = serializedObject.FindProperty ("m_BlurSize");
|
||||
m_Bounded = serializedObject.FindProperty("m_Bounded");
|
||||
}
|
||||
|
||||
override public void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.PropertyField(m_IntensityMult);
|
||||
EditorGUILayout.PropertyField(m_RangeMult);
|
||||
EditorGUILayout.PropertyField(m_ForceOnForFog);
|
||||
|
||||
// Section below just for light types with shadow
|
||||
bool supportsShadows = false;
|
||||
bool isAreaLight = false;
|
||||
foreach (FogLight fogLight in targets)
|
||||
{
|
||||
if (fogLight.type == FogLight.Type.Area)
|
||||
{
|
||||
supportsShadows = true;
|
||||
isAreaLight = true;
|
||||
break;
|
||||
}
|
||||
else if (fogLight.type == FogLight.Type.Directional)
|
||||
{
|
||||
supportsShadows = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (supportsShadows)
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.PropertyField(m_Shadows);
|
||||
EditorGUILayout.PropertyField(m_ShadowmapRes);
|
||||
EditorGUILayout.PropertyField(m_BlurIterations);
|
||||
EditorGUILayout.PropertyField(m_BlurSize);
|
||||
}
|
||||
|
||||
if (isAreaLight)
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(m_Bounded);
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
12
Assets/VolumetricFog/Editor/FogLightEditor.cs.meta
Normal file
12
Assets/VolumetricFog/Editor/FogLightEditor.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93f87826f4342d7448c226b220a4342e
|
||||
timeCreated: 1457126034
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
54
Assets/VolumetricFog/FogEllipsoid.prefab
Normal file
54
Assets/VolumetricFog/FogEllipsoid.prefab
Normal file
@ -0,0 +1,54 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &141782
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 100100000}
|
||||
serializedVersion: 4
|
||||
m_Component:
|
||||
- 4: {fileID: 463416}
|
||||
- 114: {fileID: 11477592}
|
||||
m_Layer: 0
|
||||
m_Name: FogEllipsoid
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &463416
|
||||
Transform:
|
||||
m_ObjectHideFlags: 1
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 100100000}
|
||||
m_GameObject: {fileID: 141782}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
--- !u!114 &11477592
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 1
|
||||
m_PrefabParentObject: {fileID: 0}
|
||||
m_PrefabInternal: {fileID: 100100000}
|
||||
m_GameObject: {fileID: 141782}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 789ae5fef74e6754ab3c20bdeca67ad9, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Radius: 1
|
||||
m_Stretch: 2
|
||||
--- !u!1001 &100100000
|
||||
Prefab:
|
||||
m_ObjectHideFlags: 1
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications: []
|
||||
m_RemovedComponents: []
|
||||
m_ParentPrefab: {fileID: 0}
|
||||
m_RootGameObject: {fileID: 141782}
|
||||
m_IsPrefabParent: 1
|
8
Assets/VolumetricFog/FogEllipsoid.prefab.meta
Normal file
8
Assets/VolumetricFog/FogEllipsoid.prefab.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df830a50a2bb02541a47504c9da3b3cf
|
||||
timeCreated: 1447329149
|
||||
licenseType: Pro
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
9
Assets/VolumetricFog/Scripts.meta
Normal file
9
Assets/VolumetricFog/Scripts.meta
Normal file
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dcf5e0fc0fb8d64458536282eee32001
|
||||
folderAsset: yes
|
||||
timeCreated: 1477748633
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
59
Assets/VolumetricFog/Scripts/FogEllipsoid.cs
Normal file
59
Assets/VolumetricFog/Scripts/FogEllipsoid.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using UnityEngine;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
public class FogEllipsoid : MonoBehaviour
|
||||
{
|
||||
public enum Blend
|
||||
{
|
||||
Additive,
|
||||
Multiplicative
|
||||
}
|
||||
|
||||
public Blend m_Blend = Blend.Additive;
|
||||
public float m_Density = 1.0f;
|
||||
[MinValue(0)]
|
||||
public float m_Radius = 1.0f;
|
||||
[MinValue(0)]
|
||||
public float m_Stretch = 2.0f;
|
||||
[Range(0, 1)]
|
||||
public float m_Feather = 0.7f;
|
||||
[Range(0, 1)]
|
||||
public float m_NoiseAmount = 0.0f;
|
||||
public float m_NoiseSpeed = 1.0f;
|
||||
[MinValue(0)]
|
||||
public float m_NoiseScale = 1.0f;
|
||||
|
||||
bool m_AddedToLightManager = false;
|
||||
|
||||
void AddToLightManager()
|
||||
{
|
||||
if (!m_AddedToLightManager)
|
||||
m_AddedToLightManager = LightManagerFogEllipsoids.Add(this);
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
AddToLightManager();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// LightManager might not have been available during this light's OnEnable(), so keep trying.
|
||||
AddToLightManager();
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
LightManagerFogEllipsoids.Remove(this);
|
||||
m_AddedToLightManager = false;
|
||||
}
|
||||
|
||||
void OnDrawGizmosSelected()
|
||||
{
|
||||
Matrix4x4 m = Matrix4x4.identity;
|
||||
Transform t = transform;
|
||||
m.SetTRS(t.position, t.rotation, new Vector3(1.0f, m_Stretch, 1.0f));
|
||||
Gizmos.matrix = m;
|
||||
Gizmos.DrawWireSphere(Vector3.zero, m_Radius);
|
||||
}
|
||||
}
|
12
Assets/VolumetricFog/Scripts/FogEllipsoid.cs.meta
Normal file
12
Assets/VolumetricFog/Scripts/FogEllipsoid.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 789ae5fef74e6754ab3c20bdeca67ad9
|
||||
timeCreated: 1447327681
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
157
Assets/VolumetricFog/Scripts/FogLight.DirectionalShadow.cs
Normal file
157
Assets/VolumetricFog/Scripts/FogLight.DirectionalShadow.cs
Normal file
@ -0,0 +1,157 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
public partial class FogLight : LightOverride
|
||||
{
|
||||
CommandBuffer m_BufGrabShadowmap;
|
||||
CommandBuffer m_BufGrabShadowParams;
|
||||
RenderTexture m_Shadowmap;
|
||||
|
||||
ComputeBuffer m_ShadowParamsCB;
|
||||
//[HideInInspector]
|
||||
public Shader m_BlurShadowmapShader;
|
||||
Material m_BlurShadowmapMaterial;
|
||||
|
||||
public Shader m_CopyShadowParamsShader;
|
||||
Material m_CopyShadowParamsMaterial;
|
||||
|
||||
bool directionalShadow {get{ return m_Shadows && type == Type.Directional; }}
|
||||
|
||||
void InitDirectionalShadowmap()
|
||||
{
|
||||
if (m_BufGrabShadowmap != null || !directionalShadow)
|
||||
return;
|
||||
|
||||
Light light = GetComponent<Light>();
|
||||
|
||||
m_BufGrabShadowmap = new CommandBuffer();
|
||||
m_BufGrabShadowmap.name = "Grab shadowmap for Volumetric Fog";
|
||||
light.AddCommandBuffer(LightEvent.AfterShadowMap, m_BufGrabShadowmap);
|
||||
|
||||
m_BufGrabShadowParams = new CommandBuffer();
|
||||
m_BufGrabShadowParams.name = "Grab shadow params for Volumetric Fog";
|
||||
light.AddCommandBuffer(LightEvent.BeforeScreenspaceMask, m_BufGrabShadowParams);
|
||||
|
||||
m_BlurShadowmapMaterial = new Material(m_BlurShadowmapShader);
|
||||
m_BlurShadowmapMaterial.hideFlags = HideFlags.HideAndDontSave;
|
||||
|
||||
m_CopyShadowParamsMaterial = new Material(m_CopyShadowParamsShader);
|
||||
m_CopyShadowParamsMaterial.hideFlags = HideFlags.HideAndDontSave;
|
||||
}
|
||||
|
||||
int[] temp;
|
||||
|
||||
public void UpdateDirectionalShadowmap()
|
||||
{
|
||||
InitDirectionalShadowmap();
|
||||
|
||||
if (m_BufGrabShadowmap != null)
|
||||
m_BufGrabShadowmap.Clear();
|
||||
if (m_BufGrabShadowParams != null)
|
||||
m_BufGrabShadowParams.Clear();
|
||||
|
||||
if (!directionalShadow)
|
||||
return;
|
||||
|
||||
// Copy directional shadowmap params - they're only set for regular shaders, but we need them in compute
|
||||
if (m_ShadowParamsCB == null)
|
||||
m_ShadowParamsCB = new ComputeBuffer(1, 336);
|
||||
Graphics.SetRandomWriteTarget(2, m_ShadowParamsCB);
|
||||
m_BufGrabShadowParams.DrawProcedural(Matrix4x4.identity, m_CopyShadowParamsMaterial, 0, MeshTopology.Points, 1);
|
||||
|
||||
// TODO: get the real size of the shadowmap
|
||||
int startRes = 4096;
|
||||
// To make things easier, blurred shadowmap is at most half the size of the regular.
|
||||
int targetRes = Mathf.Min((int)m_ShadowmapRes, startRes/2);
|
||||
int downsampleSteps = (int)Mathf.Log(startRes / targetRes, 2);
|
||||
|
||||
RenderTargetIdentifier shadowmap = BuiltinRenderTextureType.CurrentActive;
|
||||
m_BufGrabShadowmap.SetShadowSamplingMode(shadowmap, ShadowSamplingMode.RawDepth);
|
||||
|
||||
// RFloat for ESM, RGHalf for VSM
|
||||
RenderTextureFormat format = RenderTextureFormat.RGHalf;
|
||||
|
||||
ReleaseTemporary(ref m_Shadowmap);
|
||||
m_Shadowmap = RenderTexture.GetTemporary(targetRes, targetRes, 0, format, RenderTextureReadWrite.Linear);
|
||||
m_Shadowmap.filterMode = FilterMode.Bilinear;
|
||||
m_Shadowmap.wrapMode = TextureWrapMode.Clamp;
|
||||
|
||||
if (temp == null || temp.Length != downsampleSteps - 1)
|
||||
temp = new int[downsampleSteps - 1];
|
||||
|
||||
for (int i = 0, currentRes = startRes/2; i < downsampleSteps; i++)
|
||||
{
|
||||
m_BufGrabShadowmap.SetGlobalVector("_TexelSize", new Vector4(0.5f/currentRes, 0.5f/currentRes, 0, 0));
|
||||
|
||||
RenderTargetIdentifier targetRT;
|
||||
|
||||
if (i < downsampleSteps - 1)
|
||||
{
|
||||
temp[i] = Shader.PropertyToID("ShadowmapDownscaleTemp" + i);
|
||||
m_BufGrabShadowmap.GetTemporaryRT(temp[i], currentRes, currentRes, 0, FilterMode.Bilinear, format, RenderTextureReadWrite.Linear);
|
||||
targetRT = new RenderTargetIdentifier(temp[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Last step: write into the shadowmap texture
|
||||
targetRT = new RenderTargetIdentifier(m_Shadowmap);
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
// This step should convert to ESM/VSM
|
||||
// m_BufGrabShadowmap.Blit(shadowmap, targetRT);
|
||||
m_BufGrabShadowmap.SetGlobalTexture("_DirShadowmap", shadowmap);
|
||||
m_BufGrabShadowmap.Blit(null, targetRT, m_BlurShadowmapMaterial, /*sample & convert to VSM*/ 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_BufGrabShadowmap.Blit(temp[i - 1], targetRT, m_BlurShadowmapMaterial, /*regular sample*/ 5);
|
||||
}
|
||||
|
||||
currentRes /= 2;
|
||||
}
|
||||
|
||||
//var directionalShadowmapBlurred = Shader.PropertyToID("_DirectionalShadowmapBlurred");
|
||||
//m_BufGrabShadowmap.GetTemporaryRT(directionalShadowmapBlurred, 1024, 1024, 0, FilterMode.Bilinear, RenderTextureFormat.RFloat, RenderTextureReadWrite.Linear);
|
||||
//m_BufGrabShadowmap.Blit(shadowmap, m_Shadowmap);
|
||||
//m_BufGrabShadowmap.SetGlobalTexture(directionalShadowmapBlurred, directionalShadowmapBlurred);
|
||||
}
|
||||
|
||||
void CleanupDirectionalShadowmap()
|
||||
{
|
||||
if (m_BufGrabShadowmap != null)
|
||||
m_BufGrabShadowmap.Clear();
|
||||
|
||||
if (m_BufGrabShadowParams != null)
|
||||
m_BufGrabShadowParams.Clear();
|
||||
|
||||
if(m_ShadowParamsCB != null)
|
||||
m_ShadowParamsCB.Release();
|
||||
m_ShadowParamsCB = null;
|
||||
}
|
||||
|
||||
public bool SetUpDirectionalShadowmapForSampling(bool shadows, ComputeShader cs, int kernel)
|
||||
{
|
||||
if (!shadows || m_ShadowParamsCB == null || m_Shadowmap == null)
|
||||
{
|
||||
cs.SetFloat("_DirLightShadows", 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
cs.SetFloat("_DirLightShadows", 1);
|
||||
cs.SetBuffer(kernel, "_ShadowParams", m_ShadowParamsCB);
|
||||
cs.SetTexture(kernel, "_DirectionalShadowmap", m_Shadowmap);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReleaseTemporary(ref RenderTexture rt)
|
||||
{
|
||||
if (rt == null)
|
||||
return;
|
||||
|
||||
RenderTexture.ReleaseTemporary(rt);
|
||||
rt = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f567a9d3b2b31ac4d818cea1e427a215
|
||||
timeCreated: 1460031458
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
60
Assets/VolumetricFog/Scripts/FogLight.cs
Normal file
60
Assets/VolumetricFog/Scripts/FogLight.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using UnityEngine;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
public partial class FogLight : LightOverride
|
||||
{
|
||||
public bool m_ForceOnForFog = false;
|
||||
[Tooltip("Only one shadowed fog AreaLight at a time.")]
|
||||
[Header("Shadows")]
|
||||
public bool m_Shadows = false;
|
||||
|
||||
public enum TextureSize
|
||||
{
|
||||
x256 = 256,
|
||||
x512 = 512,
|
||||
x1024 = 1024,
|
||||
}
|
||||
|
||||
[Tooltip("Always at most half the res of the AreaLight's shadowmap.")]
|
||||
public TextureSize m_ShadowmapRes = TextureSize.x256;
|
||||
[Range(0, 3)]
|
||||
public int m_BlurIterations = 0;
|
||||
[MinValue(0)]
|
||||
public float m_BlurSize = 1.0f;
|
||||
[MinValue(0)]
|
||||
[Tooltip("Affects shadow softness.")]
|
||||
public float m_ESMExponent = 40.0f;
|
||||
|
||||
public bool m_Bounded = true;
|
||||
|
||||
public override bool GetForceOn()
|
||||
{
|
||||
return m_ForceOnForFog;
|
||||
}
|
||||
|
||||
bool m_AddedToLightManager = false;
|
||||
|
||||
void AddToLightManager()
|
||||
{
|
||||
if (!m_AddedToLightManager)
|
||||
m_AddedToLightManager = LightManagerFogLights.Add(this);
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
AddToLightManager();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// LightManager might not have been available during this light's OnEnable(), so keep trying.
|
||||
AddToLightManager();
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
LightManagerFogLights.Remove(this);
|
||||
m_AddedToLightManager = false;
|
||||
CleanupDirectionalShadowmap();
|
||||
}
|
||||
}
|
16
Assets/VolumetricFog/Scripts/FogLight.cs.meta
Normal file
16
Assets/VolumetricFog/Scripts/FogLight.cs.meta
Normal file
@ -0,0 +1,16 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 097385f2a510795498370c4d62e61572
|
||||
timeCreated: 1463313191
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- m_BlurShadowmapShader: {fileID: 4800000, guid: 701192b62a7678549918bc87434699fe,
|
||||
type: 3}
|
||||
- m_CopyShadowParamsShader: {fileID: 4800000, guid: 33fc45db43408764c8dd7647b877e561,
|
||||
type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
719
Assets/VolumetricFog/Scripts/VolumetricFog.cs
Normal file
719
Assets/VolumetricFog/Scripts/VolumetricFog.cs
Normal file
@ -0,0 +1,719 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[ExecuteInEditMode]
|
||||
[RequireComponent (typeof(Camera))]
|
||||
public class VolumetricFog : MonoBehaviour
|
||||
{
|
||||
Material m_DebugMaterial;
|
||||
[HideInInspector]
|
||||
public Shader m_DebugShader;
|
||||
[HideInInspector]
|
||||
public Shader m_ShadowmapShader;
|
||||
[HideInInspector]
|
||||
public ComputeShader m_InjectLightingAndDensity;
|
||||
[HideInInspector]
|
||||
public ComputeShader m_Scatter;
|
||||
Material m_ApplyToOpaqueMaterial;
|
||||
[HideInInspector]
|
||||
public Shader m_ApplyToOpaqueShader;
|
||||
Material m_BlurShadowmapMaterial;
|
||||
[HideInInspector]
|
||||
public Shader m_BlurShadowmapShader;
|
||||
[HideInInspector]
|
||||
public Texture2D m_Noise;
|
||||
[HideInInspector]
|
||||
public bool m_Debug = false;
|
||||
[HideInInspector]
|
||||
[Range(0.0f, 1.0f)]
|
||||
public float m_Z = 1.0f;
|
||||
|
||||
[Header("Size")]
|
||||
[MinValue(0.1f)]
|
||||
public float m_NearClip = 0.1f;
|
||||
[MinValue(0.1f)]
|
||||
public float m_FarClipMax = 100.0f;
|
||||
|
||||
[Header("Fog Density")]
|
||||
[FormerlySerializedAs("m_Density")]
|
||||
public float m_GlobalDensityMult = 1.0f;
|
||||
Vector3i m_InjectNumThreads = new Vector3i(16, 2, 16);
|
||||
Vector3i m_ScatterNumThreads = new Vector3i(32, 2, 1);
|
||||
RenderTexture m_VolumeInject;
|
||||
RenderTexture m_VolumeScatter;
|
||||
Vector3i m_VolumeResolution = new Vector3i(160, 90, 128);
|
||||
Camera m_Camera;
|
||||
Camera m_ShadowmapCamera;
|
||||
RenderTexture m_Shadowmap;
|
||||
RenderTexture m_ShadowmapBlurred;
|
||||
int m_ShadowmapRes = 1024;
|
||||
|
||||
// Density
|
||||
public float m_ConstantFog = 0;
|
||||
public float m_HeightFogAmount = 0;
|
||||
public float m_HeightFogExponent = 0;
|
||||
public float m_HeightFogOffset = 0;
|
||||
|
||||
[Tooltip("Noise multiplies with constant fog and height fog, but not with fog ellipsoids.")]
|
||||
[Range(0.0f, 1.0f)]
|
||||
public float m_NoiseFogAmount = 0;
|
||||
public float m_NoiseFogScale = 1;
|
||||
public Wind m_Wind;
|
||||
|
||||
[Range(0.0f, 0.999f)]
|
||||
public float m_Anisotropy = 0.0f;
|
||||
|
||||
[Header("Lights")]
|
||||
[FormerlySerializedAs("m_Intensity")]
|
||||
public float m_GlobalIntensityMult = 1.0f;
|
||||
[MinValue(0)]
|
||||
public float m_AmbientLightIntensity = 0.0f;
|
||||
public Color m_AmbientLightColor = Color.white;
|
||||
|
||||
[HideInInspector]
|
||||
public bool m_BlurShadowmap = false;
|
||||
[HideInInspector]
|
||||
[Range(0, 2)]
|
||||
public int m_ShadowmapDownsample = 1;
|
||||
[HideInInspector]
|
||||
[Range(0.0f, 10.0f)]
|
||||
public float m_BlurSize = 3.0f;
|
||||
[HideInInspector]
|
||||
[Range(1, 4)]
|
||||
public int m_BlurIterations = 2;
|
||||
|
||||
struct Vector3i
|
||||
{
|
||||
public int x, y, z;
|
||||
public Vector3i(int x, int y, int z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
|
||||
struct PointLightParams
|
||||
{
|
||||
public Vector3 pos;
|
||||
public float range;
|
||||
public Vector3 color;
|
||||
float padding;
|
||||
}
|
||||
|
||||
PointLightParams[] m_PointLightParams;
|
||||
ComputeBuffer m_PointLightParamsCB;
|
||||
|
||||
struct TubeLightParams
|
||||
{
|
||||
public Vector3 start;
|
||||
public float range;
|
||||
public Vector3 end;
|
||||
public float radius;
|
||||
public Vector3 color;
|
||||
float padding;
|
||||
}
|
||||
|
||||
TubeLightParams[] m_TubeLightParams;
|
||||
ComputeBuffer m_TubeLightParamsCB;
|
||||
|
||||
struct TubeLightShadowPlaneParams
|
||||
{
|
||||
public Vector4 plane0;
|
||||
public Vector4 plane1;
|
||||
public float feather0;
|
||||
public float feather1;
|
||||
float padding0;
|
||||
float padding1;
|
||||
}
|
||||
|
||||
TubeLightShadowPlaneParams[] m_TubeLightShadowPlaneParams;
|
||||
ComputeBuffer m_TubeLightShadowPlaneParamsCB;
|
||||
|
||||
struct AreaLightParams
|
||||
{
|
||||
public Matrix4x4 mat;
|
||||
public Vector4 pos;
|
||||
public Vector3 color;
|
||||
public float bounded;
|
||||
}
|
||||
|
||||
AreaLightParams[] m_AreaLightParams;
|
||||
ComputeBuffer m_AreaLightParamsCB;
|
||||
|
||||
struct FogEllipsoidParams
|
||||
{
|
||||
public Vector3 pos;
|
||||
public float radius;
|
||||
public Vector3 axis;
|
||||
public float stretch;
|
||||
public float density;
|
||||
public float noiseAmount;
|
||||
public float noiseSpeed;
|
||||
public float noiseScale;
|
||||
public float feather;
|
||||
public float blend;
|
||||
public float padding1;
|
||||
public float padding2;
|
||||
}
|
||||
|
||||
FogEllipsoidParams[] m_FogEllipsoidParams;
|
||||
ComputeBuffer m_FogEllipsoidParamsCB;
|
||||
|
||||
Camera cam{ get { if (m_Camera == null) m_Camera = GetComponent<Camera>(); return m_Camera; }}
|
||||
|
||||
float nearClip { get { return Mathf.Max(0, m_NearClip); } }
|
||||
float farClip { get { return Mathf.Min(cam.farClipPlane, m_FarClipMax); } }
|
||||
|
||||
void ReleaseComputeBuffer(ref ComputeBuffer buffer)
|
||||
{
|
||||
if(buffer != null)
|
||||
buffer.Release();
|
||||
buffer = null;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
void Cleanup()
|
||||
{
|
||||
DestroyImmediate(m_VolumeInject);
|
||||
DestroyImmediate(m_VolumeScatter);
|
||||
ReleaseComputeBuffer(ref m_PointLightParamsCB);
|
||||
ReleaseComputeBuffer(ref m_TubeLightParamsCB);
|
||||
ReleaseComputeBuffer(ref m_TubeLightShadowPlaneParamsCB);
|
||||
ReleaseComputeBuffer(ref m_AreaLightParamsCB);
|
||||
ReleaseComputeBuffer(ref m_FogEllipsoidParamsCB);
|
||||
m_VolumeInject = null;
|
||||
m_VolumeScatter = null;
|
||||
}
|
||||
|
||||
void SanitizeInput()
|
||||
{
|
||||
m_GlobalDensityMult = Mathf.Max(m_GlobalDensityMult, 0);
|
||||
m_ConstantFog = Mathf.Max(m_ConstantFog, 0);
|
||||
m_HeightFogAmount = Mathf.Max(m_HeightFogAmount, 0);
|
||||
}
|
||||
|
||||
void SetUpPointLightBuffers(int kernel)
|
||||
{
|
||||
int count = m_PointLightParamsCB == null ? 0 : m_PointLightParamsCB.count;
|
||||
m_InjectLightingAndDensity.SetFloat("_PointLightsCount", count);
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
if (m_PointLightParams == null || m_PointLightParams.Length != count)
|
||||
m_PointLightParams = new PointLightParams[count];
|
||||
|
||||
HashSet<FogLight> fogLights = LightManagerFogLights.Get();
|
||||
|
||||
int j = 0;
|
||||
for (var x = fogLights.GetEnumerator(); x.MoveNext();)
|
||||
{
|
||||
var fl = x.Current;
|
||||
if (fl == null || fl.type != FogLight.Type.Point || !fl.isOn)
|
||||
continue;
|
||||
|
||||
Light light = fl.light;
|
||||
m_PointLightParams[j].pos = light.transform.position;
|
||||
float range = light.range * fl.m_RangeMult;
|
||||
m_PointLightParams[j].range = 1.0f / (range * range);
|
||||
m_PointLightParams[j].color = new Vector3(light.color.r, light.color.g, light.color.b) * light.intensity * fl.m_IntensityMult;
|
||||
j++;
|
||||
}
|
||||
|
||||
// TODO: try a constant buffer with setfloats instead for perf
|
||||
m_PointLightParamsCB.SetData(m_PointLightParams);
|
||||
m_InjectLightingAndDensity.SetBuffer(kernel, "_PointLights", m_PointLightParamsCB);
|
||||
}
|
||||
|
||||
TubeLightShadowPlane.Params[] sppArr;
|
||||
|
||||
void SetUpTubeLightBuffers(int kernel)
|
||||
{
|
||||
int count = m_TubeLightParamsCB == null ? 0 : m_TubeLightParamsCB.count;
|
||||
m_InjectLightingAndDensity.SetFloat("_TubeLightsCount", count);
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
if (m_TubeLightParams == null || m_TubeLightParams.Length != count)
|
||||
m_TubeLightParams = new TubeLightParams[count];
|
||||
|
||||
if (m_TubeLightShadowPlaneParams == null || m_TubeLightShadowPlaneParams.Length != count)
|
||||
m_TubeLightShadowPlaneParams = new TubeLightShadowPlaneParams[count];
|
||||
|
||||
HashSet<FogLight> fogLights = LightManagerFogLights.Get();
|
||||
|
||||
int j = 0;
|
||||
for (var x = fogLights.GetEnumerator(); x.MoveNext();)
|
||||
{
|
||||
var fl = x.Current;
|
||||
if (fl == null || fl.type != FogLight.Type.Tube || !fl.isOn)
|
||||
continue;
|
||||
|
||||
TubeLight light = fl.tubeLight;
|
||||
Transform t = light.transform;
|
||||
Vector3 pos = t.position;
|
||||
Vector3 halfLength = 0.5f * t.up * light.m_Length;
|
||||
|
||||
// Tube lights
|
||||
m_TubeLightParams[j].start = pos + halfLength;
|
||||
m_TubeLightParams[j].end = pos - halfLength;
|
||||
float range = light.m_Range * fl.m_RangeMult;
|
||||
m_TubeLightParams[j].range = 1.0f / (range * range);
|
||||
m_TubeLightParams[j].color = new Vector3(light.m_Color.r, light.m_Color.g, light.m_Color.b) * light.m_Intensity * fl.m_IntensityMult;
|
||||
m_TubeLightParams[j].radius = light.m_Radius;
|
||||
|
||||
// Tube light shadow planes
|
||||
var p = light.GetShadowPlaneParams(ref sppArr);
|
||||
m_TubeLightShadowPlaneParams[j].plane0 = p[0].plane;
|
||||
m_TubeLightShadowPlaneParams[j].plane1 = p[1].plane;
|
||||
m_TubeLightShadowPlaneParams[j].feather0 = p[0].feather;
|
||||
m_TubeLightShadowPlaneParams[j].feather1 = p[1].feather;
|
||||
|
||||
j++;
|
||||
}
|
||||
|
||||
m_TubeLightParamsCB.SetData(m_TubeLightParams);
|
||||
m_InjectLightingAndDensity.SetBuffer(kernel, "_TubeLights", m_TubeLightParamsCB);
|
||||
m_TubeLightShadowPlaneParamsCB.SetData(m_TubeLightShadowPlaneParams);
|
||||
m_InjectLightingAndDensity.SetBuffer(kernel, "_TubeLightShadowPlanes", m_TubeLightShadowPlaneParamsCB);
|
||||
}
|
||||
|
||||
void SetUpAreaLightBuffers(int kernel)
|
||||
{
|
||||
int count = m_AreaLightParamsCB == null ? 0 : m_AreaLightParamsCB.count;
|
||||
m_InjectLightingAndDensity.SetFloat("_AreaLightsCount", count);
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
if (m_AreaLightParams == null || m_AreaLightParams.Length != count)
|
||||
m_AreaLightParams = new AreaLightParams[count];
|
||||
|
||||
HashSet<FogLight> fogLights = LightManagerFogLights.Get();
|
||||
|
||||
int shadowedAreaLightIndex = fogLights.Count;
|
||||
int j = 0;
|
||||
for (var x = fogLights.GetEnumerator(); x.MoveNext();)
|
||||
{
|
||||
var fl = x.Current;
|
||||
if (fl == null || fl.type != FogLight.Type.Area || !fl.isOn)
|
||||
continue;
|
||||
|
||||
AreaLight light = fl.areaLight;
|
||||
|
||||
m_AreaLightParams[j].mat = light.GetProjectionMatrix(true);
|
||||
m_AreaLightParams[j].pos = light.GetPosition();
|
||||
m_AreaLightParams[j].color = new Vector3(light.m_Color.r, light.m_Color.g, light.m_Color.b) * light.m_Intensity * fl.m_IntensityMult;
|
||||
m_AreaLightParams[j].bounded = fl.m_Bounded ? 1 : 0;
|
||||
|
||||
if (fl.m_Shadows)
|
||||
{
|
||||
RenderTexture shadowmap = light.GetBlurredShadowmap();
|
||||
if (shadowmap != null)
|
||||
{
|
||||
m_InjectLightingAndDensity.SetTexture(kernel, "_AreaLightShadowmap", shadowmap);
|
||||
m_InjectLightingAndDensity.SetFloat("_ESMExponentAreaLight", fl.m_ESMExponent);
|
||||
shadowedAreaLightIndex = j;
|
||||
}
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
|
||||
m_AreaLightParamsCB.SetData(m_AreaLightParams);
|
||||
m_InjectLightingAndDensity.SetBuffer(kernel, "_AreaLights", m_AreaLightParamsCB);
|
||||
m_InjectLightingAndDensity.SetFloat("_ShadowedAreaLightIndex", shadowedAreaLightIndex);
|
||||
}
|
||||
|
||||
void SetUpFogEllipsoidBuffers(int kernel)
|
||||
{
|
||||
int count = 0;
|
||||
HashSet<FogEllipsoid> fogEllipsoids = LightManagerFogEllipsoids.Get();
|
||||
for (var x = fogEllipsoids.GetEnumerator(); x.MoveNext();) {
|
||||
var fe = x.Current;
|
||||
if (fe != null && fe.enabled && fe.gameObject.activeSelf)
|
||||
count++;
|
||||
}
|
||||
|
||||
m_InjectLightingAndDensity.SetFloat("_FogEllipsoidsCount", count);
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
if (m_FogEllipsoidParams == null || m_FogEllipsoidParams.Length != count)
|
||||
m_FogEllipsoidParams = new FogEllipsoidParams[count];
|
||||
|
||||
int j = 0;
|
||||
for (var x = fogEllipsoids.GetEnumerator(); x.MoveNext();)
|
||||
{
|
||||
var fe = x.Current;
|
||||
if (fe == null || !fe.enabled || !fe.gameObject.activeSelf)
|
||||
continue;
|
||||
|
||||
Transform t = fe.transform;
|
||||
|
||||
m_FogEllipsoidParams[j].pos = t.position;
|
||||
m_FogEllipsoidParams[j].radius = fe.m_Radius * fe.m_Radius;
|
||||
m_FogEllipsoidParams[j].axis = -t.up;
|
||||
m_FogEllipsoidParams[j].stretch = 1.0f/fe.m_Stretch - 1.0f;
|
||||
m_FogEllipsoidParams[j].density = fe.m_Density;
|
||||
m_FogEllipsoidParams[j].noiseAmount = fe.m_NoiseAmount;
|
||||
m_FogEllipsoidParams[j].noiseSpeed = fe.m_NoiseSpeed;
|
||||
m_FogEllipsoidParams[j].noiseScale = fe.m_NoiseScale;
|
||||
m_FogEllipsoidParams[j].feather = 1.0f - fe.m_Feather;
|
||||
m_FogEllipsoidParams[j].blend = fe.m_Blend == FogEllipsoid.Blend.Additive ? 0 : 1;
|
||||
j++;
|
||||
}
|
||||
|
||||
m_FogEllipsoidParamsCB.SetData(m_FogEllipsoidParams);
|
||||
m_InjectLightingAndDensity.SetBuffer(kernel, "_FogEllipsoids", m_FogEllipsoidParamsCB);
|
||||
}
|
||||
|
||||
FogLight GetDirectionalLight()
|
||||
{
|
||||
HashSet<FogLight> fogLights = LightManagerFogLights.Get();
|
||||
FogLight fogLight = null;
|
||||
|
||||
for (var x = fogLights.GetEnumerator(); x.MoveNext();)
|
||||
{
|
||||
var fl = x.Current;
|
||||
if (fl == null || fl.type != FogLight.Type.Directional || !fl.isOn)
|
||||
continue;
|
||||
|
||||
fogLight = fl;
|
||||
break;
|
||||
}
|
||||
|
||||
return fogLight;
|
||||
}
|
||||
|
||||
FogLight m_DirectionalLight;
|
||||
|
||||
void OnPreRender()
|
||||
{
|
||||
m_DirectionalLight = GetDirectionalLight();
|
||||
|
||||
if (m_DirectionalLight != null)
|
||||
m_DirectionalLight.UpdateDirectionalShadowmap();
|
||||
}
|
||||
|
||||
float[] m_dirLightColor;
|
||||
float[] m_dirLightDir;
|
||||
|
||||
void SetUpDirectionalLight(int kernel)
|
||||
{
|
||||
if (m_dirLightColor == null || m_dirLightColor.Length != 3)
|
||||
m_dirLightColor = new float[3];
|
||||
if (m_dirLightDir == null || m_dirLightDir.Length != 3)
|
||||
m_dirLightDir = new float[3];
|
||||
|
||||
if (m_DirectionalLight == null)
|
||||
{
|
||||
m_dirLightColor[0] = 0;
|
||||
m_dirLightColor[1] = 0;
|
||||
m_dirLightColor[2] = 0;
|
||||
m_InjectLightingAndDensity.SetFloats("_DirLightColor", m_dirLightColor);
|
||||
return;
|
||||
}
|
||||
|
||||
m_DirectionalLight.SetUpDirectionalShadowmapForSampling(m_DirectionalLight.m_Shadows, m_InjectLightingAndDensity, kernel);
|
||||
// TODO: if above fails, disable shadows
|
||||
|
||||
Light light = m_DirectionalLight.light;
|
||||
Vector4 color = light.color;
|
||||
color *= light.intensity * m_DirectionalLight.m_IntensityMult;
|
||||
m_dirLightColor[0] = color.x;
|
||||
m_dirLightColor[1] = color.y;
|
||||
m_dirLightColor[2] = color.z;
|
||||
m_InjectLightingAndDensity.SetFloats("_DirLightColor", m_dirLightColor);
|
||||
Vector3 dir = light.GetComponent<Transform>().forward;
|
||||
m_dirLightDir[0] = dir.x;
|
||||
m_dirLightDir[1] = dir.y;
|
||||
m_dirLightDir[2] = dir.z;
|
||||
m_InjectLightingAndDensity.SetFloats("_DirLightDir", m_dirLightDir);
|
||||
|
||||
}
|
||||
|
||||
float[] m_fogParams;
|
||||
float[] m_windDir;
|
||||
float[] m_ambientLight;
|
||||
|
||||
void SetUpForScatter(int kernel)
|
||||
{
|
||||
SanitizeInput();
|
||||
InitResources();
|
||||
SetFrustumRays();
|
||||
|
||||
// Compensate for more light and density being injected in per world space meter when near and far are closer.
|
||||
// TODO: Not quite correct yet.
|
||||
float depthCompensation = (farClip - nearClip) * 0.01f;
|
||||
m_InjectLightingAndDensity.SetFloat("_Density", m_GlobalDensityMult * 0.001f * depthCompensation);
|
||||
m_InjectLightingAndDensity.SetFloat("_Intensity", m_GlobalIntensityMult);
|
||||
m_InjectLightingAndDensity.SetFloat("_Anisotropy", m_Anisotropy);
|
||||
m_InjectLightingAndDensity.SetTexture(kernel, "_VolumeInject", m_VolumeInject);
|
||||
m_InjectLightingAndDensity.SetTexture(kernel, "_Shadowmap", m_BlurShadowmap ? m_ShadowmapBlurred : (m_Shadowmap != null ? m_Shadowmap : (Texture)Texture2D.whiteTexture));
|
||||
m_InjectLightingAndDensity.SetTexture(kernel, "_Noise", m_Noise);
|
||||
|
||||
if (m_fogParams == null || m_fogParams.Length != 4)
|
||||
m_fogParams = new float[4];
|
||||
if (m_windDir == null || m_windDir.Length != 3)
|
||||
m_windDir = new float[3];
|
||||
if (m_ambientLight == null || m_ambientLight.Length != 3)
|
||||
m_ambientLight = new float[3];
|
||||
m_fogParams[0] = m_ConstantFog;
|
||||
m_fogParams[1] = m_HeightFogExponent;
|
||||
m_fogParams[2] = m_HeightFogOffset;
|
||||
m_fogParams[3] = m_HeightFogAmount;
|
||||
|
||||
m_InjectLightingAndDensity.SetFloats("_FogParams", m_fogParams);
|
||||
m_InjectLightingAndDensity.SetFloat("_NoiseFogAmount", m_NoiseFogAmount);
|
||||
m_InjectLightingAndDensity.SetFloat("_NoiseFogScale", m_NoiseFogScale);
|
||||
m_InjectLightingAndDensity.SetFloat("_WindSpeed", m_Wind == null ? 0 : m_Wind.m_Speed);
|
||||
Vector3 windDir = m_Wind == null ? Vector3.forward : m_Wind.transform.forward;
|
||||
m_windDir[0] = windDir.x;
|
||||
m_windDir[1] = windDir.y;
|
||||
m_windDir[2] = windDir.z;
|
||||
m_InjectLightingAndDensity.SetFloats("_WindDir", m_windDir);
|
||||
m_InjectLightingAndDensity.SetFloat("_Time", Time.time);
|
||||
m_InjectLightingAndDensity.SetFloat("_NearOverFarClip", nearClip/farClip);
|
||||
Color ambient = m_AmbientLightColor * m_AmbientLightIntensity * 0.1f;
|
||||
m_ambientLight[0] = ambient.r;
|
||||
m_ambientLight[1] = ambient.g;
|
||||
m_ambientLight[2] = ambient.b;
|
||||
m_InjectLightingAndDensity.SetFloats("_AmbientLight", m_ambientLight);
|
||||
|
||||
SetUpPointLightBuffers(kernel);
|
||||
SetUpTubeLightBuffers(kernel);
|
||||
SetUpAreaLightBuffers(kernel);
|
||||
SetUpFogEllipsoidBuffers(kernel);
|
||||
SetUpDirectionalLight(kernel);
|
||||
}
|
||||
|
||||
void Scatter()
|
||||
{
|
||||
// Inject lighting and density
|
||||
int kernel = 0;
|
||||
|
||||
SetUpForScatter(kernel);
|
||||
|
||||
m_InjectLightingAndDensity.Dispatch(kernel, m_VolumeResolution.x/m_InjectNumThreads.x, m_VolumeResolution.y/m_InjectNumThreads.y, m_VolumeResolution.z/m_InjectNumThreads.z);
|
||||
|
||||
// Solve scattering
|
||||
m_Scatter.SetTexture(0, "_VolumeInject", m_VolumeInject);
|
||||
m_Scatter.SetTexture(0, "_VolumeScatter", m_VolumeScatter);
|
||||
m_Scatter.Dispatch(0, m_VolumeResolution.x/m_ScatterNumThreads.x, m_VolumeResolution.y/m_ScatterNumThreads.y, 1);
|
||||
|
||||
ReleaseTempResources();
|
||||
}
|
||||
|
||||
void DebugDisplay(RenderTexture src, RenderTexture dest)
|
||||
{
|
||||
InitMaterial(ref m_DebugMaterial, m_DebugShader);
|
||||
|
||||
m_DebugMaterial.SetTexture("_VolumeInject", m_VolumeInject);
|
||||
m_DebugMaterial.SetTexture("_VolumeScatter", m_VolumeScatter);
|
||||
m_DebugMaterial.SetTexture("_Shadowmap", m_Shadowmap);
|
||||
m_DebugMaterial.SetTexture("_ShadowmapBlurred", m_ShadowmapBlurred);
|
||||
m_DebugMaterial.SetFloat("_Z", m_Z);
|
||||
|
||||
m_DebugMaterial.SetTexture("_MainTex", src);
|
||||
|
||||
Graphics.Blit(src, dest, m_DebugMaterial);
|
||||
}
|
||||
|
||||
void SetUpGlobalFogSamplingUniforms(int width, int height)
|
||||
{
|
||||
Shader.SetGlobalTexture("_VolumeScatter", m_VolumeScatter);
|
||||
Shader.SetGlobalVector("_Screen_TexelSize", new Vector4(1.0f / width, 1.0f / height, width, height));
|
||||
Shader.SetGlobalVector("_VolumeScatter_TexelSize", new Vector4(1.0f / m_VolumeResolution.x, 1.0f / m_VolumeResolution.y, 1.0f / m_VolumeResolution.z, 0));
|
||||
Shader.SetGlobalFloat("_CameraFarOverMaxFar", cam.farClipPlane / farClip);
|
||||
Shader.SetGlobalFloat("_NearOverFarClip", nearClip / farClip);
|
||||
}
|
||||
|
||||
[ImageEffectOpaque]
|
||||
void OnRenderImage(RenderTexture src, RenderTexture dest)
|
||||
{
|
||||
if(m_Debug)
|
||||
{
|
||||
DebugDisplay(src, dest);
|
||||
return;
|
||||
}
|
||||
|
||||
Scatter();
|
||||
|
||||
InitMaterial(ref m_ApplyToOpaqueMaterial, m_ApplyToOpaqueShader);
|
||||
|
||||
// TODO: This shouldn't be needed. Is it because the shader doesn't have the Property block?
|
||||
m_ApplyToOpaqueMaterial.SetTexture("_MainTex", src);
|
||||
|
||||
SetUpGlobalFogSamplingUniforms(src.width, src.height);
|
||||
|
||||
Graphics.Blit(src, dest, m_ApplyToOpaqueMaterial);
|
||||
|
||||
VolumetricFogInForward(true);
|
||||
}
|
||||
|
||||
void OnPostRender()
|
||||
{
|
||||
VolumetricFogInForward(false);
|
||||
}
|
||||
|
||||
void VolumetricFogInForward(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
Shader.EnableKeyword("VOLUMETRIC_FOG");
|
||||
else
|
||||
Shader.DisableKeyword("VOLUMETRIC_FOG");
|
||||
}
|
||||
|
||||
Vector3 ViewportToLocalPoint(Camera c, Transform t, Vector3 p)
|
||||
{
|
||||
// TODO: viewporttoworldpoint inverts the clip-to-world matrix every time without caching it.
|
||||
return t.InverseTransformPoint(c.ViewportToWorldPoint(p));
|
||||
}
|
||||
|
||||
static readonly Vector2[] frustumUVs =
|
||||
new Vector2[] {new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1)};
|
||||
static float[] frustumRays = new float[16];
|
||||
|
||||
void SetFrustumRays()
|
||||
{
|
||||
float far = farClip;
|
||||
Vector3 cameraPos = cam.transform.position;
|
||||
Vector2[] uvs = frustumUVs;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
Vector3 ray = cam.ViewportToWorldPoint(new Vector3(uvs[i].x, uvs[i].y, far)) - cameraPos;
|
||||
frustumRays[i*4+0] = ray.x;
|
||||
frustumRays[i*4+1] = ray.y;
|
||||
frustumRays[i*4+2] = ray.z;
|
||||
frustumRays[i*4+3] = 0;
|
||||
}
|
||||
|
||||
m_InjectLightingAndDensity.SetVector("_CameraPos", cameraPos);
|
||||
m_InjectLightingAndDensity.SetFloats("_FrustumRays", frustumRays);
|
||||
}
|
||||
|
||||
void InitVolume(ref RenderTexture volume)
|
||||
{
|
||||
if(volume)
|
||||
return;
|
||||
|
||||
volume = new RenderTexture (m_VolumeResolution.x, m_VolumeResolution.y, 0, RenderTextureFormat.ARGBHalf);
|
||||
volume.volumeDepth = m_VolumeResolution.z;
|
||||
volume.dimension = UnityEngine.Rendering.TextureDimension.Tex3D;
|
||||
volume.enableRandomWrite = true;
|
||||
volume.Create();
|
||||
}
|
||||
|
||||
void CreateBuffer(ref ComputeBuffer buffer, int count, int stride)
|
||||
{
|
||||
if (buffer != null && buffer.count == count)
|
||||
return;
|
||||
|
||||
if(buffer != null)
|
||||
{
|
||||
buffer.Release();
|
||||
buffer = null;
|
||||
}
|
||||
|
||||
if (count <= 0)
|
||||
return;
|
||||
|
||||
buffer = new ComputeBuffer(count, stride);
|
||||
}
|
||||
|
||||
void InitResources ()
|
||||
{
|
||||
// Shadowmap
|
||||
m_Shadowmap = RenderTexture.GetTemporary(m_ShadowmapRes, m_ShadowmapRes, 24, RenderTextureFormat.RFloat);
|
||||
m_Shadowmap.filterMode = FilterMode.Bilinear;
|
||||
m_Shadowmap.wrapMode = TextureWrapMode.Clamp;
|
||||
|
||||
// Volume
|
||||
InitVolume(ref m_VolumeInject);
|
||||
InitVolume(ref m_VolumeScatter);
|
||||
|
||||
|
||||
// Compute buffers
|
||||
int pointLightCount = 0, tubeLightCount = 0, areaLightCount = 0;
|
||||
HashSet<FogLight> fogLights = LightManagerFogLights.Get();
|
||||
for (var x = fogLights.GetEnumerator(); x.MoveNext();)
|
||||
{
|
||||
var fl = x.Current;
|
||||
if (fl == null)
|
||||
continue;
|
||||
|
||||
bool isOn = fl.isOn;
|
||||
|
||||
switch(fl.type)
|
||||
{
|
||||
case FogLight.Type.Point: if (isOn) pointLightCount++; break;
|
||||
case FogLight.Type.Tube: if (isOn) tubeLightCount++; break;
|
||||
case FogLight.Type.Area: if (isOn) areaLightCount++; break;
|
||||
}
|
||||
}
|
||||
|
||||
// PointLightParams {float3 float float3 float} -> 32 bytes
|
||||
CreateBuffer(ref m_PointLightParamsCB, pointLightCount, 32);
|
||||
|
||||
// TubeLightParams {float3 float float3 float float3 float} -> 48 bytes
|
||||
CreateBuffer(ref m_TubeLightParamsCB, tubeLightCount, 48);
|
||||
|
||||
// TubeLightShadowPlaneParams {float4 float4 float float float float} -> 48 bytes
|
||||
CreateBuffer(ref m_TubeLightShadowPlaneParamsCB, tubeLightCount, 48);
|
||||
|
||||
// TubeLightParams {float4x4 float4 float3 float} -> 96 bytes
|
||||
CreateBuffer(ref m_AreaLightParamsCB, areaLightCount, 96);
|
||||
|
||||
// FogEllipsoidParams {float3 float float3 9xfloat} -> 64 bytes
|
||||
HashSet<FogEllipsoid> fogEllipsoids = LightManagerFogEllipsoids.Get();
|
||||
CreateBuffer(ref m_FogEllipsoidParamsCB, fogEllipsoids == null ? 0 : fogEllipsoids.Count, 64);
|
||||
}
|
||||
|
||||
void ReleaseTemporary(ref RenderTexture rt)
|
||||
{
|
||||
if (rt == null)
|
||||
return;
|
||||
|
||||
RenderTexture.ReleaseTemporary(rt);
|
||||
rt = null;
|
||||
}
|
||||
|
||||
void ReleaseTempResources()
|
||||
{
|
||||
ReleaseTemporary(ref m_Shadowmap);
|
||||
ReleaseTemporary(ref m_ShadowmapBlurred);
|
||||
}
|
||||
|
||||
void InitMaterial(ref Material material, Shader shader)
|
||||
{
|
||||
if (material)
|
||||
return;
|
||||
|
||||
if (!shader)
|
||||
{
|
||||
Debug.LogError("Missing shader");
|
||||
return;
|
||||
}
|
||||
|
||||
material = new Material(shader);
|
||||
material.hideFlags = HideFlags.HideAndDontSave;
|
||||
}
|
||||
|
||||
void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = Color.yellow;
|
||||
Gizmos.matrix = transform.localToWorldMatrix;
|
||||
Gizmos.DrawFrustum(Vector3.zero, cam.fieldOfView, farClip, nearClip, cam.aspect);
|
||||
}
|
||||
}
|
23
Assets/VolumetricFog/Scripts/VolumetricFog.cs.meta
Normal file
23
Assets/VolumetricFog/Scripts/VolumetricFog.cs.meta
Normal file
@ -0,0 +1,23 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f35c02a2c6230584391703bf32006791
|
||||
timeCreated: 1432253353
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- m_DebugShader: {fileID: 4800000, guid: 91e29ad407521e447a7254f5c1ea9b54, type: 3}
|
||||
- m_ShadowmapShader: {fileID: 4800000, guid: 2c4cd42b134f7864fa579350e9cf6896, type: 3}
|
||||
- m_InjectLightingAndDensity: {fileID: 7200000, guid: 55812fd61bd54104aa23612fc28bfeb8,
|
||||
type: 3}
|
||||
- m_Scatter: {fileID: 7200000, guid: 2a7c01c2354d03a4e9c31096bf130b6a, type: 3}
|
||||
- m_ApplyToOpaqueShader: {fileID: 4800000, guid: 6f73df8e11615074dbf62a3a384af521,
|
||||
type: 3}
|
||||
- m_BlurShadowmapShader: {fileID: 4800000, guid: 701192b62a7678549918bc87434699fe,
|
||||
type: 3}
|
||||
- m_Noise: {fileID: 2800000, guid: 35eb47181d630134c97488476efbda59, type: 3}
|
||||
- m_Sun: {instanceID: 0}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
9
Assets/VolumetricFog/Shaders.meta
Normal file
9
Assets/VolumetricFog/Shaders.meta
Normal file
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9f2001f237efb348b16ef606855bab7
|
||||
folderAsset: yes
|
||||
timeCreated: 1477749236
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
48
Assets/VolumetricFog/Shaders/ApplyToOpaque.shader
Normal file
48
Assets/VolumetricFog/Shaders/ApplyToOpaque.shader
Normal file
@ -0,0 +1,48 @@
|
||||
Shader "Hidden/ApplyToOpaque" {
|
||||
SubShader {
|
||||
Pass {
|
||||
ZTest Always Cull Off ZWrite Off
|
||||
Blend Off
|
||||
|
||||
CGPROGRAM
|
||||
#pragma target 3.0
|
||||
#include "UnityCG.cginc"
|
||||
#include "VolumetricFog.cginc"
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
sampler2D _CameraDepthTexture;
|
||||
sampler2D _MainTex;
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 pos : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
v2f vert (appdata_img v)
|
||||
{
|
||||
v2f o;
|
||||
o.pos = v.vertex;
|
||||
o.pos.xy = o.pos.xy * 2 - 1;
|
||||
o.uv = v.texcoord.xy;
|
||||
|
||||
#if UNITY_UV_STARTS_AT_TOP
|
||||
if (_ProjectionParams.x < 0)
|
||||
o.uv.y = 1-o.uv.y;
|
||||
#endif
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
half4 frag (v2f i) : SV_Target
|
||||
{
|
||||
half linear01Depth = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv));
|
||||
half4 fog = Fog(linear01Depth, i.uv);
|
||||
return tex2D(_MainTex, i.uv) * fog.a + fog;
|
||||
}
|
||||
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
9
Assets/VolumetricFog/Shaders/ApplyToOpaque.shader.meta
Normal file
9
Assets/VolumetricFog/Shaders/ApplyToOpaque.shader.meta
Normal file
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f73df8e11615074dbf62a3a384af521
|
||||
timeCreated: 1432216767
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
240
Assets/VolumetricFog/Shaders/BlurShadowmap.shader
Normal file
240
Assets/VolumetricFog/Shaders/BlurShadowmap.shader
Normal file
@ -0,0 +1,240 @@
|
||||
|
||||
Shader "Hidden/BlurShadowmap" {
|
||||
Properties
|
||||
{
|
||||
_MainTex ("Base (RGB)", 2D) = "white" {}
|
||||
_Bloom ("Bloom (RGB)", 2D) = "black" {}
|
||||
}
|
||||
|
||||
CGINCLUDE
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
half4 _TexelSize;
|
||||
|
||||
struct v2f_tap
|
||||
{
|
||||
float4 pos : SV_POSITION;
|
||||
half2 uv20 : TEXCOORD0;
|
||||
half2 uv21 : TEXCOORD1;
|
||||
half2 uv22 : TEXCOORD2;
|
||||
half2 uv23 : TEXCOORD3;
|
||||
};
|
||||
|
||||
v2f_tap vert4Tap ( appdata_img v )
|
||||
{
|
||||
v2f_tap o;
|
||||
o.pos = v.vertex;
|
||||
|
||||
o.uv20 = v.texcoord + _TexelSize.xy;
|
||||
o.uv21 = v.texcoord + _TexelSize.xy * half2(-0.5, -0.5);
|
||||
o.uv22 = v.texcoord + _TexelSize.xy * half2( 0.5, -0.5);
|
||||
o.uv23 = v.texcoord + _TexelSize.xy * half2(-0.5, 0.5);
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
// TODO: consolidate with the above, but make sure both area and dir shadows work
|
||||
v2f_tap vert4TapDir ( appdata_img v )
|
||||
{
|
||||
v2f_tap o;
|
||||
o.pos = UnityObjectToClipPos(v.vertex);
|
||||
|
||||
o.uv20 = v.texcoord + _TexelSize.xy;
|
||||
o.uv21 = v.texcoord + _TexelSize.xy * half2(-0.5, -0.5);
|
||||
o.uv22 = v.texcoord + _TexelSize.xy * half2( 0.5, -0.5);
|
||||
o.uv23 = v.texcoord + _TexelSize.xy * half2(-0.5, 0.5);
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
float4 _ZParams;
|
||||
float _ESMExponent;
|
||||
Texture2D _Shadowmap;
|
||||
SamplerComparisonState sampler_Shadowmap;
|
||||
|
||||
// To get a sampler, which doesn't do comparison
|
||||
Texture2D _ShadowmapDummy;
|
||||
SamplerState sampler_ShadowmapDummy;
|
||||
|
||||
#define VSM 1
|
||||
|
||||
float4 fragDownsampleFromShadowmapFormat ( v2f_tap i ) : SV_Target
|
||||
{
|
||||
float4 z;
|
||||
z.r = _Shadowmap.Sample(sampler_ShadowmapDummy, i.uv20).r;
|
||||
z.g = _Shadowmap.Sample(sampler_ShadowmapDummy, i.uv21).r;
|
||||
z.b = _Shadowmap.Sample(sampler_ShadowmapDummy, i.uv22).r;
|
||||
z.a = _Shadowmap.Sample(sampler_ShadowmapDummy, i.uv23).r;
|
||||
|
||||
// The texture contains just 0. But we need to sample it somewhere for Unity to initialize the corresponding sampler.
|
||||
z.r += _ShadowmapDummy.Sample(sampler_ShadowmapDummy, 0).a;
|
||||
|
||||
// Transform to linear z, 0 at near, 1 at far
|
||||
z = z * 2 - 1;
|
||||
z = _ZParams.x * (z + 1.0) / (z + _ZParams.y);
|
||||
|
||||
#if VSM
|
||||
// TODO: this is wrong. We can't average/blur z values before converting to VSM.
|
||||
// This doesn't affect m, but affects m * m, so I should swap those two lines.
|
||||
float m = dot(z, 0.25);
|
||||
return float4(m, m * m, 0, 0);
|
||||
#else
|
||||
z = exp(_ESMExponent * z);
|
||||
return dot(z, 0.25);
|
||||
#endif
|
||||
}
|
||||
|
||||
sampler2D _DirShadowmap;
|
||||
|
||||
float4 fragDownsampleFromShadowmapFormatDir ( v2f_tap i ) : SV_Target
|
||||
{
|
||||
float4 z;
|
||||
z.r = tex2D (_DirShadowmap, i.uv20).r;
|
||||
z.g = tex2D (_DirShadowmap, i.uv21).r;
|
||||
z.b = tex2D (_DirShadowmap, i.uv22).r;
|
||||
z.a = tex2D (_DirShadowmap, i.uv23).r;
|
||||
|
||||
return z.r;
|
||||
|
||||
// Transform to linear z, 0 at near, 1 at far
|
||||
// z = z * 2 - 1;
|
||||
// z = _ZParams.x * (z + 1.0) / (z + _ZParams.y);
|
||||
|
||||
#if 1
|
||||
// float m = dot(z, 0.25);
|
||||
// return float4(m, m * m, 0, 0);
|
||||
float4 z2 = z * z;
|
||||
return float4(dot(z, 0.25), dot(z2, 0.25), 0, 0);
|
||||
#else
|
||||
//z = exp(_ESMExponent * z);
|
||||
z = exp(40.0 * z);
|
||||
return dot(z, 0.25);
|
||||
#endif
|
||||
}
|
||||
|
||||
sampler2D _MainTex;
|
||||
|
||||
float4 fragDownsample ( v2f_tap i ) : SV_Target
|
||||
{
|
||||
float4 color = tex2D (_MainTex, i.uv20);
|
||||
color += tex2D (_MainTex, i.uv21);
|
||||
color += tex2D (_MainTex, i.uv22);
|
||||
color += tex2D (_MainTex, i.uv23);
|
||||
return color * 0.25;
|
||||
}
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 pos : SV_POSITION;
|
||||
half4 uv : TEXCOORD0;
|
||||
half2 offs : TEXCOORD1;
|
||||
};
|
||||
|
||||
float _BlurSize;
|
||||
|
||||
v2f vertBlurHorizontal (appdata_img v)
|
||||
{
|
||||
v2f o;
|
||||
o.pos = v.vertex;
|
||||
|
||||
o.uv = half4(v.texcoord.xy,1,1);
|
||||
o.offs = _TexelSize.xy * half2(1.0, 0.0) * _BlurSize;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
v2f vertBlurVertical (appdata_img v)
|
||||
{
|
||||
v2f o;
|
||||
o.pos = v.vertex;
|
||||
|
||||
o.uv = half4(v.texcoord.xy, 1, 1);
|
||||
o.offs = _TexelSize.xy * half2(0.0, 1.0) * _BlurSize;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
float4 fragBlur8 (v2f i) : SV_Target
|
||||
{
|
||||
half2 coords = i.uv.xy - i.offs * 5.0;
|
||||
|
||||
float4 color = 0;
|
||||
for(int k = 0; k < 11; k++)
|
||||
{
|
||||
color += tex2D(_MainTex, coords);
|
||||
coords += i.offs;
|
||||
}
|
||||
return color/11.0;
|
||||
}
|
||||
|
||||
ENDCG
|
||||
|
||||
SubShader {
|
||||
ZTest Off Cull Off ZWrite Off Blend Off
|
||||
|
||||
// 0
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert4Tap
|
||||
#pragma fragment fragDownsampleFromShadowmapFormat
|
||||
ENDCG
|
||||
}
|
||||
|
||||
// 1
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert4Tap
|
||||
#pragma fragment fragDownsample
|
||||
ENDCG
|
||||
}
|
||||
|
||||
// 2
|
||||
Pass {
|
||||
ZTest Always
|
||||
Cull Off
|
||||
|
||||
CGPROGRAM
|
||||
|
||||
#pragma vertex vertBlurVertical
|
||||
#pragma fragment fragBlur8
|
||||
|
||||
ENDCG
|
||||
}
|
||||
|
||||
// 3
|
||||
Pass {
|
||||
ZTest Always
|
||||
Cull Off
|
||||
|
||||
CGPROGRAM
|
||||
|
||||
#pragma vertex vertBlurHorizontal
|
||||
#pragma fragment fragBlur8
|
||||
|
||||
ENDCG
|
||||
}
|
||||
|
||||
// 4
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert4TapDir
|
||||
#pragma fragment fragDownsampleFromShadowmapFormatDir
|
||||
ENDCG
|
||||
}
|
||||
|
||||
// 5
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma vertex vert4TapDir
|
||||
#pragma fragment fragDownsample
|
||||
ENDCG
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FallBack Off
|
||||
}
|
9
Assets/VolumetricFog/Shaders/BlurShadowmap.shader.meta
Normal file
9
Assets/VolumetricFog/Shaders/BlurShadowmap.shader.meta
Normal file
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 701192b62a7678549918bc87434699fe
|
||||
timeCreated: 1432227463
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
43
Assets/VolumetricFog/Shaders/CopyShadowParams.shader
Normal file
43
Assets/VolumetricFog/Shaders/CopyShadowParams.shader
Normal file
@ -0,0 +1,43 @@
|
||||
Shader "Hidden/CopyShadowParams"
|
||||
{
|
||||
SubShader
|
||||
{
|
||||
Pass
|
||||
{
|
||||
CGPROGRAM
|
||||
#pragma target 5.0
|
||||
#pragma only_renderers d3d11
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
struct ShadowParams
|
||||
{
|
||||
float4x4 worldToShadow[4];
|
||||
float4 shadowSplitSpheres[4];
|
||||
float4 shadowSplitSqRadii;
|
||||
};
|
||||
|
||||
// Hmm, we can't be sure u2 doesn't conflict with other effects.
|
||||
RWStructuredBuffer<ShadowParams> _ShadowParams : register(u2);
|
||||
|
||||
float4 vert () : SV_POSITION
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
_ShadowParams[0].worldToShadow[i] = unity_WorldToShadow[i];
|
||||
_ShadowParams[0].shadowSplitSpheres[i] = unity_ShadowSplitSpheres[i];
|
||||
}
|
||||
_ShadowParams[0].shadowSplitSqRadii = unity_ShadowSplitSqRadii;
|
||||
|
||||
return float4(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
fixed4 frag () : SV_Target
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33fc45db43408764c8dd7647b877e561
|
||||
timeCreated: 1463056552
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
56
Assets/VolumetricFog/Shaders/Debug.shader
Normal file
56
Assets/VolumetricFog/Shaders/Debug.shader
Normal file
@ -0,0 +1,56 @@
|
||||
Shader "Hidden/Debug" {
|
||||
SubShader {
|
||||
Pass {
|
||||
ZTest Always Cull Off ZWrite Off
|
||||
Blend Off
|
||||
|
||||
CGPROGRAM
|
||||
#pragma target 3.0
|
||||
#include "UnityCG.cginc"
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
sampler2D _CameraDepthTexture;
|
||||
sampler3D _VolumeInject;
|
||||
sampler3D _VolumeScatter;
|
||||
sampler2D _Shadowmap;
|
||||
sampler2D _ShadowmapBlurred;
|
||||
sampler2D _MainTex;
|
||||
sampler2D _BoxLightShadowmap;
|
||||
float _Z;
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 pos : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
v2f vert (appdata_img v)
|
||||
{
|
||||
v2f o;
|
||||
o.pos = v.vertex;
|
||||
o.pos.xy = o.pos.xy * 2 - 1;
|
||||
o.uv = v.texcoord.xy;
|
||||
|
||||
#if UNITY_UV_STARTS_AT_TOP
|
||||
if (_ProjectionParams.x < 0)
|
||||
o.uv.y = 1-o.uv.y;
|
||||
#endif
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
half4 frag (v2f i) : SV_Target
|
||||
{
|
||||
half depth = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv));
|
||||
|
||||
// return i.uv.xyyy;
|
||||
return tex2D(_BoxLightShadowmap, i.uv);
|
||||
//return log(tex2D(_ShadowmapBlurred, i.uv))/80.0;
|
||||
return tex3D(_VolumeInject, half3(i.uv.x, i.uv.y, _Z)).a;// * tex2D(_MainTex, float2(i.uv.x, i.uv.y));
|
||||
}
|
||||
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
9
Assets/VolumetricFog/Shaders/Debug.shader.meta
Normal file
9
Assets/VolumetricFog/Shaders/Debug.shader.meta
Normal file
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91e29ad407521e447a7254f5c1ea9b54
|
||||
timeCreated: 1431963567
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
538
Assets/VolumetricFog/Shaders/InjectLightingAndDensity.compute
Normal file
538
Assets/VolumetricFog/Shaders/InjectLightingAndDensity.compute
Normal file
@ -0,0 +1,538 @@
|
||||
#pragma kernel CSMain TUBE_LIGHTS TUBE_LIGHT_SHADOW_PLANES FOG_ELLIPSOIDS ANISOTROPY AREA_LIGHTS POINT_LIGHTS
|
||||
|
||||
// Directional light support not quite ready yet
|
||||
// #pragma kernel CSMain TUBE_LIGHTS TUBE_LIGHT_SHADOW_PLANES FOG_ELLIPSOIDS ANISOTROPY AREA_LIGHTS POINT_LIGHTS DIR_LIGHT
|
||||
|
||||
#define TUBE_LIGHT_ATTENUATION_LEGACY 1
|
||||
#include "..\..\TubeLight\Shaders\TubeLightAttenuation.cginc"
|
||||
|
||||
#ifdef TUBE_LIGHT_SHADOW_PLANES
|
||||
#include "..\..\TubeLight\Shaders\TubeLightShadowPlanes.cginc"
|
||||
#endif
|
||||
|
||||
RWTexture3D<half4> _VolumeInject;
|
||||
Texture2D _Shadowmap;
|
||||
SamplerState sampler_Shadowmap;
|
||||
float4 _FrustumRays[4];
|
||||
float4 _CameraPos;
|
||||
float4 _FrustumRaysLight[4];
|
||||
float4 _CameraPosLight;
|
||||
float _Density;
|
||||
float _Intensity;
|
||||
float _Anisotropy;
|
||||
Texture2D _Noise;
|
||||
SamplerState sampler_Noise;
|
||||
float4 _FogParams;
|
||||
float _NoiseFogAmount;
|
||||
float _NoiseFogScale;
|
||||
float _WindSpeed;
|
||||
float3 _WindDir;
|
||||
float _Time;
|
||||
Texture2D _LightTextureB0;
|
||||
SamplerState sampler_LightTextureB0;
|
||||
float _NearOverFarClip;
|
||||
float3 _AmbientLight;
|
||||
#ifdef FOG_BOMB
|
||||
float _FogBombRadius;
|
||||
float3 _FogBombPos;
|
||||
#endif
|
||||
|
||||
#ifdef DIR_LIGHT
|
||||
float3 _DirLightColor;
|
||||
float3 _DirLightDir;
|
||||
#ifdef DIR_LIGHT_SHADOWS
|
||||
float _DirLightShadows;
|
||||
float _ESMExponentDirLight;
|
||||
struct ShadowParams
|
||||
{
|
||||
float4x4 worldToShadow[4];
|
||||
float4 shadowSplitSpheres[4];
|
||||
float4 shadowSplitSqRadii;
|
||||
};
|
||||
RWStructuredBuffer<ShadowParams> _ShadowParams;
|
||||
Texture2D _DirectionalShadowmap;
|
||||
SamplerState sampler_DirectionalShadowmap;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef POINT_LIGHTS
|
||||
struct PointLight
|
||||
{
|
||||
float3 pos;
|
||||
float range;
|
||||
float3 color;
|
||||
float padding;
|
||||
};
|
||||
StructuredBuffer<PointLight> _PointLights;
|
||||
float _PointLightsCount;
|
||||
#endif
|
||||
|
||||
#ifdef TUBE_LIGHTS
|
||||
struct TubeLight
|
||||
{
|
||||
float3 start;
|
||||
float range;
|
||||
float3 end;
|
||||
float radius;
|
||||
float3 color;
|
||||
float padding;
|
||||
};
|
||||
StructuredBuffer<TubeLight> _TubeLights;
|
||||
float _TubeLightsCount;
|
||||
|
||||
#ifdef TUBE_LIGHT_SHADOW_PLANES
|
||||
// Same count as _TubeLightsCount
|
||||
StructuredBuffer<TubeLightShadowPlane> _TubeLightShadowPlanes;
|
||||
#endif
|
||||
|
||||
#endif // TUBE_LIGHTS
|
||||
|
||||
#ifdef AREA_LIGHTS
|
||||
struct AreaLight
|
||||
{
|
||||
float4x4 mat;
|
||||
float4 pos; // only needed for anisotropy. w: 0 ortho, 1 proj
|
||||
float3 color;
|
||||
float bounded;
|
||||
};
|
||||
StructuredBuffer<AreaLight> _AreaLights;
|
||||
float _AreaLightsCount;
|
||||
Texture2D _AreaLightShadowmap;
|
||||
SamplerState sampler_AreaLightShadowmap;
|
||||
float _ShadowedAreaLightIndex;
|
||||
float4 _AreaLightShadowmapZParams;
|
||||
float _ESMExponentAreaLight;
|
||||
#endif
|
||||
|
||||
#ifdef FOG_ELLIPSOIDS
|
||||
struct FogEllipsoid
|
||||
{
|
||||
float3 pos;
|
||||
float radius;
|
||||
float3 axis;
|
||||
float stretch;
|
||||
float density;
|
||||
float noiseAmount;
|
||||
float noiseSpeed;
|
||||
float noiseScale;
|
||||
float feather;
|
||||
float blend;
|
||||
float padding1;
|
||||
float padding2;
|
||||
};
|
||||
StructuredBuffer<FogEllipsoid> _FogEllipsoids;
|
||||
float _FogEllipsoidsCount;
|
||||
#endif
|
||||
|
||||
float hash( float n ) { return frac(sin(n)*753.5453123); }
|
||||
float noisep(float3 x)
|
||||
{
|
||||
float3 p = floor(x);
|
||||
float3 f = frac(x);
|
||||
f = f*f*(3.0-2.0*f);
|
||||
|
||||
float n = p.x + p.y*157.0 + 113.0*p.z;
|
||||
return lerp(lerp(lerp( hash(n+ 0.0), hash(n+ 1.0),f.x),
|
||||
lerp( hash(n+157.0), hash(n+158.0),f.x),f.y),
|
||||
lerp(lerp( hash(n+113.0), hash(n+114.0),f.x),
|
||||
lerp( hash(n+270.0), hash(n+271.0),f.x),f.y),f.z);
|
||||
}
|
||||
|
||||
float noise(float3 x)
|
||||
{
|
||||
float3 p = floor(x);
|
||||
float3 f = frac(x);
|
||||
f = f * f * (3.0 - 2.0 * f);
|
||||
float2 uv = (p.xy + float2(37.0,17.0) * p.z) + f.xy;
|
||||
float2 rg = _Noise.SampleLevel(sampler_Noise, (uv + 0.5) / 256.0, 0).yx;
|
||||
return -1.0 + 2.0 * lerp(rg.x, rg.y, f.z);
|
||||
}
|
||||
|
||||
float ScrollNoise(float3 pos, float speed, float scale, float3 dir, float amount, float bias = 0.0, float mult = 1.0)
|
||||
{
|
||||
float time = _Time * speed;
|
||||
float noiseScale = scale;
|
||||
float3 noiseScroll = dir * time;
|
||||
float3 q = pos - noiseScroll;
|
||||
q *= scale;
|
||||
float f = 0;
|
||||
f = 0.5 * noisep(q);
|
||||
// scroll the next octave in the opposite direction to get some morphing instead of just scrolling
|
||||
q += noiseScroll * scale;
|
||||
q = q * 2.01;
|
||||
f += 0.25 * noisep(q);
|
||||
|
||||
f += bias;
|
||||
f *= mult;
|
||||
|
||||
f = max(f, 0.0);
|
||||
return lerp(1.0, f, amount);
|
||||
}
|
||||
|
||||
#ifdef FOG_ELLIPSOIDS
|
||||
void FogEllipsoids(float3 pos, inout float density)
|
||||
{
|
||||
for (int i = 0; i < _FogEllipsoidsCount; i++)
|
||||
{
|
||||
float3 dir = _FogEllipsoids[i].pos - pos;
|
||||
float3 axis = _FogEllipsoids[i].axis;
|
||||
float3 dirAlongAxis = dot(dir, axis) * axis;
|
||||
|
||||
float scrollNoise = ScrollNoise(dir, _FogEllipsoids[i].noiseSpeed, _FogEllipsoids[i].noiseScale, axis, _FogEllipsoids[i].noiseAmount);
|
||||
|
||||
dir = dir + dirAlongAxis * _FogEllipsoids[i].stretch;
|
||||
float distsq = dot(dir, dir);
|
||||
float radius = _FogEllipsoids[i].radius;
|
||||
float feather = _FogEllipsoids[i].feather;
|
||||
// float feather = 0.3;
|
||||
feather = (1.0 - smoothstep (radius * feather, radius, distsq));
|
||||
|
||||
float contribution = scrollNoise * feather * _FogEllipsoids[i].density;
|
||||
density = lerp(density + contribution, density * contribution, _FogEllipsoids[i].blend);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FOG_BOMB
|
||||
float Pulse(float c, float w, float x)
|
||||
{
|
||||
return smoothstep(c - w, c, x) - smoothstep(c, c + w, x);
|
||||
}
|
||||
#endif
|
||||
|
||||
float Density(float3 pos)
|
||||
{
|
||||
float fog = _FogParams.x;
|
||||
|
||||
fog += max(exp(_FogParams.y*(-pos.y + _FogParams.z)) * _FogParams.w, 0.0);
|
||||
|
||||
float3 warp = pos;
|
||||
|
||||
#ifdef FOG_BOMB
|
||||
if (_FogBombRadius > 0)
|
||||
{
|
||||
float3 posToBomb = _FogBombPos - pos;
|
||||
float distToBomb = length(posToBomb);
|
||||
fog *= smoothstep (_FogBombRadius * 0.9, _FogBombRadius * 1.1, distToBomb);
|
||||
fog *= 1.0 + 0.5 * Pulse(_FogBombRadius * 1.35, 0.7, distToBomb);
|
||||
warp += (1 - smoothstep(_FogBombRadius, _FogBombRadius * 1.4, distToBomb)) * posToBomb * 0.3;
|
||||
}
|
||||
#endif
|
||||
|
||||
fog *= ScrollNoise(warp, _WindSpeed, _NoiseFogScale, _WindDir, _NoiseFogAmount, -0.3, 8.0);
|
||||
|
||||
#ifdef FOG_ELLIPSOIDS
|
||||
FogEllipsoids(pos, fog);
|
||||
#endif
|
||||
|
||||
return max(fog * _Density, 0.0);
|
||||
}
|
||||
|
||||
float3 FrustumRay(float2 uv, float4 frustumRays[4])
|
||||
{
|
||||
float3 ray0 = lerp(frustumRays[0].xyz, frustumRays[1].xyz, uv.x);
|
||||
float3 ray1 = lerp(frustumRays[3].xyz, frustumRays[2].xyz, uv.x);
|
||||
return lerp(ray0, ray1, uv.y);
|
||||
}
|
||||
|
||||
#ifdef ANISOTROPY
|
||||
float anisotropy(float costheta)
|
||||
{
|
||||
float g = _Anisotropy;
|
||||
float gsq = g*g;
|
||||
float denom = 1 + gsq - 2.0 * g * costheta;
|
||||
denom = denom * denom * denom;
|
||||
denom = sqrt(max(0, denom));
|
||||
return (1 - gsq) / denom;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if AREA_LIGHTS || DIR_LIGHT_SHADOWS
|
||||
#define VSM 1
|
||||
#if VSM
|
||||
float ChebyshevUpperBound(float2 moments, float mean)
|
||||
{
|
||||
// Compute variance
|
||||
float variance = moments.y - (moments.x * moments.x);
|
||||
float _VSMBias = 0.001f;
|
||||
variance = max(variance, _VSMBias * mean * mean);
|
||||
|
||||
// Compute probabilistic upper bound
|
||||
float d = mean - moments.x;
|
||||
float pMax = variance / (variance + (d * d));
|
||||
|
||||
// One-tailed Chebyshev
|
||||
return (mean <= moments.x ? 1.0f : pMax);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if DIR_LIGHT
|
||||
#if DIR_LIGHT_SHADOWS
|
||||
float4 getCascadeWeights_splitSpheres(float3 pos)
|
||||
{
|
||||
float3 fromCenter0 = pos - _ShadowParams[0].shadowSplitSpheres[0].xyz;
|
||||
float3 fromCenter1 = pos - _ShadowParams[0].shadowSplitSpheres[1].xyz;
|
||||
float3 fromCenter2 = pos - _ShadowParams[0].shadowSplitSpheres[2].xyz;
|
||||
float3 fromCenter3 = pos - _ShadowParams[0].shadowSplitSpheres[3].xyz;
|
||||
float4 distances2 = float4(dot(fromCenter0,fromCenter0), dot(fromCenter1,fromCenter1), dot(fromCenter2,fromCenter2), dot(fromCenter3,fromCenter3));
|
||||
float4 weights = float4(distances2 >= _ShadowParams[0].shadowSplitSqRadii);
|
||||
return weights;
|
||||
}
|
||||
|
||||
float4 getShadowCoord(float3 pos, float4 cascadeWeights)
|
||||
{
|
||||
return mul(_ShadowParams[0].worldToShadow[(int)dot(cascadeWeights, float4(1,1,1,1))], float4(pos, 1));
|
||||
}
|
||||
#endif
|
||||
|
||||
float3 DirectionalLight(float3 pos)
|
||||
{
|
||||
if (!any(_DirLightColor))
|
||||
return 0;
|
||||
|
||||
float att = 1;
|
||||
|
||||
#if DIR_LIGHT_SHADOWS
|
||||
if (_DirLightShadows > 0.0)
|
||||
{
|
||||
float4 cascadeWeights = getCascadeWeights_splitSpheres(pos);
|
||||
//bool inside = dot(cascadeWeights, float4(1,1,1,1)) < 4;
|
||||
float3 samplePos = getShadowCoord(pos, cascadeWeights).xyz;
|
||||
//occlusion += inside ? UNITY_SAMPLE_SHADOW(u_CascadedShadowMap, samplePos) : 1.f;
|
||||
#if 1
|
||||
att *= _DirectionalShadowmap.SampleLevel(sampler_DirectionalShadowmap, samplePos.xy, 0).r > samplePos.z;
|
||||
#else
|
||||
float2 shadowmap = _DirectionalShadowmap.SampleLevel(sampler_DirectionalShadowmap, samplePos, 0).xy;
|
||||
att *= ChebyshevUpperBound(shadowmap.xy, samplePos.z);
|
||||
|
||||
// float depth = exp(-40.0 * samplePos.z);
|
||||
// att = saturate(shadowmap.r * depth);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ANISOTROPY
|
||||
float3 posToCamera = normalize(_CameraPos.xyz - pos);
|
||||
float costheta = dot(posToCamera, _DirLightDir);
|
||||
att *= anisotropy(costheta);
|
||||
#endif
|
||||
|
||||
return _DirLightColor * att;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef POINT_LIGHTS
|
||||
float3 PointLights(float3 pos)
|
||||
{
|
||||
float3 color = 0;
|
||||
for (int i = 0; i < _PointLightsCount; i++)
|
||||
{
|
||||
float3 posToLight = _PointLights[i].pos - pos;
|
||||
float distNorm = dot(posToLight, posToLight) * _PointLights[i].range;
|
||||
float att = Attenuation(distNorm);
|
||||
|
||||
#if ANISOTROPY
|
||||
float3 cameraToPos = normalize(pos - _CameraPos.xyz);
|
||||
float costheta = dot(cameraToPos, normalize(posToLight));
|
||||
att *= anisotropy(costheta);
|
||||
#endif
|
||||
|
||||
color += _PointLights[i].color * att;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TUBE_LIGHTS
|
||||
float almostIdentity(float x, float m, float n)
|
||||
{
|
||||
if (x > m)
|
||||
return x;
|
||||
|
||||
float a = 2.0f*n - m;
|
||||
float b = 2.0f*m - 3.0f*n;
|
||||
float t = x/m;
|
||||
|
||||
return (a*t + b)*t*t + n;
|
||||
}
|
||||
|
||||
float3 TubeLights(float3 pos)
|
||||
{
|
||||
float3 color = 0;
|
||||
for (int i = 0; i < _TubeLightsCount; i++)
|
||||
{
|
||||
float3 L0 = _TubeLights[i].start - pos;
|
||||
float3 L1 = _TubeLights[i].end - pos;
|
||||
float distNorm = 0.5f * (length(L0) * length(L1) + dot(L0, L1)) * _TubeLights[i].range;
|
||||
float att = Attenuation(distNorm);
|
||||
|
||||
#if ANISOTROPY
|
||||
// Just like when calculating specular for area lights:
|
||||
// assume forward scattering lobe -> the point on the light that's the closest to
|
||||
// the view direction is representative
|
||||
float3 posToCamera = normalize(pos - _CameraPos.xyz);
|
||||
float3 r = -posToCamera;
|
||||
float3 Ld = L1 - L0;
|
||||
float L0oL0 = dot(L0, L0);
|
||||
float RoL0 = dot(r, L0);
|
||||
float RoLd = dot(r, Ld);
|
||||
float L0oLd = dot(L0, Ld);
|
||||
float LdoLd = dot(Ld, Ld);
|
||||
float distLd = sqrt(LdoLd);
|
||||
|
||||
#if 1
|
||||
// Smallest angle to ray
|
||||
float t = (L0oLd * RoL0 - L0oL0 * RoLd) / (L0oLd * RoLd - LdoLd * RoL0);
|
||||
t = saturate(t);
|
||||
|
||||
// As r becomes parallel to Ld and then points away, t flips from 0 to 1 (or vv) and a discontinuity shows up.
|
||||
// Counteract by detecting that relative angle/position and flip t. The discontinuity in t moves to the back side.
|
||||
float3 L0xLd = cross(L0, Ld);
|
||||
float3 LdxR = cross(Ld, r);
|
||||
float RAtLd = dot(L0xLd, LdxR);
|
||||
|
||||
// RAtLd is negative if R points away from Ld.
|
||||
// TODO: check if lerp below is indeed cheaper.
|
||||
// if (RAtLd < 0)
|
||||
// t = 1 - t;
|
||||
t = lerp(1 - t, t, step(0, RAtLd));
|
||||
|
||||
#else
|
||||
// Original by Karis
|
||||
// Closest distance to ray
|
||||
float t = (RoL0 * RoLd - L0oLd) / (distLd * distLd - RoLd * RoLd);
|
||||
t = saturate(t);
|
||||
|
||||
#endif
|
||||
float3 closestPoint = L0 + Ld * t;
|
||||
float3 centerToRay = dot(closestPoint, r) * r - closestPoint;
|
||||
// closestPoint = closestPoint + centerToRay * saturate(_TubeLights[i].radius / length(centerToRay));
|
||||
float centerToRayNorm = length(centerToRay) / _TubeLights[i].radius;
|
||||
// The last param should in theory be 1
|
||||
centerToRayNorm = almostIdentity(centerToRayNorm, 2, 1.2);
|
||||
closestPoint = closestPoint + centerToRay / centerToRayNorm;
|
||||
|
||||
// Attenuation from the closest point looks really good if there's anisotropy, but breaks
|
||||
// for (close to) isotropic medium. Probably because there's no forward lobe anymore, so
|
||||
// the closest point to the view direction is not representative? But artifacts look like
|
||||
// smth else is going on too.
|
||||
// att = Attenuation(dot(closestPoint, closestPoint) * _TubeLights[i].range);
|
||||
|
||||
float costheta = dot(posToCamera, normalize(closestPoint));
|
||||
att *= anisotropy(costheta);
|
||||
#endif
|
||||
|
||||
#ifdef TUBE_LIGHT_SHADOW_PLANES
|
||||
att *= ShadowPlanes(pos, _TubeLightShadowPlanes[i]);
|
||||
#endif
|
||||
|
||||
// GDC hack
|
||||
att = isnan(att) || isinf(att) ? 0 : att;
|
||||
|
||||
color += _TubeLights[i].color * att;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef AREA_LIGHTS
|
||||
float3 AreaLights(float3 pos)
|
||||
{
|
||||
float3 color = 0;
|
||||
uint count = _AreaLightsCount;
|
||||
uint shadowedAreaLightIndex = _ShadowedAreaLightIndex;
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
float4 pClip = mul(_AreaLights[i].mat, float4(pos, 1));
|
||||
float3 p = float3(pClip.x / pClip.w, pClip.y / pClip.w, pClip.z);
|
||||
float z = p.z * 0.5 + 0.5;
|
||||
|
||||
float att = 1;
|
||||
|
||||
if (_AreaLights[i].bounded)
|
||||
{
|
||||
att *= saturate(AttenuationToZero(z * z));
|
||||
|
||||
// Magic tweaks to the shape
|
||||
float corner = 0.4;
|
||||
float outset = 0.8;
|
||||
float smooth = 0.7;
|
||||
|
||||
float d = length(max(abs(p.xy) - 1 + corner*outset, 0.0)) - corner;
|
||||
att *= saturate(1 - smoothstep(-smooth, 0, d));
|
||||
att *= smoothstep(-0.01, 0.01, z);
|
||||
}
|
||||
|
||||
#if ANISOTROPY
|
||||
float3 cameraToPos = normalize(pos - _CameraPos.xyz);
|
||||
float4 lightPos = _AreaLights[i].pos;
|
||||
float3 posToLight = lerp(lightPos.xyz, lightPos.xyz - pos, lightPos.w);
|
||||
float costheta = dot(cameraToPos, normalize(posToLight));
|
||||
att *= anisotropy(costheta);
|
||||
#endif
|
||||
|
||||
if (i == shadowedAreaLightIndex && all(abs(p) < 1))
|
||||
{
|
||||
#if VSM
|
||||
float2 shadowmap = _AreaLightShadowmap.SampleLevel(sampler_AreaLightShadowmap, p.xy * 0.5 + 0.5, 0).xy;
|
||||
att *= ChebyshevUpperBound(shadowmap.xy, z);
|
||||
#else
|
||||
float shadowmap = _AreaLightShadowmap.SampleLevel(sampler_AreaLightShadowmap, p.xy * 0.5 + 0.5, 0);
|
||||
float depth = exp(-_ESMExponentAreaLight * z);
|
||||
att *= saturate(shadowmap * depth);
|
||||
#endif
|
||||
}
|
||||
|
||||
color += _AreaLights[i].color * att;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
[numthreads(16,2,16)]
|
||||
void CSMain (uint3 id : SV_DispatchThreadID)
|
||||
{
|
||||
float3 color = _AmbientLight;
|
||||
float2 uv = float2(id.x/159.0, id.y/89.0);
|
||||
float z = id.z/127.0;
|
||||
z = _NearOverFarClip + z * (1 - _NearOverFarClip);
|
||||
float3 pos = FrustumRay(uv, _FrustumRays) * z + _CameraPos.xyz;
|
||||
|
||||
|
||||
// Directional light
|
||||
#ifdef DIR_LIGHT
|
||||
color += DirectionalLight(pos);
|
||||
#endif
|
||||
|
||||
|
||||
// Point lights
|
||||
#ifdef POINT_LIGHTS
|
||||
color += PointLights(pos);
|
||||
#endif
|
||||
|
||||
|
||||
// Tube lights
|
||||
#ifdef TUBE_LIGHTS
|
||||
color += TubeLights(pos);
|
||||
#endif
|
||||
|
||||
|
||||
// Area lights
|
||||
#ifdef AREA_LIGHTS
|
||||
color += AreaLights(pos);
|
||||
#endif
|
||||
|
||||
|
||||
// Density
|
||||
float density = Density(pos);
|
||||
|
||||
|
||||
// Output
|
||||
float4 output;
|
||||
output.rgb = _Intensity * density * color;
|
||||
output.a = density;
|
||||
_VolumeInject[id] = output;
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55812fd61bd54104aa23612fc28bfeb8
|
||||
timeCreated: 1431967312
|
||||
licenseType: Pro
|
||||
ComputeShaderImporter:
|
||||
currentBuildTarget: 5
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
38
Assets/VolumetricFog/Shaders/Scatter.compute
Normal file
38
Assets/VolumetricFog/Shaders/Scatter.compute
Normal file
@ -0,0 +1,38 @@
|
||||
// Based on 'Volumetric fog: Unified, compute shader based solution to atmospheric scattering, ACM Siggraph 2014'
|
||||
// https://bartwronski.com/publications/
|
||||
|
||||
#pragma kernel CSMain
|
||||
#define VOLUME_DEPTH 128
|
||||
|
||||
Texture3D _VolumeInject;
|
||||
RWTexture3D<float4> _VolumeScatter;
|
||||
|
||||
float Extinction(float density)
|
||||
{
|
||||
return exp(-density);
|
||||
}
|
||||
|
||||
void WriteOutput(in uint3 pos, in float4 colorAndDensity)
|
||||
{
|
||||
_VolumeScatter[pos] = float4(colorAndDensity.rgb, Extinction(colorAndDensity.a));
|
||||
}
|
||||
|
||||
float4 AccumulateScattering(float4 colorAndDensityFront, float4 colorAndDensityBack)
|
||||
{
|
||||
float3 light = colorAndDensityFront.rgb + saturate(Extinction(colorAndDensityFront.a)) * colorAndDensityBack.rgb;
|
||||
return float4(light.rgb, colorAndDensityFront.a + colorAndDensityBack.a);
|
||||
}
|
||||
|
||||
[numthreads(32, 2, 1)]
|
||||
void CSMain (uint3 id : SV_DispatchThreadID)
|
||||
{
|
||||
float4 currentSlice = _VolumeInject[uint3(id.xy, 0)];
|
||||
WriteOutput(uint3(id.xy, 0), currentSlice);
|
||||
|
||||
for(uint z = 1; z < VOLUME_DEPTH; z++)
|
||||
{
|
||||
float4 nextValue = _VolumeInject[uint3(id.xy, z)];
|
||||
currentSlice = AccumulateScattering(currentSlice, nextValue);
|
||||
WriteOutput(uint3(id.xy, z), currentSlice);
|
||||
}
|
||||
}
|
9
Assets/VolumetricFog/Shaders/Scatter.compute.meta
Normal file
9
Assets/VolumetricFog/Shaders/Scatter.compute.meta
Normal file
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a7c01c2354d03a4e9c31096bf130b6a
|
||||
timeCreated: 1432198206
|
||||
licenseType: Pro
|
||||
ComputeShaderImporter:
|
||||
currentBuildTarget: 5
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
32
Assets/VolumetricFog/Shaders/Shadowmap.shader
Normal file
32
Assets/VolumetricFog/Shaders/Shadowmap.shader
Normal file
@ -0,0 +1,32 @@
|
||||
Shader "Hidden/Shadowmap" {
|
||||
SubShader {
|
||||
Tags { "RenderType"="Opaque" }
|
||||
Pass
|
||||
{
|
||||
Fog { Mode Off }
|
||||
Cull Back
|
||||
CGPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#include "UnityCG.cginc"
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 pos : SV_POSITION;
|
||||
};
|
||||
|
||||
v2f vert (appdata_base v)
|
||||
{
|
||||
v2f o;
|
||||
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
|
||||
return o;
|
||||
}
|
||||
|
||||
float4 frag(v2f i) : COLOR
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
9
Assets/VolumetricFog/Shaders/Shadowmap.shader.meta
Normal file
9
Assets/VolumetricFog/Shaders/Shadowmap.shader.meta
Normal file
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c4cd42b134f7864fa579350e9cf6896
|
||||
timeCreated: 1432028591
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
34
Assets/VolumetricFog/Shaders/VolumetricFog.cginc
Normal file
34
Assets/VolumetricFog/Shaders/VolumetricFog.cginc
Normal file
@ -0,0 +1,34 @@
|
||||
sampler3D _VolumeScatter;
|
||||
float4 _VolumeScatter_TexelSize;
|
||||
float4 _Screen_TexelSize;
|
||||
float _CameraFarOverMaxFar;
|
||||
float _NearOverFarClip;
|
||||
|
||||
int ihash(int n)
|
||||
{
|
||||
n = (n<<13)^n;
|
||||
return (n*(n*n*15731+789221)+1376312589) & 2147483647;
|
||||
}
|
||||
|
||||
float frand(int n)
|
||||
{
|
||||
return ihash(n) / 2147483647.0;
|
||||
}
|
||||
|
||||
float2 cellNoise(int2 p)
|
||||
{
|
||||
int i = p.y*256 + p.x;
|
||||
return float2(frand(i), frand(i + 57)) - 0.5;//*2.0-1.0;
|
||||
}
|
||||
|
||||
half4 Fog(half linear01Depth, half2 screenuv)
|
||||
{
|
||||
half z = linear01Depth * _CameraFarOverMaxFar;
|
||||
z = (z - _NearOverFarClip) / (1 - _NearOverFarClip);
|
||||
if (z < 0.0)
|
||||
return half4(0, 0, 0, 1);
|
||||
|
||||
half3 uvw = half3(screenuv.x, screenuv.y, z);
|
||||
uvw.xy += cellNoise(uvw.xy * _Screen_TexelSize.zw) * _VolumeScatter_TexelSize.xy * 0.8;
|
||||
return tex3D(_VolumeScatter, uvw);
|
||||
}
|
9
Assets/VolumetricFog/Shaders/VolumetricFog.cginc.meta
Normal file
9
Assets/VolumetricFog/Shaders/VolumetricFog.cginc.meta
Normal file
@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5353ac921ca28c4448781247e4f9ec3b
|
||||
timeCreated: 1452690568
|
||||
licenseType: Pro
|
||||
ShaderImporter:
|
||||
defaultTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Assets/VolumetricFog/Shaders/noise.png
Normal file
BIN
Assets/VolumetricFog/Shaders/noise.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 258 KiB |
55
Assets/VolumetricFog/Shaders/noise.png.meta
Normal file
55
Assets/VolumetricFog/Shaders/noise.png.meta
Normal file
@ -0,0 +1,55 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35eb47181d630134c97488476efbda59
|
||||
timeCreated: 1432252542
|
||||
licenseType: Pro
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
serializedVersion: 2
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
linearTexture: 0
|
||||
correctGamma: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: .25
|
||||
normalMapFilter: 0
|
||||
isReadable: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 0
|
||||
cubemapConvolution: 0
|
||||
cubemapConvolutionSteps: 8
|
||||
cubemapConvolutionExponent: 1.5
|
||||
seamlessCubemap: 0
|
||||
textureFormat: -3
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapMode: 0
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
rGBM: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: .5, y: .5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaIsTransparency: 0
|
||||
textureType: 5
|
||||
buildTargetSettings: []
|
||||
spriteSheet:
|
||||
sprites: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user