@ -0,0 +1,16 @@ | |||||
using System.Collections; | |||||
using System.Collections.Generic; | |||||
using UnityEngine; | |||||
public class Boulder : HeavyObject | |||||
{ | |||||
public override void OnWaterStay(WaterController water,float waveHeight) | |||||
{ | |||||
} | |||||
public override void OnWaterExit(WaterController water) | |||||
{ | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: b17436874f6d74be884f5af7ebabda8a | |||||
timeCreated: 1485018292 | |||||
licenseType: Free | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,85 @@ | |||||
using System.Collections; | |||||
using System.Collections.Generic; | |||||
using UnityEngine; | |||||
using BansheeGz.BGSpline.Components; | |||||
using BansheeGz.BGSpline.Curve; | |||||
public class Catapult : MonoBehaviour | |||||
{ | |||||
public BGCurve splineCurve; | |||||
public float speed = 600f; | |||||
public float rotateSpeed = 200f; | |||||
public float timePadding = 0.5f; | |||||
public float timeBetweenLaunches = 0.5f; | |||||
public float minScale = 0.3f; | |||||
public float maxScale = 1f; | |||||
public float randomFactor = 50f; | |||||
public AnimationCurve animationCurve; | |||||
public Transform player; | |||||
private bool isLaunching = false; | |||||
private BGCcCursor splineCursor; | |||||
private BGCcCursorObjectTranslate splineObjectTranslate; | |||||
private Vector3 originalScale; | |||||
private MeshRenderer meshRenderer; | |||||
void Start () | |||||
{ | |||||
splineCursor = splineCurve.GetComponent<BGCcCursor>(); | |||||
splineObjectTranslate = splineCurve.GetComponent<BGCcCursorObjectTranslate>(); | |||||
meshRenderer = splineObjectTranslate.ObjectToManipulate.GetComponent<MeshRenderer>(); | |||||
meshRenderer.enabled = false; | |||||
originalScale = splineCurve.transform.parent.localScale; | |||||
NotificationServer.register("statechange Searchlight", searchlightStateChanged); | |||||
} | |||||
void Update() | |||||
{ | |||||
splineObjectTranslate.ObjectToManipulate.Rotate(Vector3.forward, Time.deltaTime * rotateSpeed); | |||||
} | |||||
public void searchlightStateChanged() | |||||
{ | |||||
LeanTween.cancel(splineObjectTranslate.ObjectToManipulate.parent.gameObject, true); | |||||
if (Searchlight.state == Searchlight.SearchState.Chasing) | |||||
launchBoulder(); | |||||
} | |||||
public void launchBoulder() | |||||
{ | |||||
if (Searchlight.state != Searchlight.SearchState.Chasing || isLaunching) | |||||
{ | |||||
isLaunching = false; | |||||
return; | |||||
} | |||||
isLaunching = true; | |||||
LeanTween.delayedCall(splineObjectTranslate.ObjectToManipulate.parent.gameObject, timeBetweenLaunches, ()=>{ | |||||
if (Searchlight.state != Searchlight.SearchState.Chasing) | |||||
{ | |||||
isLaunching = false; | |||||
return; | |||||
} | |||||
meshRenderer.enabled = true; | |||||
splineCursor.DistanceRatio = 0f; | |||||
splineCurve.transform.parent.position = player.position | |||||
+ Vector3.right * Random.Range(-randomFactor, randomFactor) | |||||
+ Vector3.forward * Random.Range(-randomFactor, randomFactor); | |||||
splineCurve.transform.parent.localEulerAngles = Vector3.up * Random.Range(0f, 360f); | |||||
splineCurve.transform.parent.localScale = new Vector3(Random.Range(minScale * originalScale.x, maxScale * originalScale.x), originalScale.y, originalScale.z); | |||||
LeanTween.value(splineObjectTranslate.ObjectToManipulate.gameObject, 0f, 1f, splineCursor.Math.GetDistance() / speed) | |||||
.setEase(animationCurve) | |||||
.setOnUpdate((float val)=>{ | |||||
splineCursor.DistanceRatio = val; | |||||
}).setOnComplete(()=>{ | |||||
splineCursor.DistanceRatio = 0f; | |||||
isLaunching = false; | |||||
meshRenderer.enabled = false; | |||||
launchBoulder(); | |||||
}); | |||||
}); | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 8d68d4df0b2854bf6bc9e40235372a0f | |||||
timeCreated: 1485013470 | |||||
licenseType: Free | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,52 @@ | |||||
using System.Collections; | |||||
using System.Collections.Generic; | |||||
using UnityEngine; | |||||
using UnityStandardAssets.CinematicEffects; | |||||
public class ScreenEffects : MonoBehaviour | |||||
{ | |||||
public DepthOfField depthOfField; | |||||
private float originalNearRadius; | |||||
private float originalFarRadius; | |||||
void Start () | |||||
{ | |||||
depthOfField = GetComponent<DepthOfField>(); | |||||
if (depthOfField == null) | |||||
depthOfField = gameObject.AddComponent<DepthOfField>(); | |||||
originalNearRadius = depthOfField.focus.nearBlurRadius; | |||||
originalFarRadius = depthOfField.focus.farBlurRadius; | |||||
NotificationServer.register("show GameUI", showGame); | |||||
NotificationServer.register("hide GameUI", hideGame); | |||||
} | |||||
public void showGame() | |||||
{ | |||||
LeanTween.cancel(gameObject, false); | |||||
float startNear = depthOfField.focus.nearBlurRadius; | |||||
float startFar = depthOfField.focus.farBlurRadius; | |||||
DepthOfField.FocusSettings focus = depthOfField.focus; | |||||
LeanTween.value(gameObject, 1f, 0f, 0.5f).setEaseInOutQuad().setOnUpdate((float val)=>{ | |||||
focus.nearBlurRadius = startNear * val; | |||||
focus.farBlurRadius = startFar * val; | |||||
}).setOnComplete(()=>{ | |||||
depthOfField.enabled = false; | |||||
}).setIgnoreTimeScale(true); | |||||
} | |||||
public void hideGame() | |||||
{ | |||||
LeanTween.cancel(gameObject, false); | |||||
depthOfField.enabled = true; | |||||
float startNear = depthOfField.focus.nearBlurRadius; | |||||
float startFar = depthOfField.focus.farBlurRadius; | |||||
DepthOfField.FocusSettings focus = depthOfField.focus; | |||||
LeanTween.value(gameObject, 0f, 1f, 0.5f).setEaseInOutQuad().setOnUpdate((float val)=>{ | |||||
focus.nearBlurRadius = startNear + (originalNearRadius-startNear) * val; | |||||
focus.farBlurRadius = startFar + (originalFarRadius-startFar) * val; | |||||
}).setIgnoreTimeScale(true); | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 0bbf215cb5bca4fa09f6547f0a24f318 | |||||
timeCreated: 1485020054 | |||||
licenseType: Free | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -1,8 +1,15 @@ | |||||
fileFormatVersion: 2 | fileFormatVersion: 2 | ||||
<<<<<<< HEAD | |||||
guid: 0fbabcae9313543ffbf1429aeac3664d | |||||
folderAsset: yes | |||||
timeCreated: 1485006712 | |||||
licenseType: Free | |||||
======= | |||||
guid: 73fb9ee280b0c75439600b4d5c3a6a7d | guid: 73fb9ee280b0c75439600b4d5c3a6a7d | ||||
folderAsset: yes | folderAsset: yes | ||||
timeCreated: 1484996421 | timeCreated: 1484996421 | ||||
licenseType: Pro | licenseType: Pro | ||||
>>>>>>> 4dc44205d41ab697039660061470b5816237ab96 | |||||
DefaultImporter: | DefaultImporter: | ||||
userData: | userData: | ||||
assetBundleName: | assetBundleName: | ||||
@ -1,8 +1,8 @@ | |||||
fileFormatVersion: 2 | fileFormatVersion: 2 | ||||
guid: 1715d56e0d2b68246a82993db31014ab | |||||
guid: ed6d7947a564a4dd1987f60392be4349 | |||||
folderAsset: yes | folderAsset: yes | ||||
timeCreated: 1485006712 | |||||
licenseType: Free | |||||
timeCreated: 1457326591 | |||||
licenseType: Store | |||||
DefaultImporter: | DefaultImporter: | ||||
userData: | userData: | ||||
assetBundleName: | assetBundleName: |
@ -0,0 +1,360 @@ | |||||
using UnityEngine; | |||||
using UnityEngine.Rendering; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
[ExecuteInEditMode] | |||||
[RequireComponent(typeof(Camera))] | |||||
[AddComponentMenu("Image Effects/Cinematic/Ambient Occlusion")] | |||||
#if UNITY_5_4_OR_NEWER | |||||
[ImageEffectAllowedInSceneView] | |||||
#endif | |||||
public partial class AmbientOcclusion : MonoBehaviour | |||||
{ | |||||
#region Public Properties | |||||
/// Effect settings. | |||||
[SerializeField] | |||||
public Settings settings = Settings.defaultSettings; | |||||
/// Checks if the ambient-only mode is supported under the current settings. | |||||
public bool isAmbientOnlySupported | |||||
{ | |||||
get { return targetCamera.hdr && occlusionSource == OcclusionSource.GBuffer; } | |||||
} | |||||
/// Checks if the G-buffer is available | |||||
public bool isGBufferAvailable | |||||
{ | |||||
get { return targetCamera.actualRenderingPath == RenderingPath.DeferredShading; } | |||||
} | |||||
#endregion | |||||
#region Private Properties | |||||
// Properties referring to the current settings | |||||
float intensity | |||||
{ | |||||
get { return settings.intensity; } | |||||
} | |||||
float radius | |||||
{ | |||||
get { return Mathf.Max(settings.radius, 1e-4f); } | |||||
} | |||||
SampleCount sampleCount | |||||
{ | |||||
get { return settings.sampleCount; } | |||||
} | |||||
int sampleCountValue | |||||
{ | |||||
get | |||||
{ | |||||
switch (settings.sampleCount) | |||||
{ | |||||
case SampleCount.Lowest: return 3; | |||||
case SampleCount.Low: return 6; | |||||
case SampleCount.Medium: return 12; | |||||
case SampleCount.High: return 20; | |||||
} | |||||
return Mathf.Clamp(settings.sampleCountValue, 1, 256); | |||||
} | |||||
} | |||||
OcclusionSource occlusionSource | |||||
{ | |||||
get | |||||
{ | |||||
if (settings.occlusionSource == OcclusionSource.GBuffer && !isGBufferAvailable) | |||||
// An unavailable source was chosen: fallback to DepthNormalsTexture. | |||||
return OcclusionSource.DepthNormalsTexture; | |||||
else | |||||
return settings.occlusionSource; | |||||
} | |||||
} | |||||
bool downsampling | |||||
{ | |||||
get { return settings.downsampling; } | |||||
} | |||||
bool ambientOnly | |||||
{ | |||||
get { return settings.ambientOnly && !settings.debug && isAmbientOnlySupported; } | |||||
} | |||||
// Texture format used for storing AO | |||||
RenderTextureFormat aoTextureFormat | |||||
{ | |||||
get | |||||
{ | |||||
if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.R8)) | |||||
return RenderTextureFormat.R8; | |||||
else | |||||
return RenderTextureFormat.Default; | |||||
} | |||||
} | |||||
// AO shader | |||||
Shader aoShader | |||||
{ | |||||
get | |||||
{ | |||||
if (_aoShader == null) | |||||
_aoShader = Shader.Find("Hidden/Image Effects/Cinematic/AmbientOcclusion"); | |||||
return _aoShader; | |||||
} | |||||
} | |||||
[SerializeField] Shader _aoShader; | |||||
// Temporary aterial for the AO shader | |||||
Material aoMaterial | |||||
{ | |||||
get | |||||
{ | |||||
if (_aoMaterial == null) | |||||
_aoMaterial = ImageEffectHelper.CheckShaderAndCreateMaterial(aoShader); | |||||
return _aoMaterial; | |||||
} | |||||
} | |||||
Material _aoMaterial; | |||||
// Command buffer for the AO pass | |||||
CommandBuffer aoCommands | |||||
{ | |||||
get | |||||
{ | |||||
if (_aoCommands == null) | |||||
{ | |||||
_aoCommands = new CommandBuffer(); | |||||
_aoCommands.name = "AmbientOcclusion"; | |||||
} | |||||
return _aoCommands; | |||||
} | |||||
} | |||||
CommandBuffer _aoCommands; | |||||
// Target camera | |||||
Camera targetCamera | |||||
{ | |||||
get { return GetComponent<Camera>(); } | |||||
} | |||||
// Property observer | |||||
PropertyObserver propertyObserver { get; set; } | |||||
// Reference to the quad mesh in the built-in assets | |||||
// (used in MRT blitting) | |||||
Mesh quadMesh | |||||
{ | |||||
get { return _quadMesh; } | |||||
} | |||||
[SerializeField] Mesh _quadMesh; | |||||
#endregion | |||||
#region Effect Passes | |||||
// Build commands for the AO pass (used in the ambient-only mode). | |||||
void BuildAOCommands() | |||||
{ | |||||
var cb = aoCommands; | |||||
var tw = targetCamera.pixelWidth; | |||||
var th = targetCamera.pixelHeight; | |||||
var ts = downsampling ? 2 : 1; | |||||
var format = aoTextureFormat; | |||||
var rwMode = RenderTextureReadWrite.Linear; | |||||
var filter = FilterMode.Bilinear; | |||||
// AO buffer | |||||
var m = aoMaterial; | |||||
var rtMask = Shader.PropertyToID("_OcclusionTexture"); | |||||
cb.GetTemporaryRT(rtMask, tw / ts, th / ts, 0, filter, format, rwMode); | |||||
// AO estimation | |||||
cb.Blit((Texture)null, rtMask, m, 2); | |||||
// Blur buffer | |||||
var rtBlur = Shader.PropertyToID("_OcclusionBlurTexture"); | |||||
// Primary blur filter (large kernel) | |||||
cb.GetTemporaryRT(rtBlur, tw, th, 0, filter, format, rwMode); | |||||
cb.SetGlobalVector("_BlurVector", Vector2.right * 2); | |||||
cb.Blit(rtMask, rtBlur, m, 4); | |||||
cb.ReleaseTemporaryRT(rtMask); | |||||
cb.GetTemporaryRT(rtMask, tw, th, 0, filter, format, rwMode); | |||||
cb.SetGlobalVector("_BlurVector", Vector2.up * 2 * ts); | |||||
cb.Blit(rtBlur, rtMask, m, 4); | |||||
cb.ReleaseTemporaryRT(rtBlur); | |||||
// Secondary blur filter (small kernel) | |||||
cb.GetTemporaryRT(rtBlur, tw, th, 0, filter, format, rwMode); | |||||
cb.SetGlobalVector("_BlurVector", Vector2.right * ts); | |||||
cb.Blit(rtMask, rtBlur, m, 6); | |||||
cb.ReleaseTemporaryRT(rtMask); | |||||
cb.GetTemporaryRT(rtMask, tw, th, 0, filter, format, rwMode); | |||||
cb.SetGlobalVector("_BlurVector", Vector2.up * ts); | |||||
cb.Blit(rtBlur, rtMask, m, 6); | |||||
cb.ReleaseTemporaryRT(rtBlur); | |||||
// Combine AO to the G-buffer. | |||||
var mrt = new RenderTargetIdentifier[] { | |||||
BuiltinRenderTextureType.GBuffer0, // Albedo, Occ | |||||
BuiltinRenderTextureType.CameraTarget // Ambient | |||||
}; | |||||
cb.SetRenderTarget(mrt, BuiltinRenderTextureType.CameraTarget); | |||||
cb.SetGlobalTexture("_OcclusionTexture", rtMask); | |||||
cb.DrawMesh(quadMesh, Matrix4x4.identity, m, 0, 8); | |||||
cb.ReleaseTemporaryRT(rtMask); | |||||
} | |||||
// Execute the AO pass immediately (used in the forward mode). | |||||
void ExecuteAOPass(RenderTexture source, RenderTexture destination) | |||||
{ | |||||
var tw = source.width; | |||||
var th = source.height; | |||||
var ts = downsampling ? 2 : 1; | |||||
var format = aoTextureFormat; | |||||
var rwMode = RenderTextureReadWrite.Linear; | |||||
var useGBuffer = settings.occlusionSource == OcclusionSource.GBuffer; | |||||
// AO buffer | |||||
var m = aoMaterial; | |||||
var rtMask = RenderTexture.GetTemporary(tw / ts, th / ts, 0, format, rwMode); | |||||
// AO estimation | |||||
Graphics.Blit((Texture)null, rtMask, m, (int)occlusionSource); | |||||
// Primary blur filter (large kernel) | |||||
var rtBlur = RenderTexture.GetTemporary(tw, th, 0, format, rwMode); | |||||
m.SetVector("_BlurVector", Vector2.right * 2); | |||||
Graphics.Blit(rtMask, rtBlur, m, useGBuffer ? 4 : 3); | |||||
RenderTexture.ReleaseTemporary(rtMask); | |||||
rtMask = RenderTexture.GetTemporary(tw, th, 0, format, rwMode); | |||||
m.SetVector("_BlurVector", Vector2.up * 2 * ts); | |||||
Graphics.Blit(rtBlur, rtMask, m, useGBuffer ? 4 : 3); | |||||
RenderTexture.ReleaseTemporary(rtBlur); | |||||
// Secondary blur filter (small kernel) | |||||
rtBlur = RenderTexture.GetTemporary(tw, th, 0, format, rwMode); | |||||
m.SetVector("_BlurVector", Vector2.right * ts); | |||||
Graphics.Blit(rtMask, rtBlur, m, useGBuffer ? 6 : 5); | |||||
RenderTexture.ReleaseTemporary(rtMask); | |||||
rtMask = RenderTexture.GetTemporary(tw, th, 0, format, rwMode); | |||||
m.SetVector("_BlurVector", Vector2.up * ts); | |||||
Graphics.Blit(rtBlur, rtMask, m, useGBuffer ? 6 : 5); | |||||
RenderTexture.ReleaseTemporary(rtBlur); | |||||
// Combine AO with the source. | |||||
m.SetTexture("_OcclusionTexture", rtMask); | |||||
if (!settings.debug) | |||||
Graphics.Blit(source, destination, m, 7); | |||||
else | |||||
Graphics.Blit(source, destination, m, 9); | |||||
RenderTexture.ReleaseTemporary(rtMask); | |||||
} | |||||
// Update the common material properties. | |||||
void UpdateMaterialProperties() | |||||
{ | |||||
var m = aoMaterial; | |||||
m.SetFloat("_Intensity", intensity); | |||||
m.SetFloat("_Radius", radius); | |||||
m.SetFloat("_TargetScale", downsampling ? 0.5f : 1); | |||||
m.SetInt("_SampleCount", sampleCountValue); | |||||
} | |||||
#endregion | |||||
#region MonoBehaviour Functions | |||||
void OnEnable() | |||||
{ | |||||
// Check if the shader is supported in the current platform. | |||||
if (!ImageEffectHelper.IsSupported(aoShader, true, false, this)) | |||||
{ | |||||
enabled = false; | |||||
return; | |||||
} | |||||
// Register the command buffer if in the ambient-only mode. | |||||
if (ambientOnly) | |||||
targetCamera.AddCommandBuffer(CameraEvent.BeforeReflections, aoCommands); | |||||
// Enable depth textures which the occlusion source requires. | |||||
if (occlusionSource == OcclusionSource.DepthTexture) | |||||
targetCamera.depthTextureMode |= DepthTextureMode.Depth; | |||||
if (occlusionSource != OcclusionSource.GBuffer) | |||||
targetCamera.depthTextureMode |= DepthTextureMode.DepthNormals; | |||||
} | |||||
void OnDisable() | |||||
{ | |||||
// Destroy all the temporary resources. | |||||
if (_aoMaterial != null) DestroyImmediate(_aoMaterial); | |||||
_aoMaterial = null; | |||||
if (_aoCommands != null) | |||||
targetCamera.RemoveCommandBuffer(CameraEvent.BeforeReflections, _aoCommands); | |||||
_aoCommands = null; | |||||
} | |||||
void Update() | |||||
{ | |||||
if (propertyObserver.CheckNeedsReset(settings, targetCamera)) | |||||
{ | |||||
// Reinitialize all the resources by disabling/enabling itself. | |||||
// This is not very efficient way but just works... | |||||
OnDisable(); | |||||
OnEnable(); | |||||
// Build the command buffer if in the ambient-only mode. | |||||
if (ambientOnly) | |||||
{ | |||||
aoCommands.Clear(); | |||||
BuildAOCommands(); | |||||
} | |||||
propertyObserver.Update(settings, targetCamera); | |||||
} | |||||
// Update the material properties (later used in the AO commands). | |||||
if (ambientOnly) UpdateMaterialProperties(); | |||||
} | |||||
[ImageEffectOpaque] | |||||
void OnRenderImage(RenderTexture source, RenderTexture destination) | |||||
{ | |||||
if (ambientOnly) | |||||
{ | |||||
// Do nothing in the ambient-only mode. | |||||
Graphics.Blit(source, destination); | |||||
} | |||||
else | |||||
{ | |||||
// Execute the AO pass. | |||||
UpdateMaterialProperties(); | |||||
ExecuteAOPass(source, destination); | |||||
} | |||||
} | |||||
#endregion | |||||
} | |||||
} |
@ -0,0 +1,14 @@ | |||||
fileFormatVersion: 2 | |||||
guid: e89654dcf6db746d2a57aeaaa14f5e83 | |||||
timeCreated: 1457327177 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: | |||||
- _aoShader: {fileID: 4800000, guid: 65e203e5acda447acbf9dc1ef78c4a39, type: 3} | |||||
- _quadMesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -1,8 +1,8 @@ | |||||
fileFormatVersion: 2 | fileFormatVersion: 2 | ||||
guid: de5a3adcedb96f64183518ac89622ade | |||||
guid: c1589efc0706e448d9a0af709e2c99cc | |||||
folderAsset: yes | folderAsset: yes | ||||
timeCreated: 1485006712 | |||||
licenseType: Free | |||||
timeCreated: 1457326964 | |||||
licenseType: Store | |||||
DefaultImporter: | DefaultImporter: | ||||
userData: | userData: | ||||
assetBundleName: | assetBundleName: |
@ -0,0 +1,89 @@ | |||||
using UnityEngine; | |||||
using UnityEditor; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
[CanEditMultipleObjects] | |||||
[CustomEditor(typeof(AmbientOcclusion))] | |||||
public class AmbientOcclusionEditor : Editor | |||||
{ | |||||
SerializedProperty _intensity; | |||||
SerializedProperty _radius; | |||||
SerializedProperty _sampleCount; | |||||
SerializedProperty _sampleCountValue; | |||||
SerializedProperty _downsampling; | |||||
SerializedProperty _occlusionSource; | |||||
SerializedProperty _ambientOnly; | |||||
SerializedProperty _debug; | |||||
static GUIContent _textValue = new GUIContent("Value"); | |||||
static string _textNoGBuffer = | |||||
"G-buffer is currently unavailable. " + | |||||
"Change Renderring Path in camera settings to Deferred."; | |||||
static string _textNoAmbientOnly = | |||||
"The ambient-only mode is currently disabled; " + | |||||
"it requires G-buffer source and HDR rendering."; | |||||
static string _textGBufferNote = | |||||
"Forward opaque objects don't go in the G-buffer. " + | |||||
"This may lead to artifacts."; | |||||
void OnEnable() | |||||
{ | |||||
_intensity = serializedObject.FindProperty("settings.intensity"); | |||||
_radius = serializedObject.FindProperty("settings.radius"); | |||||
_sampleCount = serializedObject.FindProperty("settings.sampleCount"); | |||||
_sampleCountValue = serializedObject.FindProperty("settings.sampleCountValue"); | |||||
_downsampling = serializedObject.FindProperty("settings.downsampling"); | |||||
_occlusionSource = serializedObject.FindProperty("settings.occlusionSource"); | |||||
_ambientOnly = serializedObject.FindProperty("settings.ambientOnly"); | |||||
_debug = serializedObject.FindProperty("settings.debug"); | |||||
} | |||||
public override void OnInspectorGUI() | |||||
{ | |||||
var targetInstance = (AmbientOcclusion)target; | |||||
serializedObject.Update(); | |||||
EditorGUILayout.PropertyField(_intensity); | |||||
EditorGUILayout.PropertyField(_radius); | |||||
EditorGUILayout.PropertyField(_sampleCount); | |||||
if (_sampleCount.hasMultipleDifferentValues || | |||||
_sampleCount.enumValueIndex == (int)AmbientOcclusion.SampleCount.Variable) | |||||
{ | |||||
EditorGUI.indentLevel++; | |||||
EditorGUILayout.PropertyField(_sampleCountValue, _textValue); | |||||
EditorGUI.indentLevel--; | |||||
} | |||||
EditorGUILayout.PropertyField(_downsampling); | |||||
EditorGUILayout.PropertyField(_occlusionSource); | |||||
if (!_occlusionSource.hasMultipleDifferentValues && | |||||
_occlusionSource.enumValueIndex == (int)AmbientOcclusion.OcclusionSource.GBuffer) | |||||
{ | |||||
if (!targetInstance.isGBufferAvailable) | |||||
EditorGUILayout.HelpBox(_textNoGBuffer, MessageType.Warning); | |||||
else if (!_ambientOnly.hasMultipleDifferentValues && !_ambientOnly.boolValue) | |||||
EditorGUILayout.HelpBox(_textGBufferNote, MessageType.Info); | |||||
} | |||||
EditorGUILayout.PropertyField(_ambientOnly); | |||||
if (!_ambientOnly.hasMultipleDifferentValues && | |||||
_ambientOnly.boolValue && | |||||
!targetInstance.isAmbientOnlySupported) | |||||
{ | |||||
EditorGUILayout.HelpBox(_textNoAmbientOnly, MessageType.Warning); | |||||
} | |||||
EditorGUILayout.PropertyField(_debug); | |||||
serializedObject.ApplyModifiedProperties(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 05ea8e27ed8e74e67a9220b4f4119e51 | |||||
timeCreated: 1457327141 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -1,8 +1,8 @@ | |||||
fileFormatVersion: 2 | fileFormatVersion: 2 | ||||
guid: 2a6e699820a38a544acfc0015eb679a2 | |||||
guid: 1d775599023574a39befabe47bdfddde | |||||
folderAsset: yes | folderAsset: yes | ||||
timeCreated: 1484972317 | |||||
licenseType: Free | |||||
timeCreated: 1457326936 | |||||
licenseType: Store | |||||
DefaultImporter: | DefaultImporter: | ||||
userData: | userData: | ||||
assetBundleName: | assetBundleName: |
@ -0,0 +1,44 @@ | |||||
using UnityEngine; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
public partial class AmbientOcclusion : MonoBehaviour | |||||
{ | |||||
// Observer class that detects changes on properties | |||||
struct PropertyObserver | |||||
{ | |||||
// AO properties | |||||
bool _downsampling; | |||||
OcclusionSource _occlusionSource; | |||||
bool _ambientOnly; | |||||
bool _debug; | |||||
// Camera properties | |||||
int _pixelWidth; | |||||
int _pixelHeight; | |||||
// Check if it has to reset itself for property changes. | |||||
public bool CheckNeedsReset(Settings setting, Camera camera) | |||||
{ | |||||
return | |||||
_downsampling != setting.downsampling || | |||||
_occlusionSource != setting.occlusionSource || | |||||
_ambientOnly != setting.ambientOnly || | |||||
_debug != setting.debug || | |||||
_pixelWidth != camera.pixelWidth || | |||||
_pixelHeight != camera.pixelHeight; | |||||
} | |||||
// Update the internal state. | |||||
public void Update(Settings setting, Camera camera) | |||||
{ | |||||
_downsampling = setting.downsampling; | |||||
_occlusionSource = setting.occlusionSource; | |||||
_ambientOnly = setting.ambientOnly; | |||||
_debug = setting.debug; | |||||
_pixelWidth = camera.pixelWidth; | |||||
_pixelHeight = camera.pixelHeight; | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 1d9548d9a173a40e4b758ecf6e4fed49 | |||||
timeCreated: 1457326885 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,85 @@ | |||||
using System; | |||||
using UnityEngine; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
public partial class AmbientOcclusion : MonoBehaviour | |||||
{ | |||||
/// Values for Settings.sampleCount, determining the number of sample points. | |||||
public enum SampleCount | |||||
{ | |||||
Lowest, Low, Medium, High, Variable | |||||
} | |||||
/// Values for Settings.occlusionSource, determining the source buffer of occlusion. | |||||
public enum OcclusionSource | |||||
{ | |||||
DepthTexture, DepthNormalsTexture, GBuffer | |||||
} | |||||
/// Class used for storing settings of AmbientOcclusion. | |||||
[Serializable] | |||||
public class Settings | |||||
{ | |||||
/// Degree of darkness produced by the effect. | |||||
[SerializeField, Range(0, 4)] | |||||
[Tooltip("Degree of darkness produced by the effect.")] | |||||
public float intensity; | |||||
/// Radius of sample points, which affects extent of darkened areas. | |||||
[SerializeField] | |||||
[Tooltip("Radius of sample points, which affects extent of darkened areas.")] | |||||
public float radius; | |||||
/// Number of sample points, which affects quality and performance. | |||||
[SerializeField] | |||||
[Tooltip("Number of sample points, which affects quality and performance.")] | |||||
public SampleCount sampleCount; | |||||
/// Determines the sample count when SampleCount.Variable is used. | |||||
[SerializeField] | |||||
[Tooltip("Determines the sample count when SampleCount.Variable is used.")] | |||||
public int sampleCountValue; | |||||
/// Halves the resolution of the effect to increase performance. | |||||
[SerializeField] | |||||
[Tooltip("Halves the resolution of the effect to increase performance.")] | |||||
public bool downsampling; | |||||
/// Enables the ambient-only mode in that the effect only affects | |||||
/// ambient lighting. This mode is only available with G-buffer | |||||
/// source and HDR rendering. | |||||
[SerializeField] | |||||
[Tooltip("If checked, the effect only affects ambient lighting.")] | |||||
public bool ambientOnly; | |||||
/// Source buffer on which the occlusion estimator is based. | |||||
[SerializeField] | |||||
[Tooltip("Source buffer on which the occlusion estimator is based.")] | |||||
public OcclusionSource occlusionSource; | |||||
/// Displays occlusion for debug purpose. | |||||
[SerializeField] | |||||
[Tooltip("Displays occlusion for debug purpose.")] | |||||
public bool debug; | |||||
/// Returns the default settings. | |||||
public static Settings defaultSettings | |||||
{ | |||||
get | |||||
{ | |||||
return new Settings | |||||
{ | |||||
intensity = 1, | |||||
radius = 0.3f, | |||||
sampleCount = SampleCount.Medium, | |||||
sampleCountValue = 24, | |||||
downsampling = false, | |||||
ambientOnly = false, | |||||
occlusionSource = OcclusionSource.DepthNormalsTexture | |||||
}; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: e952a344c72354904a417d27abe6f55e | |||||
timeCreated: 1457331804 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -1,8 +1,8 @@ | |||||
fileFormatVersion: 2 | fileFormatVersion: 2 | ||||
guid: 338533d5d159c00478a9fb5321b7d922 | |||||
guid: ae08100d29090452888e1b6a7b5a7170 | |||||
folderAsset: yes | folderAsset: yes | ||||
timeCreated: 1485007190 | |||||
licenseType: Free | |||||
timeCreated: 1457326958 | |||||
licenseType: Store | |||||
DefaultImporter: | DefaultImporter: | ||||
userData: | userData: | ||||
assetBundleName: | assetBundleName: |
@ -0,0 +1,409 @@ | |||||
// Upgrade NOTE: commented out 'float4x4 _WorldToCamera', a built-in variable | |||||
// Upgrade NOTE: replaced '_WorldToCamera' with 'unity_WorldToCamera' | |||||
// -------- | |||||
// Additional options for further customization | |||||
// -------- | |||||
// By default, a fixed sampling pattern is used in the AO estimator. | |||||
// Although this gives preferable results in most cases, a completely | |||||
// random sampling pattern could give aesthetically good results in some | |||||
// cases. Comment out the line below to use the random pattern instead of | |||||
// the fixed one. | |||||
#define FIXED_SAMPLING_PATTERN 1 | |||||
// The constant below determines the contrast of occlusion. Altough this | |||||
// allows intentional over/under occlusion, currently is not exposed to the | |||||
// editor, because it’s thought to be rarely useful. | |||||
static const float kContrast = 0.6; | |||||
// The constant below controls the geometry-awareness of the blur filter. | |||||
// The higher value, the more sensitive it is. | |||||
static const float kGeom = 50; | |||||
// The constants below are used in the AO estimator. Beta is mainly used | |||||
// for suppressing self-shadowing noise, and Epsilon is used to prevent | |||||
// calculation underflow. See the paper (Morgan 2011 http://goo.gl/2iz3P) | |||||
// for further details of these constants. | |||||
static const float kBeta = 0.002; | |||||
static const float kEpsilon = 1e-4; | |||||
// -------- | |||||
#include "UnityCG.cginc" | |||||
// Global shader properties | |||||
sampler2D _CameraGBufferTexture2; | |||||
sampler2D_float _CameraDepthTexture; | |||||
sampler2D _CameraDepthNormalsTexture; | |||||
// float4x4 _WorldToCamera; | |||||
// Sample count | |||||
// Use a constant on GLES2 (basically it doesn't support dynamic looping). | |||||
#if SHADER_API_GLES | |||||
static const int _SampleCount = 5; | |||||
#else | |||||
int _SampleCount; | |||||
#endif | |||||
sampler2D _MainTex; | |||||
float4 _MainTex_TexelSize; | |||||
sampler2D _OcclusionTexture; | |||||
// Material shader properties | |||||
half _Intensity; | |||||
float _Radius; | |||||
float _TargetScale; | |||||
float2 _BlurVector; | |||||
// Utility for sin/cos | |||||
float2 CosSin(float theta) | |||||
{ | |||||
float sn, cs; | |||||
sincos(theta, sn, cs); | |||||
return float2(cs, sn); | |||||
} | |||||
// Gamma encoding function for AO value | |||||
// (do nothing if in the linear mode) | |||||
half EncodeAO(half x) | |||||
{ | |||||
// Gamma encoding | |||||
half x_g = 1 - pow(1 - x, 1 / 2.2); | |||||
// ColorSpaceLuminance.w is 0 (gamma) or 1 (linear). | |||||
return lerp(x_g, x, unity_ColorSpaceLuminance.w); | |||||
} | |||||
// Pseudo random number generator with 2D argument | |||||
float UVRandom(float u, float v) | |||||
{ | |||||
float f = dot(float2(12.9898, 78.233), float2(u, v)); | |||||
return frac(43758.5453 * sin(f)); | |||||
} | |||||
// Interleaved gradient function from Jimenez 2014 http://goo.gl/eomGso | |||||
float GradientNoise(float2 uv) | |||||
{ | |||||
uv = floor(uv * _ScreenParams.xy); | |||||
float f = dot(float2(0.06711056f, 0.00583715f), uv); | |||||
return frac(52.9829189f * frac(f)); | |||||
} | |||||
// Boundary check for depth sampler | |||||
// (returns a very large value if it lies out of bounds) | |||||
float CheckBounds(float2 uv, float d) | |||||
{ | |||||
float ob = any(uv < 0) + any(uv > 1); | |||||
#if defined(UNITY_REVERSED_Z) | |||||
ob += (d <= 0.00001); | |||||
#else | |||||
ob += (d >= 0.99999); | |||||
#endif | |||||
return ob * 1e8; | |||||
} | |||||
// Depth/normal sampling functions | |||||
float SampleDepth(float2 uv) | |||||
{ | |||||
#if SOURCE_GBUFFER || SOURCE_DEPTH | |||||
float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv); | |||||
return LinearEyeDepth(d) + CheckBounds(uv, d); | |||||
#else | |||||
float4 cdn = tex2D(_CameraDepthNormalsTexture, uv); | |||||
float d = DecodeFloatRG(cdn.zw); | |||||
return d * _ProjectionParams.z + CheckBounds(uv, d); | |||||
#endif | |||||
} | |||||
float3 SampleNormal(float2 uv) | |||||
{ | |||||
#if SOURCE_GBUFFER | |||||
float3 norm = tex2D(_CameraGBufferTexture2, uv).xyz; | |||||
norm = norm * 2 - any(norm); // gets (0,0,0) when norm == 0 | |||||
return mul((float3x3)unity_WorldToCamera, norm); | |||||
#else | |||||
float4 cdn = tex2D(_CameraDepthNormalsTexture, uv); | |||||
return DecodeViewNormalStereo(cdn) * float3(1, 1, -1); | |||||
#endif | |||||
} | |||||
float SampleDepthNormal(float2 uv, out float3 normal) | |||||
{ | |||||
#if SOURCE_GBUFFER || SOURCE_DEPTH | |||||
normal = SampleNormal(uv); | |||||
return SampleDepth(uv); | |||||
#else | |||||
float4 cdn = tex2D(_CameraDepthNormalsTexture, uv); | |||||
normal = DecodeViewNormalStereo(cdn) * float3(1, 1, -1); | |||||
float d = DecodeFloatRG(cdn.zw); | |||||
return d * _ProjectionParams.z + CheckBounds(uv, d); | |||||
#endif | |||||
} | |||||
// Reconstruct view-space position from UV and depth. | |||||
// p11_22 = (unity_CameraProjection._11, unity_CameraProjection._22) | |||||
// p13_31 = (unity_CameraProjection._13, unity_CameraProjection._23) | |||||
float3 ReconstructViewPos(float2 uv, float depth, float2 p11_22, float2 p13_31) | |||||
{ | |||||
return float3((uv * 2 - 1 - p13_31) / p11_22, 1) * depth; | |||||
} | |||||
// Normal vector comparer (for geometry-aware weighting) | |||||
half CompareNormal(half3 d1, half3 d2) | |||||
{ | |||||
return pow((dot(d1, d2) + 1) * 0.5, kGeom); | |||||
} | |||||
// Final combiner function | |||||
half3 CombineOcclusion(half3 src, half3 ao) | |||||
{ | |||||
return lerp(src, 0, EncodeAO(ao)); | |||||
} | |||||
// Sample point picker | |||||
float3 PickSamplePoint(float2 uv, float index) | |||||
{ | |||||
// Uniformaly distributed points on a unit sphere http://goo.gl/X2F1Ho | |||||
#if FIXED_SAMPLING_PATTERN | |||||
float gn = GradientNoise(uv * _TargetScale); | |||||
float u = frac(UVRandom(0, index) + gn) * 2 - 1; | |||||
float theta = (UVRandom(1, index) + gn) * UNITY_PI * 2; | |||||
#else | |||||
float u = UVRandom(uv.x + _Time.x, uv.y + index) * 2 - 1; | |||||
float theta = UVRandom(-uv.x - _Time.x, uv.y + index) * UNITY_PI * 2; | |||||
#endif | |||||
float3 v = float3(CosSin(theta) * sqrt(1 - u * u), u); | |||||
// Make them distributed between [0, _Radius] | |||||
float l = sqrt((index + 1) / _SampleCount) * _Radius; | |||||
return v * l; | |||||
} | |||||
// Occlusion estimator function | |||||
float EstimateOcclusion(float2 uv) | |||||
{ | |||||
// Parameters used in coordinate conversion | |||||
float3x3 proj = (float3x3)unity_CameraProjection; | |||||
float2 p11_22 = float2(unity_CameraProjection._11, unity_CameraProjection._22); | |||||
float2 p13_31 = float2(unity_CameraProjection._13, unity_CameraProjection._23); | |||||
// View space normal and depth | |||||
float3 norm_o; | |||||
float depth_o = SampleDepthNormal(uv, norm_o); | |||||
#if SOURCE_DEPTHNORMALS | |||||
// Offset the depth value to avoid precision error. | |||||
// (depth in the DepthNormals mode has only 16-bit precision) | |||||
depth_o -= _ProjectionParams.z / 65536; | |||||
#endif | |||||
// Reconstruct the view-space position. | |||||
float3 vpos_o = ReconstructViewPos(uv, depth_o, p11_22, p13_31); | |||||
// Distance-based AO estimator based on Morgan 2011 http://goo.gl/2iz3P | |||||
float ao = 0.0; | |||||
for (int s = 0; s < _SampleCount; s++) | |||||
{ | |||||
// Sample point | |||||
#if SHADER_API_D3D11 | |||||
// This 'floor(1.0001 * s)' operation is needed to avoid a NVidia | |||||
// shader issue. This issue is only observed on DX11. | |||||
float3 v_s1 = PickSamplePoint(uv, floor(1.0001 * s)); | |||||
#else | |||||
float3 v_s1 = PickSamplePoint(uv, s); | |||||
#endif | |||||
v_s1 = faceforward(v_s1, -norm_o, v_s1); | |||||
float3 vpos_s1 = vpos_o + v_s1; | |||||
// Reproject the sample point | |||||
float3 spos_s1 = mul(proj, vpos_s1); | |||||
float2 uv_s1 = (spos_s1.xy / vpos_s1.z + 1) * 0.5; | |||||
// Depth at the sample point | |||||
float depth_s1 = SampleDepth(uv_s1); | |||||
// Relative position of the sample point | |||||
float3 vpos_s2 = ReconstructViewPos(uv_s1, depth_s1, p11_22, p13_31); | |||||
float3 v_s2 = vpos_s2 - vpos_o; | |||||
// Estimate the obscurance value | |||||
float a1 = max(dot(v_s2, norm_o) - kBeta * depth_o, 0); | |||||
float a2 = dot(v_s2, v_s2) + kEpsilon; | |||||
ao += a1 / a2; | |||||
} | |||||
ao *= _Radius; // intensity normalization | |||||
// Apply other parameters. | |||||
return pow(ao * _Intensity / _SampleCount, kContrast); | |||||
} | |||||
// Geometry-aware separable blur filter (large kernel) | |||||
half SeparableBlurLarge(sampler2D tex, float2 uv, float2 delta) | |||||
{ | |||||
#if !SHADER_API_MOBILE | |||||
// 9-tap Gaussian blur with adaptive sampling | |||||
float2 uv1a = uv - delta; | |||||
float2 uv1b = uv + delta; | |||||
float2 uv2a = uv - delta * 2; | |||||
float2 uv2b = uv + delta * 2; | |||||
float2 uv3a = uv - delta * 3.2307692308; | |||||
float2 uv3b = uv + delta * 3.2307692308; | |||||
half3 n0 = SampleNormal(uv); | |||||
half w0 = 0.37004405286; | |||||
half w1a = CompareNormal(n0, SampleNormal(uv1a)) * 0.31718061674; | |||||
half w1b = CompareNormal(n0, SampleNormal(uv1b)) * 0.31718061674; | |||||
half w2a = CompareNormal(n0, SampleNormal(uv2a)) * 0.19823788546; | |||||
half w2b = CompareNormal(n0, SampleNormal(uv2b)) * 0.19823788546; | |||||
half w3a = CompareNormal(n0, SampleNormal(uv3a)) * 0.11453744493; | |||||
half w3b = CompareNormal(n0, SampleNormal(uv3b)) * 0.11453744493; | |||||
half s = tex2D(_MainTex, uv).r * w0; | |||||
s += tex2D(_MainTex, uv1a).r * w1a; | |||||
s += tex2D(_MainTex, uv1b).r * w1b; | |||||
s += tex2D(_MainTex, uv2a).r * w2a; | |||||
s += tex2D(_MainTex, uv2b).r * w2b; | |||||
s += tex2D(_MainTex, uv3a).r * w3a; | |||||
s += tex2D(_MainTex, uv3b).r * w3b; | |||||
return s / (w0 + w1a + w1b + w2a + w2b + w3a + w3b); | |||||
#else | |||||
// 9-tap Gaussian blur with linear sampling | |||||
// (less quality but slightly fast) | |||||
float2 uv1a = uv - delta * 1.3846153846; | |||||
float2 uv1b = uv + delta * 1.3846153846; | |||||
float2 uv2a = uv - delta * 3.2307692308; | |||||
float2 uv2b = uv + delta * 3.2307692308; | |||||
half3 n0 = SampleNormal(uv); | |||||
half w0 = 0.2270270270; | |||||
half w1a = CompareNormal(n0, SampleNormal(uv1a)) * 0.3162162162; | |||||
half w1b = CompareNormal(n0, SampleNormal(uv1b)) * 0.3162162162; | |||||
half w2a = CompareNormal(n0, SampleNormal(uv2a)) * 0.0702702703; | |||||
half w2b = CompareNormal(n0, SampleNormal(uv2b)) * 0.0702702703; | |||||
half s = tex2D(_MainTex, uv).r * w0; | |||||
s += tex2D(_MainTex, uv1a).r * w1a; | |||||
s += tex2D(_MainTex, uv1b).r * w1b; | |||||
s += tex2D(_MainTex, uv2a).r * w2a; | |||||
s += tex2D(_MainTex, uv2b).r * w2b; | |||||
return s / (w0 + w1a + w1b + w2a + w2b); | |||||
#endif | |||||
} | |||||
// Geometry-aware separable blur filter (small kernel) | |||||
half SeparableBlurSmall(sampler2D tex, float2 uv, float2 delta) | |||||
{ | |||||
float2 uv1 = uv - delta; | |||||
float2 uv2 = uv + delta; | |||||
half3 n0 = SampleNormal(uv); | |||||
half w0 = 2; | |||||
half w1 = CompareNormal(n0, SampleNormal(uv1)); | |||||
half w2 = CompareNormal(n0, SampleNormal(uv2)); | |||||
half s = tex2D(_MainTex, uv).r * w0; | |||||
s += tex2D(_MainTex, uv1).r * w1; | |||||
s += tex2D(_MainTex, uv2).r * w2; | |||||
return s / (w0 + w1 + w2); | |||||
} | |||||
// Occlusion estimation pass | |||||
half4 frag_ao(v2f_img i) : SV_Target | |||||
{ | |||||
return EstimateOcclusion(i.uv); | |||||
} | |||||
// Noise reduction pass (large kernel) | |||||
half4 frag_blur1(v2f_img i) : SV_Target | |||||
{ | |||||
float2 delta = _MainTex_TexelSize.xy * _BlurVector; | |||||
return SeparableBlurLarge(_MainTex, i.uv, delta); | |||||
} | |||||
// Noise reduction pass (small kernel) | |||||
half4 frag_blur2(v2f_img i) : SV_Target | |||||
{ | |||||
float2 delta = _MainTex_TexelSize.xy * _BlurVector; | |||||
return SeparableBlurSmall(_MainTex, i.uv, delta); | |||||
} | |||||
// Combiner pass for the forward mode | |||||
struct v2f_multitex | |||||
{ | |||||
float4 pos : SV_POSITION; | |||||
float2 uv0 : TEXCOORD0; | |||||
float2 uv1 : TEXCOORD1; | |||||
}; | |||||
v2f_multitex vert_multitex(appdata_img v) | |||||
{ | |||||
// Handles vertically-flipped case. | |||||
float vflip = sign(_MainTex_TexelSize.y); | |||||
v2f_multitex o; | |||||
o.pos = mul(UNITY_MATRIX_MVP, v.vertex); | |||||
o.uv0 = v.texcoord.xy; | |||||
o.uv1 = (v.texcoord.xy - 0.5) * float2(1, vflip) + 0.5; | |||||
return o; | |||||
} | |||||
half4 frag_combine(v2f_multitex i) : SV_Target | |||||
{ | |||||
half4 src = tex2D(_MainTex, i.uv0); | |||||
half ao = tex2D(_OcclusionTexture, i.uv1).r; | |||||
return half4(CombineOcclusion(src.rgb, ao), src.a); | |||||
} | |||||
// Combiner pass for the ambient-only mode | |||||
v2f_img vert_gbuffer(appdata_img v) | |||||
{ | |||||
v2f_img o; | |||||
o.pos = v.vertex * float4(2, 2, 0, 0) + float4(0, 0, 0, 1); | |||||
#if UNITY_UV_STARTS_AT_TOP | |||||
o.uv = v.texcoord * float2(1, -1) + float2(0, 1); | |||||
#else | |||||
o.uv = v.texcoord; | |||||
#endif | |||||
return o; | |||||
} | |||||
#if !SHADER_API_GLES // excluding the MRT pass under GLES2 | |||||
struct CombinerOutput | |||||
{ | |||||
half4 gbuffer0 : SV_Target0; | |||||
half4 gbuffer3 : SV_Target1; | |||||
}; | |||||
CombinerOutput frag_gbuffer_combine(v2f_img i) | |||||
{ | |||||
half ao = tex2D(_OcclusionTexture, i.uv).r; | |||||
CombinerOutput o; | |||||
o.gbuffer0 = half4(0, 0, 0, ao); | |||||
o.gbuffer3 = half4((half3)EncodeAO(ao), 0); | |||||
return o; | |||||
} | |||||
#else | |||||
fixed4 frag_gbuffer_combine(v2f_img i) : SV_Target0 | |||||
{ | |||||
return 0; | |||||
} | |||||
#endif | |||||
// Debug blit | |||||
half4 frag_blit_ao(v2f_multitex i) : SV_Target | |||||
{ | |||||
half4 src = tex2D(_MainTex, i.uv0); | |||||
half ao = tex2D(_OcclusionTexture, i.uv1).r; | |||||
return half4(CombineOcclusion(1, ao), src.a); | |||||
} |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 7bbc8f354c22e447dbd9deb502530d91 | |||||
timeCreated: 1463553768 | |||||
licenseType: Store | |||||
ShaderImporter: | |||||
defaultTextures: [] | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,129 @@ | |||||
Shader "Hidden/Image Effects/Cinematic/AmbientOcclusion" | |||||
{ | |||||
Properties | |||||
{ | |||||
_MainTex("", 2D) = ""{} | |||||
_OcclusionTexture("", 2D) = ""{} | |||||
} | |||||
SubShader | |||||
{ | |||||
// 0: Occlusion estimation with CameraDepthTexture | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#define SOURCE_DEPTH 1 | |||||
#include "AmbientOcclusion.cginc" | |||||
#pragma vertex vert_img | |||||
#pragma fragment frag_ao | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 1: Occlusion estimation with CameraDepthNormalsTexture | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#define SOURCE_DEPTHNORMALS 1 | |||||
#include "AmbientOcclusion.cginc" | |||||
#pragma vertex vert_img | |||||
#pragma fragment frag_ao | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 2: Occlusion estimation with G-Buffer | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#define SOURCE_GBUFFER 1 | |||||
#include "AmbientOcclusion.cginc" | |||||
#pragma vertex vert_img | |||||
#pragma fragment frag_ao | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 3: Noise reduction (first pass) with CameraDepthNormalsTexture | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#define SOURCE_DEPTHNORMALS 1 | |||||
#include "AmbientOcclusion.cginc" | |||||
#pragma vertex vert_img | |||||
#pragma fragment frag_blur1 | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 4: Noise reduction (first pass) with G Buffer | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#define SOURCE_GBUFFER 1 | |||||
#include "AmbientOcclusion.cginc" | |||||
#pragma vertex vert_img | |||||
#pragma fragment frag_blur1 | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 5: Noise reduction (second pass) with CameraDepthNormalsTexture | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#define SOURCE_DEPTHNORMALS 1 | |||||
#include "AmbientOcclusion.cginc" | |||||
#pragma vertex vert_img | |||||
#pragma fragment frag_blur2 | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 6: Noise reduction (second pass) with G Buffer | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#define SOURCE_GBUFFER 1 | |||||
#include "AmbientOcclusion.cginc" | |||||
#pragma vertex vert_img | |||||
#pragma fragment frag_blur2 | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 7: Occlusion combiner | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#include "AmbientOcclusion.cginc" | |||||
#pragma vertex vert_multitex | |||||
#pragma fragment frag_combine | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 8: Occlusion combiner for the ambient-only mode | |||||
Pass | |||||
{ | |||||
Blend Zero OneMinusSrcColor, Zero OneMinusSrcAlpha | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#include "AmbientOcclusion.cginc" | |||||
#pragma vertex vert_gbuffer | |||||
#pragma fragment frag_gbuffer_combine | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 9: Debug blit | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#include "AmbientOcclusion.cginc" | |||||
#pragma vertex vert_multitex | |||||
#pragma fragment frag_blit_ao | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 65e203e5acda447acbf9dc1ef78c4a39 | |||||
timeCreated: 1457327141 | |||||
licenseType: Store | |||||
ShaderImporter: | |||||
defaultTextures: [] | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 4b15f777774297b4f91455d3353a0c40 | |||||
folderAsset: yes | |||||
timeCreated: 1454589502 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,90 @@ | |||||
using UnityEngine; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
[ExecuteInEditMode] | |||||
[RequireComponent(typeof(Camera))] | |||||
[AddComponentMenu("Image Effects/Anti-aliasing")] | |||||
#if UNITY_5_4_OR_NEWER | |||||
[ImageEffectAllowedInSceneView] | |||||
#endif | |||||
public class AntiAliasing : MonoBehaviour | |||||
{ | |||||
public enum Method | |||||
{ | |||||
Smaa, | |||||
Fxaa | |||||
} | |||||
[SerializeField] | |||||
private SMAA m_SMAA = new SMAA(); | |||||
[SerializeField] | |||||
private FXAA m_FXAA = new FXAA(); | |||||
[SerializeField, HideInInspector] | |||||
private int m_Method = (int)Method.Smaa; | |||||
public int method | |||||
{ | |||||
get { return m_Method; } | |||||
set | |||||
{ | |||||
if (m_Method == value) | |||||
return; | |||||
m_Method = value; | |||||
} | |||||
} | |||||
public IAntiAliasing current | |||||
{ | |||||
get | |||||
{ | |||||
if (method == (int)Method.Smaa) | |||||
return m_SMAA; | |||||
else | |||||
return m_FXAA; | |||||
} | |||||
} | |||||
private Camera m_Camera; | |||||
public Camera cameraComponent | |||||
{ | |||||
get | |||||
{ | |||||
if (m_Camera == null) | |||||
m_Camera = GetComponent<Camera>(); | |||||
return m_Camera; | |||||
} | |||||
} | |||||
private void OnEnable() | |||||
{ | |||||
m_SMAA.OnEnable(this); | |||||
m_FXAA.OnEnable(this); | |||||
} | |||||
private void OnDisable() | |||||
{ | |||||
m_SMAA.OnDisable(); | |||||
m_FXAA.OnDisable(); | |||||
} | |||||
private void OnPreCull() | |||||
{ | |||||
current.OnPreCull(cameraComponent); | |||||
} | |||||
private void OnPostRender() | |||||
{ | |||||
current.OnPostRender(cameraComponent); | |||||
} | |||||
private void OnRenderImage(RenderTexture source, RenderTexture destination) | |||||
{ | |||||
current.OnRenderImage(cameraComponent, source, destination); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: fdc35e0180670ab4e8d2f9439137791f | |||||
timeCreated: 1454589503 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 738f82ccf57b5974cb672f8032c72169 | |||||
folderAsset: yes | |||||
timeCreated: 1454595975 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,64 @@ | |||||
using System; | |||||
using UnityEditor; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
[CustomEditor(typeof(AntiAliasing))] | |||||
public class AntiAliasingEditor : Editor | |||||
{ | |||||
private string[] methodNames = | |||||
{ | |||||
"Subpixel Morphological Anti-aliasing", | |||||
"Fast Approximate Anti-aliasing" | |||||
}; | |||||
private int m_SelectedMethod; | |||||
private SMAAEditor m_SMAAEditor = new SMAAEditor(); | |||||
private FXAAEditor m_FXAAEditor = new FXAAEditor(); | |||||
IAntiAliasingEditor m_AntiAliasingEditor; | |||||
private void OnEnable() | |||||
{ | |||||
m_SMAAEditor.OnEnable(serializedObject, "m_SMAA"); | |||||
m_FXAAEditor.OnEnable(serializedObject, "m_FXAA"); | |||||
} | |||||
public override void OnInspectorGUI() | |||||
{ | |||||
var antiAliasingTarget = (AntiAliasing)target; | |||||
m_SelectedMethod = antiAliasingTarget.method; | |||||
EditorGUI.BeginChangeCheck(); | |||||
m_SelectedMethod = EditorGUILayout.Popup("Method", m_SelectedMethod, methodNames); | |||||
bool dirty = false; | |||||
if (EditorGUI.EndChangeCheck()) | |||||
{ | |||||
if (m_SelectedMethod < 0) | |||||
m_SelectedMethod = 0; | |||||
else if (m_SelectedMethod > 1) | |||||
m_SelectedMethod = 1; | |||||
antiAliasingTarget.method = m_SelectedMethod; | |||||
dirty = true; | |||||
} | |||||
if (m_SelectedMethod == 0) | |||||
m_AntiAliasingEditor = m_SMAAEditor; | |||||
else | |||||
m_AntiAliasingEditor = m_FXAAEditor; | |||||
dirty |= m_AntiAliasingEditor.OnInspectorGUI(antiAliasingTarget.current); | |||||
if (dirty) | |||||
{ | |||||
EditorUtility.SetDirty(antiAliasingTarget); | |||||
serializedObject.ApplyModifiedProperties(); | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 6bd9375ab74a65448b556b0452e8c6af | |||||
timeCreated: 1454593885 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,10 @@ | |||||
using UnityEditor; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
public interface IAntiAliasingEditor | |||||
{ | |||||
void OnEnable(SerializedObject serializedObject, string path); | |||||
bool OnInspectorGUI(IAntiAliasing target); | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 015ee83e537e9e4438f403e2149c69ae | |||||
timeCreated: 1454595240 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 7629f5693f26f34448aa9c713d257e26 | |||||
folderAsset: yes | |||||
timeCreated: 1453733554 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 880813d23951c624d9d5c3e6d2a4e93c | |||||
folderAsset: yes | |||||
timeCreated: 1454331861 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,57 @@ | |||||
using UnityEditor; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
public class FXAAEditor : IAntiAliasingEditor | |||||
{ | |||||
private string[] presetNames = | |||||
{ | |||||
"Extreme performance", | |||||
"Performance", | |||||
"Default", | |||||
"Quality", | |||||
"Extreme quality" | |||||
}; | |||||
public void OnEnable(SerializedObject serializedObject, string path) | |||||
{ | |||||
} | |||||
public bool OnInspectorGUI(IAntiAliasing target) | |||||
{ | |||||
var fxaaTarget = (FXAA)target; | |||||
if (!fxaaTarget.validSourceFormat) | |||||
EditorGUILayout.HelpBox("FXAA should be used at the end of the post-processing stack after conversion to LDR (after Tonemapping) to maximize quality and avoid artifacts.", MessageType.Warning); | |||||
int selectedPreset = 2; | |||||
if (fxaaTarget.preset.Equals(FXAA.Preset.extremePerformancePreset)) | |||||
selectedPreset = 0; | |||||
else if (fxaaTarget.preset.Equals(FXAA.Preset.performancePreset)) | |||||
selectedPreset = 1; | |||||
else if (fxaaTarget.preset.Equals(FXAA.Preset.defaultPreset)) | |||||
selectedPreset = 2; | |||||
else if (fxaaTarget.preset.Equals(FXAA.Preset.qualityPreset)) | |||||
selectedPreset = 3; | |||||
else if (fxaaTarget.preset.Equals(FXAA.Preset.extremeQualityPreset)) | |||||
selectedPreset = 4; | |||||
EditorGUI.BeginChangeCheck(); | |||||
selectedPreset = EditorGUILayout.Popup("Preset", selectedPreset, presetNames); | |||||
if (EditorGUI.EndChangeCheck()) | |||||
{ | |||||
if (selectedPreset < 0) | |||||
selectedPreset = 0; | |||||
else if (selectedPreset > 4) | |||||
selectedPreset = 4; | |||||
fxaaTarget.preset = FXAA.availablePresets[selectedPreset]; | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 60bfb637c85e3e04ea76962349fee327 | |||||
timeCreated: 1454331861 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,255 @@ | |||||
using UnityEngine; | |||||
using System; | |||||
using Object = UnityEngine.Object; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
[Serializable] | |||||
public class FXAA : IAntiAliasing | |||||
{ | |||||
private Shader m_Shader; | |||||
private Shader shader | |||||
{ | |||||
get | |||||
{ | |||||
if (m_Shader == null) | |||||
m_Shader = Shader.Find("Hidden/Fast Approximate Anti-aliasing"); | |||||
return m_Shader; | |||||
} | |||||
} | |||||
private Material m_Material; | |||||
public Material material | |||||
{ | |||||
get | |||||
{ | |||||
if (m_Material == null) | |||||
m_Material = ImageEffectHelper.CheckShaderAndCreateMaterial(shader); | |||||
return m_Material; | |||||
} | |||||
} | |||||
[Serializable] | |||||
public struct QualitySettings | |||||
{ | |||||
[Tooltip("The amount of desired sub-pixel aliasing removal. Effects the sharpeness of the output.")] | |||||
[Range(0.0f, 1.0f)] | |||||
public float subpixelAliasingRemovalAmount; | |||||
[Tooltip("The minimum amount of local contrast required to qualify a region as containing an edge.")] | |||||
[Range(0.063f, 0.333f)] | |||||
public float edgeDetectionThreshold; | |||||
[Tooltip("Local contrast adaptation value to disallow the algorithm from executing on the darker regions.")] | |||||
[Range(0.0f, 0.0833f)] | |||||
public float minimumRequiredLuminance; | |||||
} | |||||
[Serializable] | |||||
public struct ConsoleSettings | |||||
{ | |||||
[Tooltip("The amount of spread applied to the sampling coordinates while sampling for subpixel information.")] | |||||
[Range(0.33f, 0.5f)] | |||||
public float subpixelSpreadAmount; | |||||
[Tooltip("This value dictates how sharp the edges in the image are kept; a higher value implies sharper edges.")] | |||||
[Range(2.0f, 8.0f)] | |||||
public float edgeSharpnessAmount; | |||||
[Tooltip("The minimum amount of local contrast required to qualify a region as containing an edge.")] | |||||
[Range(0.125f, 0.25f)] | |||||
public float edgeDetectionThreshold; | |||||
[Tooltip("Local contrast adaptation value to disallow the algorithm from executing on the darker regions.")] | |||||
[Range(0.04f, 0.06f)] | |||||
public float minimumRequiredLuminance; | |||||
} | |||||
[Serializable] | |||||
public struct Preset | |||||
{ | |||||
[AttributeUsage(AttributeTargets.Field)] | |||||
public class LayoutAttribute : PropertyAttribute | |||||
{} | |||||
[Layout] | |||||
public QualitySettings qualitySettings; | |||||
[Layout] | |||||
public ConsoleSettings consoleSettings; | |||||
private static readonly Preset s_ExtremePerformance = new Preset | |||||
{ | |||||
qualitySettings = new QualitySettings | |||||
{ | |||||
subpixelAliasingRemovalAmount = 0.0f, | |||||
edgeDetectionThreshold = 0.333f, | |||||
minimumRequiredLuminance = 0.0833f | |||||
}, | |||||
consoleSettings = new ConsoleSettings | |||||
{ | |||||
subpixelSpreadAmount = 0.33f, | |||||
edgeSharpnessAmount = 8.0f, | |||||
edgeDetectionThreshold = 0.25f, | |||||
minimumRequiredLuminance = 0.06f | |||||
} | |||||
}; | |||||
private static readonly Preset s_Performance = new Preset | |||||
{ | |||||
qualitySettings = new QualitySettings | |||||
{ | |||||
subpixelAliasingRemovalAmount = 0.25f, | |||||
edgeDetectionThreshold = 0.25f, | |||||
minimumRequiredLuminance = 0.0833f | |||||
}, | |||||
consoleSettings = new ConsoleSettings | |||||
{ | |||||
subpixelSpreadAmount = 0.33f, | |||||
edgeSharpnessAmount = 8.0f, | |||||
edgeDetectionThreshold = 0.125f, | |||||
minimumRequiredLuminance = 0.06f | |||||
} | |||||
}; | |||||
private static readonly Preset s_Default = new Preset | |||||
{ | |||||
qualitySettings = new QualitySettings | |||||
{ | |||||
subpixelAliasingRemovalAmount = 0.75f, | |||||
edgeDetectionThreshold = 0.166f, | |||||
minimumRequiredLuminance = 0.0833f | |||||
}, | |||||
consoleSettings = new ConsoleSettings | |||||
{ | |||||
subpixelSpreadAmount = 0.5f, | |||||
edgeSharpnessAmount = 8.0f, | |||||
edgeDetectionThreshold = 0.125f, | |||||
minimumRequiredLuminance = 0.05f | |||||
} | |||||
}; | |||||
private static readonly Preset s_Quality = new Preset | |||||
{ | |||||
qualitySettings = new QualitySettings | |||||
{ | |||||
subpixelAliasingRemovalAmount = 1.0f, | |||||
edgeDetectionThreshold = 0.125f, | |||||
minimumRequiredLuminance = 0.0625f | |||||
}, | |||||
consoleSettings = new ConsoleSettings | |||||
{ | |||||
subpixelSpreadAmount = 0.5f, | |||||
edgeSharpnessAmount = 4.0f, | |||||
edgeDetectionThreshold = 0.125f, | |||||
minimumRequiredLuminance = 0.04f | |||||
} | |||||
}; | |||||
private static readonly Preset s_ExtremeQuality = new Preset | |||||
{ | |||||
qualitySettings = new QualitySettings | |||||
{ | |||||
subpixelAliasingRemovalAmount = 1.0f, | |||||
edgeDetectionThreshold = 0.063f, | |||||
minimumRequiredLuminance = 0.0312f | |||||
}, | |||||
consoleSettings = new ConsoleSettings | |||||
{ | |||||
subpixelSpreadAmount = 0.5f, | |||||
edgeSharpnessAmount = 2.0f, | |||||
edgeDetectionThreshold = 0.125f, | |||||
minimumRequiredLuminance = 0.04f | |||||
} | |||||
}; | |||||
public static Preset extremePerformancePreset | |||||
{ | |||||
get { return s_ExtremePerformance; } | |||||
} | |||||
public static Preset performancePreset | |||||
{ | |||||
get { return s_Performance; } | |||||
} | |||||
public static Preset defaultPreset | |||||
{ | |||||
get { return s_Default; } | |||||
} | |||||
public static Preset qualityPreset | |||||
{ | |||||
get { return s_Quality; } | |||||
} | |||||
public static Preset extremeQualityPreset | |||||
{ | |||||
get { return s_ExtremeQuality; } | |||||
} | |||||
} | |||||
[SerializeField, HideInInspector] | |||||
public Preset preset = Preset.defaultPreset; | |||||
public static Preset[] availablePresets = | |||||
{ | |||||
Preset.extremePerformancePreset, | |||||
Preset.performancePreset, | |||||
Preset.defaultPreset, | |||||
Preset.qualityPreset, | |||||
Preset.extremeQualityPreset | |||||
}; | |||||
public bool validSourceFormat { get; private set; } | |||||
public void OnEnable(AntiAliasing owner) | |||||
{ | |||||
if (!ImageEffectHelper.IsSupported(shader, true, false, owner)) | |||||
owner.enabled = false; | |||||
} | |||||
public void OnDisable() | |||||
{ | |||||
if (m_Material != null) | |||||
Object.DestroyImmediate(m_Material); | |||||
} | |||||
public void OnPreCull(Camera camera) | |||||
{ | |||||
} | |||||
public void OnPostRender(Camera camera) | |||||
{ | |||||
} | |||||
public void OnRenderImage(Camera camera, RenderTexture source, RenderTexture destination) | |||||
{ | |||||
#if UNITY_EDITOR | |||||
validSourceFormat = true; | |||||
if (source.format == RenderTextureFormat.ARGBHalf | |||||
|| source.format == RenderTextureFormat.ARGBFloat | |||||
|| source.format == RenderTextureFormat.ARGB2101010) | |||||
validSourceFormat = false; | |||||
#endif | |||||
material.SetVector("_QualitySettings", new Vector3(preset.qualitySettings.subpixelAliasingRemovalAmount, | |||||
preset.qualitySettings.edgeDetectionThreshold, preset.qualitySettings.minimumRequiredLuminance)); | |||||
material.SetVector("_ConsoleSettings", new Vector4(preset.consoleSettings.subpixelSpreadAmount, | |||||
preset.consoleSettings.edgeSharpnessAmount, preset.consoleSettings.edgeDetectionThreshold, | |||||
preset.consoleSettings.minimumRequiredLuminance)); | |||||
Graphics.Blit(source, destination, material, 0); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,13 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 8ca0fe85db4ef594fb0771b250c00e23 | |||||
timeCreated: 1453738651 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: | |||||
- shader: {fileID: 4800000, guid: 3eaaee164ee0fed4d9a0bbe8434805a6, type: 3} | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 3f7cc4a9005f5f846957997471c28f2b | |||||
folderAsset: yes | |||||
timeCreated: 1455022968 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,104 @@ | |||||
Shader "Hidden/Fast Approximate Anti-aliasing" | |||||
{ | |||||
Properties | |||||
{ | |||||
_MainTex ("Texture", 2D) = "white" {} | |||||
} | |||||
CGINCLUDE | |||||
#pragma fragmentoption ARB_precision_hint_fastest | |||||
#if defined(SHADER_API_PS3) | |||||
#define FXAA_PS3 1 | |||||
// Shaves off 2 cycles from the shader | |||||
#define FXAA_EARLY_EXIT 0 | |||||
#elif defined(SHADER_API_XBOX360) | |||||
#define FXAA_360 1 | |||||
// Shaves off 10ms from the shader's execution time | |||||
#define FXAA_EARLY_EXIT 1 | |||||
#else | |||||
#define FXAA_PC 1 | |||||
#endif | |||||
#define FXAA_HLSL_3 1 | |||||
#define FXAA_QUALITY__PRESET 39 | |||||
#define FXAA_GREEN_AS_LUMA 1 | |||||
#pragma target 3.0 | |||||
#include "FXAA3.cginc" | |||||
float4 _MainTex_TexelSize; | |||||
float3 _QualitySettings; | |||||
float4 _ConsoleSettings; | |||||
struct Input | |||||
{ | |||||
float4 position : POSITION; | |||||
float2 uv : TEXCOORD0; | |||||
}; | |||||
struct Varying | |||||
{ | |||||
float4 position : SV_POSITION; | |||||
float2 uv : TEXCOORD0; | |||||
}; | |||||
Varying vertex(Input input) | |||||
{ | |||||
Varying output; | |||||
output.position = mul(UNITY_MATRIX_MVP, input.position); | |||||
output.uv = input.uv; | |||||
return output; | |||||
} | |||||
sampler2D _MainTex; | |||||
float calculateLuma(float4 color) | |||||
{ | |||||
return color.g * 1.963211 + color.r; | |||||
} | |||||
fixed4 fragment(Varying input) : SV_Target | |||||
{ | |||||
const float4 consoleUV = input.uv.xyxy + .5 * float4(-_MainTex_TexelSize.xy, _MainTex_TexelSize.xy); | |||||
const float4 consoleSubpixelFrame = _ConsoleSettings.x * float4(-1., -1., 1., 1.) * | |||||
_MainTex_TexelSize.xyxy; | |||||
const float4 consoleSubpixelFramePS3 = float4(-2., -2., 2., 2.) * _MainTex_TexelSize.xyxy; | |||||
const float4 consoleSubpixelFrameXBOX = float4(8., 8., -4., -4.) * _MainTex_TexelSize.xyxy; | |||||
#if defined(SHADER_API_XBOX360) | |||||
const float4 consoleConstants = float4(1., -1., .25, -.25); | |||||
#else | |||||
const float4 consoleConstants = float4(0., 0., 0., 0.); | |||||
#endif | |||||
return FxaaPixelShader(input.uv, consoleUV, _MainTex, _MainTex, _MainTex, _MainTex_TexelSize.xy, | |||||
consoleSubpixelFrame, consoleSubpixelFramePS3, consoleSubpixelFrameXBOX, | |||||
_QualitySettings.x, _QualitySettings.y, _QualitySettings.z, _ConsoleSettings.y, _ConsoleSettings.z, | |||||
_ConsoleSettings.w, consoleConstants); | |||||
} | |||||
ENDCG | |||||
SubShader | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
Fog { Mode off } | |||||
Pass | |||||
{ | |||||
CGPROGRAM | |||||
#pragma vertex vertex | |||||
#pragma fragment fragment | |||||
#include "UnityCG.cginc" | |||||
ENDCG | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 3eaaee164ee0fed4d9a0bbe8434805a6 | |||||
timeCreated: 1453736553 | |||||
licenseType: Store | |||||
ShaderImporter: | |||||
defaultTextures: [] | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: b44d2ca11a7157e4db9f1e02f5249f95 | |||||
timeCreated: 1453990603 | |||||
licenseType: Store | |||||
ShaderImporter: | |||||
defaultTextures: [] | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,13 @@ | |||||
using UnityEngine; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
public interface IAntiAliasing | |||||
{ | |||||
void OnEnable(AntiAliasing owner); | |||||
void OnDisable(); | |||||
void OnPreCull(Camera camera); | |||||
void OnPostRender(Camera camera); | |||||
void OnRenderImage(Camera camera, RenderTexture source, RenderTexture destination); | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 90329fa7c7a616243a47de84e6e5c041 | |||||
timeCreated: 1454590083 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: d958f8498dd28db459dc41b661331fc8 | |||||
folderAsset: yes | |||||
timeCreated: 1446717353 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 3ab9eab0d04085b4abd47b6b0657143c | |||||
folderAsset: yes | |||||
timeCreated: 1430588699 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,106 @@ | |||||
using UnityEditor; | |||||
using System.Collections.Generic; | |||||
using System; | |||||
using System.Linq; | |||||
using UnityEngine; | |||||
using System.Reflection; | |||||
using UnityEngine.Serialization; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
public class SMAAEditor : IAntiAliasingEditor | |||||
{ | |||||
private List<SerializedProperty> m_TopLevelFields = new List<SerializedProperty>(); | |||||
[Serializable] | |||||
class InfoMap | |||||
{ | |||||
public string name; | |||||
public bool experimental; | |||||
public bool quality; | |||||
public List<SerializedProperty> properties; | |||||
} | |||||
private List<InfoMap> m_GroupFields = new List<InfoMap>(); | |||||
public void OnEnable(SerializedObject serializedObject, string path) | |||||
{ | |||||
var topLevelSettings = typeof(SMAA).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => x.GetCustomAttributes(typeof(SMAA.TopLevelSettings), false).Any()); | |||||
var settingsGroups = typeof(SMAA).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => x.GetCustomAttributes(typeof(SMAA.SettingsGroup), false).Any()); | |||||
foreach (var group in topLevelSettings) | |||||
{ | |||||
var searchPath = path + "." + group.Name + "."; | |||||
foreach (var setting in group.FieldType.GetFields(BindingFlags.Instance | BindingFlags.Public)) | |||||
{ | |||||
var property = serializedObject.FindProperty(searchPath + setting.Name); | |||||
if (property != null) | |||||
m_TopLevelFields.Add(property); | |||||
} | |||||
} | |||||
foreach (var group in settingsGroups) | |||||
{ | |||||
var searchPath = path + "." + group.Name + "."; | |||||
foreach (var setting in group.FieldType.GetFields(BindingFlags.Instance | BindingFlags.Public)) | |||||
{ | |||||
var infoGroup = m_GroupFields.FirstOrDefault(x => x.name == group.Name); | |||||
if (infoGroup == null) | |||||
{ | |||||
infoGroup = new InfoMap(); | |||||
infoGroup.properties = new List<SerializedProperty>(); | |||||
infoGroup.name = group.Name; | |||||
infoGroup.quality = group.FieldType == typeof(SMAA.QualitySettings); | |||||
infoGroup.experimental = group.GetCustomAttributes(typeof(SMAA.ExperimentalGroup), false).Length > 0; | |||||
m_GroupFields.Add(infoGroup); | |||||
} | |||||
var property = serializedObject.FindProperty(searchPath + setting.Name); | |||||
if (property != null) | |||||
{ | |||||
infoGroup.properties.Add(property); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
public bool OnInspectorGUI(IAntiAliasing target) | |||||
{ | |||||
EditorGUI.BeginChangeCheck(); | |||||
foreach (var setting in m_TopLevelFields) | |||||
EditorGUILayout.PropertyField(setting); | |||||
foreach (var group in m_GroupFields) | |||||
{ | |||||
if (group.quality && (target as SMAA).settings.quality != SMAA.QualityPreset.Custom) | |||||
{ | |||||
continue; | |||||
} | |||||
string title = ObjectNames.NicifyVariableName(group.name); | |||||
if (group.experimental) | |||||
title += " (Experimental)"; | |||||
EditorGUILayout.Space(); | |||||
EditorGUILayout.LabelField(title, EditorStyles.boldLabel); | |||||
EditorGUI.indentLevel++; | |||||
var enabledField = group.properties.FirstOrDefault(x => x.propertyPath == "m_SMAA." + group.name + ".enabled"); | |||||
if (enabledField != null && !enabledField.boolValue) | |||||
{ | |||||
EditorGUILayout.PropertyField(enabledField); | |||||
EditorGUI.indentLevel--; | |||||
continue; | |||||
} | |||||
foreach (var field in group.properties) | |||||
EditorGUILayout.PropertyField(field); | |||||
EditorGUI.indentLevel--; | |||||
} | |||||
return EditorGUI.EndChangeCheck(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 5c2ffc06b5a6ee64d8e1d9bdf074732c | |||||
timeCreated: 1430643832 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 9362798305520c448b294dd314a7daff | |||||
folderAsset: yes | |||||
timeCreated: 1430505545 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,57 @@ | |||||
fileFormatVersion: 2 | |||||
guid: e62aa7035ea66c94b90b2d8774d02cca | |||||
timeCreated: 1432601000 | |||||
licenseType: Store | |||||
TextureImporter: | |||||
fileIDToRecycleName: {} | |||||
serializedVersion: 2 | |||||
mipmaps: | |||||
mipMapMode: 0 | |||||
enableMipMap: 0 | |||||
linearTexture: 1 | |||||
correctGamma: 0 | |||||
fadeOut: 0 | |||||
borderMipMap: 0 | |||||
mipMapFadeDistanceStart: 1 | |||||
mipMapFadeDistanceEnd: 3 | |||||
bumpmap: | |||||
convertToNormalMap: 0 | |||||
externalNormalMap: 0 | |||||
heightScale: 0.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: 0 | |||||
mipBias: -1 | |||||
wrapMode: 1 | |||||
nPOTScale: 0 | |||||
lightmap: 0 | |||||
rGBM: 2 | |||||
compressionQuality: 50 | |||||
allowsAlphaSplitting: 0 | |||||
spriteMode: 0 | |||||
spriteExtrude: 1 | |||||
spriteMeshType: 1 | |||||
alignment: 0 | |||||
spritePivot: {x: 0.5, y: 0.5} | |||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0} | |||||
spritePixelsToUnits: 100 | |||||
alphaIsTransparency: 0 | |||||
textureType: 5 | |||||
buildTargetSettings: [] | |||||
spriteSheet: | |||||
sprites: [] | |||||
outline: [] | |||||
spritePackingTag: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 74597699f47d8ae458dca68f79f1b21f | |||||
timeCreated: 1430504573 | |||||
licenseType: Store | |||||
ShaderImporter: | |||||
defaultTextures: [] | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,382 @@ | |||||
Shader "Hidden/Subpixel Morphological Anti-aliasing" | |||||
{ | |||||
Properties | |||||
{ | |||||
_MainTex ("Base (RGB)", 2D) = "white" {} | |||||
} | |||||
CGINCLUDE | |||||
#pragma fragmentoption ARB_precision_hint_fastest | |||||
#pragma target 3.0 | |||||
#pragma glsl | |||||
#pragma exclude_renderers flash | |||||
sampler2D _MainTex; | |||||
sampler2D _BlendTex; | |||||
sampler2D _AreaTex; | |||||
sampler2D _SearchTex; | |||||
sampler2D _AccumulationTex; | |||||
sampler2D _CameraDepthTexture; | |||||
float4 _MainTex_TexelSize; | |||||
float4 _Metrics; // 1f / width, 1f / height, width, height | |||||
float4 _Params1; // SMAA_THRESHOLD, SMAA_DEPTH_THRESHOLD, SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG | |||||
float2 _Params2; // SMAA_CORNER_ROUNDING, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR | |||||
float3 _Params3; // SMAA_PREDICATION_THRESHOLD, SMAA_PREDICATION_SCALE, SMAA_PREDICATION_STRENGTH | |||||
float4x4 _ReprojectionMatrix; | |||||
float4 _SubsampleIndices; | |||||
#define SMAA_RT_METRICS _Metrics | |||||
#define SMAA_THRESHOLD _Params1.x | |||||
#define SMAA_DEPTH_THRESHOLD _Params1.y | |||||
#define SMAA_MAX_SEARCH_STEPS _Params1.z | |||||
#define SMAA_MAX_SEARCH_STEPS_DIAG _Params1.w | |||||
#define SMAA_CORNER_ROUNDING _Params2.x | |||||
#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR _Params2.y | |||||
#define SMAA_PREDICATION_THRESHOLD _Params3.x | |||||
#define SMAA_PREDICATION_SCALE _Params3.y | |||||
#define SMAA_PREDICATION_STRENGTH _Params3.z | |||||
// Can't use SMAA_HLSL_3 as it won't compile with OpenGL, so lets make our own set of defines for Unity | |||||
#define SMAA_CUSTOM_SL | |||||
#define mad(a, b, c) (a * b + c) | |||||
#define SMAATexture2D(tex) sampler2D tex | |||||
#define SMAATexturePass2D(tex) tex | |||||
#define SMAASampleLevelZero(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) | |||||
#define SMAASampleLevelZeroPoint(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) | |||||
#define SMAASampleLevelZeroOffset(tex, coord, offset) tex2Dlod(tex, float4(coord + offset * SMAA_RT_METRICS.xy, 0.0, 0.0)) | |||||
#define SMAASample(tex, coord) tex2D(tex, coord) | |||||
#define SMAASamplePoint(tex, coord) tex2D(tex, coord) | |||||
#define SMAASampleOffset(tex, coord, offset) tex2D(tex, coord + offset * SMAA_RT_METRICS.xy) | |||||
#define SMAA_FLATTEN UNITY_FLATTEN | |||||
#define SMAA_BRANCH UNITY_BRANCH | |||||
// SMAA_CUSTOM_SL | |||||
#define SMAA_AREATEX_SELECT(sample) sample.rg | |||||
#define SMAA_SEARCHTEX_SELECT(sample) sample.a | |||||
#define SMAA_INCLUDE_VS 0 | |||||
struct vInput | |||||
{ | |||||
float4 pos : POSITION; | |||||
float2 uv : TEXCOORD0; | |||||
}; | |||||
struct fInput_edge | |||||
{ | |||||
float4 pos : SV_POSITION; | |||||
float2 uv : TEXCOORD0; | |||||
float4 offset[3] : TEXCOORD1; | |||||
}; | |||||
fInput_edge vert_edge(vInput i) | |||||
{ | |||||
fInput_edge o; | |||||
o.pos = mul(UNITY_MATRIX_MVP, i.pos); | |||||
o.uv = i.uv; | |||||
#if UNITY_UV_STARTS_AT_TOP | |||||
if (_MainTex_TexelSize.y < 0) | |||||
o.uv.y = 1.0 - i.uv.y; | |||||
#endif | |||||
o.offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, -1.0), o.uv.xyxy); | |||||
o.offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, 1.0), o.uv.xyxy); | |||||
o.offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, -2.0), o.uv.xyxy); | |||||
return o; | |||||
} | |||||
ENDCG | |||||
SubShader | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
Fog { Mode off } | |||||
// (0) Clear | |||||
Pass | |||||
{ | |||||
CGPROGRAM | |||||
#pragma vertex vert_img | |||||
#pragma fragment frag | |||||
#include "UnityCG.cginc" | |||||
float4 frag(v2f_img i) : SV_Target | |||||
{ | |||||
return float4(0.0, 0.0, 0.0, 0.0); | |||||
} | |||||
ENDCG | |||||
} | |||||
// ----------------------------------------------------------------------------- | |||||
// Edge Detection | |||||
// (1) Luma | |||||
Pass | |||||
{ | |||||
// TODO: Stencil not working | |||||
// Stencil | |||||
// { | |||||
// Pass replace | |||||
// Ref 1 | |||||
// } | |||||
CGPROGRAM | |||||
#pragma vertex vert_edge | |||||
#pragma fragment frag | |||||
#pragma multi_compile __ USE_PREDICATION | |||||
#if USE_PREDICATION | |||||
#define SMAA_PREDICATION 1 | |||||
#else | |||||
#define SMAA_PREDICATION 0 | |||||
#endif | |||||
#include "UnityCG.cginc" | |||||
#include "SMAA.cginc" | |||||
float4 frag(fInput_edge i) : SV_Target | |||||
{ | |||||
#if SMAA_PREDICATION | |||||
return float4(SMAALumaEdgeDetectionPS(i.uv, i.offset, _MainTex, _CameraDepthTexture), 0.0, 0.0); | |||||
#else | |||||
return float4(SMAALumaEdgeDetectionPS(i.uv, i.offset, _MainTex), 0.0, 0.0); | |||||
#endif | |||||
} | |||||
ENDCG | |||||
} | |||||
// (2) Color | |||||
Pass | |||||
{ | |||||
// TODO: Stencil not working | |||||
// Stencil | |||||
// { | |||||
// Pass replace | |||||
// Ref 1 | |||||
// } | |||||
CGPROGRAM | |||||
#pragma vertex vert_edge | |||||
#pragma fragment frag | |||||
#pragma multi_compile __ USE_PREDICATION | |||||
#if USE_PREDICATION | |||||
#define SMAA_PREDICATION 1 | |||||
#else | |||||
#define SMAA_PREDICATION 0 | |||||
#endif | |||||
#include "UnityCG.cginc" | |||||
#include "SMAA.cginc" | |||||
float4 frag(fInput_edge i) : SV_Target | |||||
{ | |||||
#if SMAA_PREDICATION | |||||
return float4(SMAAColorEdgeDetectionPS(i.uv, i.offset, _MainTex, _CameraDepthTexture), 0.0, 0.0); | |||||
#else | |||||
return float4(SMAAColorEdgeDetectionPS(i.uv, i.offset, _MainTex), 0.0, 0.0); | |||||
#endif | |||||
} | |||||
ENDCG | |||||
} | |||||
// (3) Depth | |||||
Pass | |||||
{ | |||||
// TODO: Stencil not working | |||||
// Stencil | |||||
// { | |||||
// Pass replace | |||||
// Ref 1 | |||||
// } | |||||
CGPROGRAM | |||||
#pragma vertex vert_edge | |||||
#pragma fragment frag | |||||
#define SMAA_PREDICATION 0 | |||||
#include "UnityCG.cginc" | |||||
#include "SMAA.cginc" | |||||
float4 frag(fInput_edge i) : SV_Target | |||||
{ | |||||
return float4(SMAADepthEdgeDetectionPS(i.uv, i.offset, _CameraDepthTexture), 0.0, 0.0); | |||||
} | |||||
ENDCG | |||||
} | |||||
// ----------------------------------------------------------------------------- | |||||
// Blend Weights Calculation | |||||
// (4) | |||||
Pass | |||||
{ | |||||
// TODO: Stencil not working | |||||
// Stencil | |||||
// { | |||||
// Pass keep | |||||
// Comp equal | |||||
// Ref 1 | |||||
// } | |||||
CGPROGRAM | |||||
#pragma vertex vert | |||||
#pragma fragment frag | |||||
#pragma multi_compile __ USE_DIAG_SEARCH | |||||
#pragma multi_compile __ USE_CORNER_DETECTION | |||||
#if !defined(USE_DIAG_SEARCH) | |||||
#define SMAA_DISABLE_DIAG_DETECTION | |||||
#endif | |||||
#if !defined(USE_CORNER_DETECTION) | |||||
#define SMAA_DISABLE_CORNER_DETECTION | |||||
#endif | |||||
#include "UnityCG.cginc" | |||||
#include "SMAA.cginc" | |||||
struct fInput | |||||
{ | |||||
float4 pos : SV_POSITION; | |||||
float2 uv : TEXCOORD0; | |||||
float2 pixcoord : TEXCOORD1; | |||||
float4 offset[3] : TEXCOORD2; | |||||
}; | |||||
fInput vert(vInput i) | |||||
{ | |||||
fInput o; | |||||
o.pos = mul(UNITY_MATRIX_MVP, i.pos); | |||||
o.uv = i.uv; | |||||
o.pixcoord = o.uv * SMAA_RT_METRICS.zw; | |||||
// We will use these offsets for the searches later on (see @PSEUDO_GATHER4): | |||||
o.offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, -0.125, 1.25, -0.125), o.uv.xyxy); | |||||
o.offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, -0.25, -0.125, 1.25), o.uv.xyxy); | |||||
// And these for the searches, they indicate the ends of the loops: | |||||
o.offset[2] = mad(SMAA_RT_METRICS.xxyy, float4(-2.0, 2.0, -2.0, 2.0) * float(SMAA_MAX_SEARCH_STEPS), | |||||
float4(o.offset[0].xz, o.offset[1].yw)); | |||||
return o; | |||||
} | |||||
float4 frag(fInput i) : SV_Target | |||||
{ | |||||
return SMAABlendingWeightCalculationPS(i.uv, i.pixcoord, i.offset, _MainTex, _AreaTex, _SearchTex, | |||||
_SubsampleIndices); | |||||
} | |||||
ENDCG | |||||
} | |||||
// ----------------------------------------------------------------------------- | |||||
// Neighborhood Blending | |||||
// (5) | |||||
Pass | |||||
{ | |||||
CGPROGRAM | |||||
#pragma vertex vert | |||||
#pragma fragment frag | |||||
#pragma multi_compile __ USE_UV_BASED_REPROJECTION | |||||
#if defined (USE_UV_BASED_REPROJECTION) | |||||
#define SMAA_UV_BASED_REPROJECTION 1 | |||||
#endif | |||||
#include "UnityCG.cginc" | |||||
#include "SMAA.cginc" | |||||
struct fInput | |||||
{ | |||||
float4 pos : SV_POSITION; | |||||
float2 uv : TEXCOORD0; | |||||
float4 offset : TEXCOORD1; | |||||
}; | |||||
fInput vert(vInput i) | |||||
{ | |||||
fInput o; | |||||
o.pos = mul(UNITY_MATRIX_MVP, i.pos); | |||||
o.uv = i.uv; | |||||
o.offset = mad(SMAA_RT_METRICS.xyxy, float4(1.0, 0.0, 0.0, 1.0), o.uv.xyxy); | |||||
return o; | |||||
} | |||||
float4 frag(fInput i) : SV_Target | |||||
{ | |||||
return SMAANeighborhoodBlendingPS(i.uv, i.offset, _MainTex, _BlendTex); | |||||
} | |||||
ENDCG | |||||
} | |||||
// ----------------------------------------------------------------------------- | |||||
// Accumulation Resolve | |||||
// (6) | |||||
Pass | |||||
{ | |||||
CGPROGRAM | |||||
#pragma vertex vert | |||||
#pragma fragment frag | |||||
#pragma multi_compile __ USE_UV_BASED_REPROJECTION | |||||
#if defined (USE_UV_BASED_REPROJECTION) | |||||
#define SMAA_UV_BASED_REPROJECTION 1 | |||||
#endif | |||||
#include "UnityCG.cginc" | |||||
#include "SMAA.cginc" | |||||
struct fInput | |||||
{ | |||||
float4 pos : SV_POSITION; | |||||
float2 uv : TEXCOORD0; | |||||
}; | |||||
fInput vert(vInput i) | |||||
{ | |||||
fInput o; | |||||
o.pos = mul(UNITY_MATRIX_MVP, i.pos); | |||||
o.uv = i.uv; | |||||
return o; | |||||
} | |||||
float4 frag(fInput i) : SV_Target | |||||
{ | |||||
return SMAAResolvePS(i.uv, _MainTex, _AccumulationTex); | |||||
} | |||||
ENDCG | |||||
} | |||||
} | |||||
FallBack off | |||||
} |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 4505fec7a81214243b8e59edf89e3a53 | |||||
timeCreated: 1432603500 | |||||
licenseType: Store | |||||
ShaderImporter: | |||||
defaultTextures: [] | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,57 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 387ed7c38eb63554db846987adb98e68 | |||||
timeCreated: 1432601000 | |||||
licenseType: Store | |||||
TextureImporter: | |||||
fileIDToRecycleName: {} | |||||
serializedVersion: 2 | |||||
mipmaps: | |||||
mipMapMode: 0 | |||||
enableMipMap: 0 | |||||
linearTexture: 1 | |||||
correctGamma: 0 | |||||
fadeOut: 0 | |||||
borderMipMap: 0 | |||||
mipMapFadeDistanceStart: 1 | |||||
mipMapFadeDistanceEnd: 3 | |||||
bumpmap: | |||||
convertToNormalMap: 0 | |||||
externalNormalMap: 0 | |||||
heightScale: 0.25 | |||||
normalMapFilter: 0 | |||||
isReadable: 0 | |||||
grayScaleToAlpha: 1 | |||||
generateCubemap: 0 | |||||
cubemapConvolution: 0 | |||||
cubemapConvolutionSteps: 8 | |||||
cubemapConvolutionExponent: 1.5 | |||||
seamlessCubemap: 0 | |||||
textureFormat: 1 | |||||
maxTextureSize: 2048 | |||||
textureSettings: | |||||
filterMode: 0 | |||||
aniso: -1 | |||||
mipBias: -1 | |||||
wrapMode: 1 | |||||
nPOTScale: 0 | |||||
lightmap: 0 | |||||
rGBM: 2 | |||||
compressionQuality: 50 | |||||
allowsAlphaSplitting: 0 | |||||
spriteMode: 0 | |||||
spriteExtrude: 1 | |||||
spriteMeshType: 1 | |||||
alignment: 0 | |||||
spritePivot: {x: 0.5, y: 0.5} | |||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0} | |||||
spritePixelsToUnits: 100 | |||||
alphaIsTransparency: 0 | |||||
textureType: 5 | |||||
buildTargetSettings: [] | |||||
spriteSheet: | |||||
sprites: [] | |||||
outline: [] | |||||
spritePackingTag: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,484 @@ | |||||
using UnityEngine; | |||||
using System; | |||||
#if UNITY_EDITOR | |||||
using UnityEditor; | |||||
#endif | |||||
using Object = UnityEngine.Object; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
[Serializable] | |||||
public class SMAA : IAntiAliasing | |||||
{ | |||||
[AttributeUsage(AttributeTargets.Field)] | |||||
public class SettingsGroup : Attribute | |||||
{} | |||||
[AttributeUsage(AttributeTargets.Field)] | |||||
public class TopLevelSettings : Attribute | |||||
{} | |||||
[AttributeUsage(AttributeTargets.Field)] | |||||
public class ExperimentalGroup : Attribute | |||||
{} | |||||
public enum DebugPass | |||||
{ | |||||
Off, | |||||
Edges, | |||||
Weights, | |||||
Accumulation | |||||
} | |||||
public enum QualityPreset | |||||
{ | |||||
Low = 0, | |||||
Medium = 1, | |||||
High = 2, | |||||
Ultra = 3, | |||||
Custom | |||||
} | |||||
public enum EdgeDetectionMethod | |||||
{ | |||||
Luma = 1, | |||||
Color = 2, | |||||
Depth = 3 | |||||
} | |||||
[Serializable] | |||||
public struct GlobalSettings | |||||
{ | |||||
[Tooltip("Use this to fine tune your settings when working in Custom quality mode. \"Accumulation\" only works when \"Temporal Filtering\" is enabled.")] | |||||
public DebugPass debugPass; | |||||
[Tooltip("Low: 60% of the quality.\nMedium: 80% of the quality.\nHigh: 95% of the quality.\nUltra: 99% of the quality (overkill).")] | |||||
public QualityPreset quality; | |||||
[Tooltip("You've three edge detection methods to choose from: luma, color or depth.\nThey represent different quality/performance and anti-aliasing/sharpness tradeoffs, so our recommendation is for you to choose the one that best suits your particular scenario:\n\n- Depth edge detection is usually the fastest but it may miss some edges.\n- Luma edge detection is usually more expensive than depth edge detection, but catches visible edges that depth edge detection can miss.\n- Color edge detection is usually the most expensive one but catches chroma-only edges.")] | |||||
public EdgeDetectionMethod edgeDetectionMethod; | |||||
public static GlobalSettings defaultSettings | |||||
{ | |||||
get | |||||
{ | |||||
return new GlobalSettings | |||||
{ | |||||
debugPass = DebugPass.Off, | |||||
quality = QualityPreset.High, | |||||
edgeDetectionMethod = EdgeDetectionMethod.Color | |||||
}; | |||||
} | |||||
} | |||||
} | |||||
[Serializable] | |||||
public struct QualitySettings | |||||
{ | |||||
[Tooltip("Enables/Disables diagonal processing.")] | |||||
public bool diagonalDetection; | |||||
[Tooltip("Enables/Disables corner detection. Leave this on to avoid blurry corners.")] | |||||
public bool cornerDetection; | |||||
[Range(0f, 0.5f)] | |||||
[Tooltip("Specifies the threshold or sensitivity to edges. Lowering this value you will be able to detect more edges at the expense of performance.\n0.1 is a reasonable value, and allows to catch most visible edges. 0.05 is a rather overkill value, that allows to catch 'em all.")] | |||||
public float threshold; | |||||
[Min(0.0001f)] | |||||
[Tooltip("Specifies the threshold for depth edge detection. Lowering this value you will be able to detect more edges at the expense of performance.")] | |||||
public float depthThreshold; | |||||
[Range(0, 112)] | |||||
[Tooltip("Specifies the maximum steps performed in the horizontal/vertical pattern searches, at each side of the pixel.\nIn number of pixels, it's actually the double. So the maximum line length perfectly handled by, for example 16, is 64 (by perfectly, we meant that longer lines won't look as good, but still antialiased).")] | |||||
public int maxSearchSteps; | |||||
[Range(0, 20)] | |||||
[Tooltip("Specifies the maximum steps performed in the diagonal pattern searches, at each side of the pixel. In this case we jump one pixel at time, instead of two.\nOn high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 steps), but it can have a significant impact on older machines.")] | |||||
public int maxDiagonalSearchSteps; | |||||
[Range(0, 100)] | |||||
[Tooltip("Specifies how much sharp corners will be rounded.")] | |||||
public int cornerRounding; | |||||
[Min(0f)] | |||||
[Tooltip("If there is an neighbor edge that has a local contrast factor times bigger contrast than current edge, current edge will be discarded.\nThis allows to eliminate spurious crossing edges, and is based on the fact that, if there is too much contrast in a direction, that will hide perceptually contrast in the other neighbors.")] | |||||
public float localContrastAdaptationFactor; | |||||
public static QualitySettings[] presetQualitySettings = | |||||
{ | |||||
// Low | |||||
new QualitySettings | |||||
{ | |||||
diagonalDetection = false, | |||||
cornerDetection = false, | |||||
threshold = 0.15f, | |||||
depthThreshold = 0.01f, | |||||
maxSearchSteps = 4, | |||||
maxDiagonalSearchSteps = 8, | |||||
cornerRounding = 25, | |||||
localContrastAdaptationFactor = 2f | |||||
}, | |||||
// Medium | |||||
new QualitySettings | |||||
{ | |||||
diagonalDetection = false, | |||||
cornerDetection = false, | |||||
threshold = 0.1f, | |||||
depthThreshold = 0.01f, | |||||
maxSearchSteps = 8, | |||||
maxDiagonalSearchSteps = 8, | |||||
cornerRounding = 25, | |||||
localContrastAdaptationFactor = 2f | |||||
}, | |||||
// High | |||||
new QualitySettings | |||||
{ | |||||
diagonalDetection = true, | |||||
cornerDetection = true, | |||||
threshold = 0.1f, | |||||
depthThreshold = 0.01f, | |||||
maxSearchSteps = 16, | |||||
maxDiagonalSearchSteps = 8, | |||||
cornerRounding = 25, | |||||
localContrastAdaptationFactor = 2f | |||||
}, | |||||
// Ultra | |||||
new QualitySettings | |||||
{ | |||||
diagonalDetection = true, | |||||
cornerDetection = true, | |||||
threshold = 0.05f, | |||||
depthThreshold = 0.01f, | |||||
maxSearchSteps = 32, | |||||
maxDiagonalSearchSteps = 16, | |||||
cornerRounding = 25, | |||||
localContrastAdaptationFactor = 2f | |||||
}, | |||||
}; | |||||
} | |||||
[Serializable] | |||||
public struct TemporalSettings | |||||
{ | |||||
[Tooltip("Temporal filtering makes it possible for the SMAA algorithm to benefit from minute subpixel information available that has been accumulated over many frames.")] | |||||
public bool enabled; | |||||
public bool UseTemporal() | |||||
{ | |||||
#if UNITY_EDITOR | |||||
return enabled && EditorApplication.isPlayingOrWillChangePlaymode; | |||||
#else | |||||
return enabled; | |||||
#endif | |||||
} | |||||
[Range(0.5f, 10.0f)] | |||||
[Tooltip("The size of the fuzz-displacement (jitter) in pixels applied to the camera's perspective projection matrix.\nUsed for 2x temporal anti-aliasing.")] | |||||
public float fuzzSize; | |||||
public static TemporalSettings defaultSettings | |||||
{ | |||||
get | |||||
{ | |||||
return new TemporalSettings | |||||
{ | |||||
enabled = false, | |||||
fuzzSize = 2f | |||||
}; | |||||
} | |||||
} | |||||
} | |||||
[Serializable] | |||||
public struct PredicationSettings | |||||
{ | |||||
[Tooltip("Predicated thresholding allows to better preserve texture details and to improve performance, by decreasing the number of detected edges using an additional buffer (the detph buffer).\nIt locally decreases the luma or color threshold if an edge is found in an additional buffer (so the global threshold can be higher).")] | |||||
public bool enabled; | |||||
[Min(0.0001f)] | |||||
[Tooltip("Threshold to be used in the additional predication buffer.")] | |||||
public float threshold; | |||||
[Range(1f, 5f)] | |||||
[Tooltip("How much to scale the global threshold used for luma or color edge detection when using predication.")] | |||||
public float scale; | |||||
[Range(0f, 1f)] | |||||
[Tooltip("How much to locally decrease the threshold.")] | |||||
public float strength; | |||||
public static PredicationSettings defaultSettings | |||||
{ | |||||
get | |||||
{ | |||||
return new PredicationSettings | |||||
{ | |||||
enabled = false, | |||||
threshold = 0.01f, | |||||
scale = 2f, | |||||
strength = 0.4f | |||||
}; | |||||
} | |||||
} | |||||
} | |||||
[TopLevelSettings] | |||||
public GlobalSettings settings = GlobalSettings.defaultSettings; | |||||
[SettingsGroup] | |||||
public QualitySettings quality = QualitySettings.presetQualitySettings[2]; | |||||
[SettingsGroup] | |||||
public PredicationSettings predication = PredicationSettings.defaultSettings; | |||||
[SettingsGroup, ExperimentalGroup] | |||||
public TemporalSettings temporal = TemporalSettings.defaultSettings; | |||||
private Matrix4x4 m_ProjectionMatrix; | |||||
private Matrix4x4 m_PreviousViewProjectionMatrix; | |||||
private float m_FlipFlop = 1.0f; | |||||
private RenderTexture m_Accumulation; | |||||
private Shader m_Shader; | |||||
public Shader shader | |||||
{ | |||||
get | |||||
{ | |||||
if (m_Shader == null) | |||||
m_Shader = Shader.Find("Hidden/Subpixel Morphological Anti-aliasing"); | |||||
return m_Shader; | |||||
} | |||||
} | |||||
private Texture2D m_AreaTexture; | |||||
private Texture2D areaTexture | |||||
{ | |||||
get | |||||
{ | |||||
if (m_AreaTexture == null) | |||||
m_AreaTexture = Resources.Load<Texture2D>("AreaTex"); | |||||
return m_AreaTexture; | |||||
} | |||||
} | |||||
private Texture2D m_SearchTexture; | |||||
private Texture2D searchTexture | |||||
{ | |||||
get | |||||
{ | |||||
if (m_SearchTexture == null) | |||||
m_SearchTexture = Resources.Load<Texture2D>("SearchTex"); | |||||
return m_SearchTexture; | |||||
} | |||||
} | |||||
private Material m_Material; | |||||
private Material material | |||||
{ | |||||
get | |||||
{ | |||||
if (m_Material == null) | |||||
m_Material = ImageEffectHelper.CheckShaderAndCreateMaterial(shader); | |||||
return m_Material; | |||||
} | |||||
} | |||||
public void OnEnable(AntiAliasing owner) | |||||
{ | |||||
if (!ImageEffectHelper.IsSupported(shader, true, false, owner)) | |||||
owner.enabled = false; | |||||
} | |||||
public void OnDisable() | |||||
{ | |||||
// Cleanup | |||||
if (m_Material != null) | |||||
Object.DestroyImmediate(m_Material); | |||||
if (m_Accumulation != null) | |||||
Object.DestroyImmediate(m_Accumulation); | |||||
m_Material = null; | |||||
m_Accumulation = null; | |||||
} | |||||
public void OnPreCull(Camera camera) | |||||
{ | |||||
if (temporal.UseTemporal()) | |||||
{ | |||||
m_ProjectionMatrix = camera.projectionMatrix; | |||||
m_FlipFlop -= (2.0f * m_FlipFlop); | |||||
Matrix4x4 fuzz = Matrix4x4.identity; | |||||
fuzz.m03 = (0.25f * m_FlipFlop) * temporal.fuzzSize / camera.pixelWidth; | |||||
fuzz.m13 = (-0.25f * m_FlipFlop) * temporal.fuzzSize / camera.pixelHeight; | |||||
camera.projectionMatrix = fuzz * camera.projectionMatrix; | |||||
} | |||||
} | |||||
public void OnPostRender(Camera camera) | |||||
{ | |||||
if (temporal.UseTemporal()) | |||||
camera.ResetProjectionMatrix(); | |||||
} | |||||
public void OnRenderImage(Camera camera, RenderTexture source, RenderTexture destination) | |||||
{ | |||||
int width = camera.pixelWidth; | |||||
int height = camera.pixelHeight; | |||||
bool isFirstFrame = false; | |||||
QualitySettings preset = quality; | |||||
if (settings.quality != QualityPreset.Custom) | |||||
preset = QualitySettings.presetQualitySettings[(int)settings.quality]; | |||||
// Pass IDs | |||||
int passEdgeDetection = (int)settings.edgeDetectionMethod; | |||||
int passBlendWeights = 4; | |||||
int passNeighborhoodBlending = 5; | |||||
int passResolve = 6; | |||||
// Reprojection setup | |||||
var viewProjectionMatrix = GL.GetGPUProjectionMatrix(m_ProjectionMatrix, true) * camera.worldToCameraMatrix; | |||||
// Uniforms | |||||
material.SetTexture("_AreaTex", areaTexture); | |||||
material.SetTexture("_SearchTex", searchTexture); | |||||
material.SetVector("_Metrics", new Vector4(1f / width, 1f / height, width, height)); | |||||
material.SetVector("_Params1", new Vector4(preset.threshold, preset.depthThreshold, preset.maxSearchSteps, preset.maxDiagonalSearchSteps)); | |||||
material.SetVector("_Params2", new Vector2(preset.cornerRounding, preset.localContrastAdaptationFactor)); | |||||
material.SetMatrix("_ReprojectionMatrix", m_PreviousViewProjectionMatrix * Matrix4x4.Inverse(viewProjectionMatrix)); | |||||
float subsampleIndex = (m_FlipFlop < 0.0f) ? 2.0f : 1.0f; | |||||
material.SetVector("_SubsampleIndices", new Vector4(subsampleIndex, subsampleIndex, subsampleIndex, 0.0f)); | |||||
// Handle predication & depth-based edge detection | |||||
Shader.DisableKeyword("USE_PREDICATION"); | |||||
if (settings.edgeDetectionMethod == EdgeDetectionMethod.Depth) | |||||
{ | |||||
camera.depthTextureMode |= DepthTextureMode.Depth; | |||||
} | |||||
else if (predication.enabled) | |||||
{ | |||||
camera.depthTextureMode |= DepthTextureMode.Depth; | |||||
Shader.EnableKeyword("USE_PREDICATION"); | |||||
material.SetVector("_Params3", new Vector3(predication.threshold, predication.scale, predication.strength)); | |||||
} | |||||
// Diag search & corner detection | |||||
Shader.DisableKeyword("USE_DIAG_SEARCH"); | |||||
Shader.DisableKeyword("USE_CORNER_DETECTION"); | |||||
if (preset.diagonalDetection) | |||||
Shader.EnableKeyword("USE_DIAG_SEARCH"); | |||||
if (preset.cornerDetection) | |||||
Shader.EnableKeyword("USE_CORNER_DETECTION"); | |||||
// UV-based reprojection (up to Unity 5.x) | |||||
// TODO: use motion vectors when available! | |||||
Shader.DisableKeyword("USE_UV_BASED_REPROJECTION"); | |||||
if (temporal.UseTemporal()) | |||||
Shader.EnableKeyword("USE_UV_BASED_REPROJECTION"); | |||||
// Persistent textures and lazy-initializations | |||||
if (m_Accumulation == null || (m_Accumulation.width != width || m_Accumulation.height != height)) | |||||
{ | |||||
if (m_Accumulation) | |||||
RenderTexture.ReleaseTemporary(m_Accumulation); | |||||
m_Accumulation = RenderTexture.GetTemporary(width, height, 0, source.format, RenderTextureReadWrite.Linear); | |||||
m_Accumulation.hideFlags = HideFlags.HideAndDontSave; | |||||
isFirstFrame = true; | |||||
} | |||||
RenderTexture rt1 = TempRT(width, height, source.format); | |||||
Graphics.Blit(null, rt1, material, 0); // Clear | |||||
// Edge Detection | |||||
Graphics.Blit(source, rt1, material, passEdgeDetection); | |||||
if (settings.debugPass == DebugPass.Edges) | |||||
{ | |||||
Graphics.Blit(rt1, destination); | |||||
} | |||||
else | |||||
{ | |||||
RenderTexture rt2 = TempRT(width, height, source.format); | |||||
Graphics.Blit(null, rt2, material, 0); // Clear | |||||
// Blend Weights | |||||
Graphics.Blit(rt1, rt2, material, passBlendWeights); | |||||
if (settings.debugPass == DebugPass.Weights) | |||||
{ | |||||
Graphics.Blit(rt2, destination); | |||||
} | |||||
else | |||||
{ | |||||
// Neighborhood Blending | |||||
material.SetTexture("_BlendTex", rt2); | |||||
if (temporal.UseTemporal()) | |||||
{ | |||||
// Temporal filtering | |||||
Graphics.Blit(source, rt1, material, passNeighborhoodBlending); | |||||
if (settings.debugPass == DebugPass.Accumulation) | |||||
{ | |||||
Graphics.Blit(m_Accumulation, destination); | |||||
} | |||||
else if (!isFirstFrame) | |||||
{ | |||||
material.SetTexture("_AccumulationTex", m_Accumulation); | |||||
Graphics.Blit(rt1, destination, material, passResolve); | |||||
} | |||||
else | |||||
{ | |||||
Graphics.Blit(rt1, destination); | |||||
} | |||||
//Graphics.Blit(rt1, m_Accumulation); | |||||
Graphics.Blit(destination, m_Accumulation); | |||||
RenderTexture.active = null; | |||||
} | |||||
else | |||||
{ | |||||
Graphics.Blit(source, destination, material, passNeighborhoodBlending); | |||||
} | |||||
} | |||||
RenderTexture.ReleaseTemporary(rt2); | |||||
} | |||||
RenderTexture.ReleaseTemporary(rt1); | |||||
// Store the future-previous frame's view-projection matrix | |||||
m_PreviousViewProjectionMatrix = viewProjectionMatrix; | |||||
} | |||||
private RenderTexture TempRT(int width, int height, RenderTextureFormat format) | |||||
{ | |||||
// Skip the depth & stencil buffer creation when DebugPass is set to avoid flickering | |||||
// TODO: Stencil buffer not working for some reason | |||||
// int depthStencilBits = DebugPass == DebugPass.Off ? 24 : 0; | |||||
int depthStencilBits = 0; | |||||
return RenderTexture.GetTemporary(width, height, depthStencilBits, format, RenderTextureReadWrite.Linear); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,13 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 9c2502951f8f3e743917c441eba57d1c | |||||
timeCreated: 1454511067 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: | |||||
- m_Shader: {fileID: 4800000, guid: 4505fec7a81214243b8e59edf89e3a53, type: 3} | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 193f90bb87f484c62ad73788d9cb2d44 | |||||
folderAsset: yes | |||||
timeCreated: 1454052266 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,205 @@ | |||||
using System; | |||||
using UnityEngine; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
[ExecuteInEditMode] | |||||
[RequireComponent(typeof(Camera))] | |||||
[AddComponentMenu("Image Effects/Cinematic/Bloom")] | |||||
#if UNITY_5_4_OR_NEWER | |||||
[ImageEffectAllowedInSceneView] | |||||
#endif | |||||
public class Bloom : MonoBehaviour | |||||
{ | |||||
[Serializable] | |||||
public struct Settings | |||||
{ | |||||
[SerializeField] | |||||
[Tooltip("Filters out pixels under this level of brightness.")] | |||||
public float threshold; | |||||
public float thresholdGamma | |||||
{ | |||||
set { threshold = value; } | |||||
get { return Mathf.Max(0.0f, threshold); } | |||||
} | |||||
public float thresholdLinear | |||||
{ | |||||
set { threshold = Mathf.LinearToGammaSpace(value); } | |||||
get { return Mathf.GammaToLinearSpace(thresholdGamma); } | |||||
} | |||||
[SerializeField, Range(0, 1)] | |||||
[Tooltip("Makes transition between under/over-threshold gradual.")] | |||||
public float softKnee; | |||||
[SerializeField, Range(1, 7)] | |||||
[Tooltip("Changes extent of veiling effects in a screen resolution-independent fashion.")] | |||||
public float radius; | |||||
[SerializeField] | |||||
[Tooltip("Blend factor of the result image.")] | |||||
public float intensity; | |||||
[SerializeField] | |||||
[Tooltip("Controls filter quality and buffer resolution.")] | |||||
public bool highQuality; | |||||
[SerializeField] | |||||
[Tooltip("Reduces flashing noise with an additional filter.")] | |||||
public bool antiFlicker; | |||||
public static Settings defaultSettings | |||||
{ | |||||
get | |||||
{ | |||||
var settings = new Settings | |||||
{ | |||||
threshold = 0.9f, | |||||
softKnee = 0.5f, | |||||
radius = 2.0f, | |||||
intensity = 0.7f, | |||||
highQuality = true, | |||||
antiFlicker = false | |||||
}; | |||||
return settings; | |||||
} | |||||
} | |||||
} | |||||
#region Public Properties | |||||
[SerializeField] | |||||
public Settings settings = Settings.defaultSettings; | |||||
#endregion | |||||
[SerializeField, HideInInspector] | |||||
private Shader m_Shader; | |||||
public Shader shader | |||||
{ | |||||
get | |||||
{ | |||||
if (m_Shader == null) | |||||
{ | |||||
const string shaderName = "Hidden/Image Effects/Cinematic/Bloom"; | |||||
m_Shader = Shader.Find(shaderName); | |||||
} | |||||
return m_Shader; | |||||
} | |||||
} | |||||
private Material m_Material; | |||||
public Material material | |||||
{ | |||||
get | |||||
{ | |||||
if (m_Material == null) | |||||
m_Material = ImageEffectHelper.CheckShaderAndCreateMaterial(shader); | |||||
return m_Material; | |||||
} | |||||
} | |||||
#region Private Members | |||||
const int kMaxIterations = 16; | |||||
RenderTexture[] m_blurBuffer1 = new RenderTexture[kMaxIterations]; | |||||
RenderTexture[] m_blurBuffer2 = new RenderTexture[kMaxIterations]; | |||||
private void OnEnable() | |||||
{ | |||||
if (!ImageEffectHelper.IsSupported(shader, true, false, this)) | |||||
enabled = false; | |||||
} | |||||
private void OnDisable() | |||||
{ | |||||
if (m_Material != null) | |||||
DestroyImmediate(m_Material); | |||||
m_Material = null; | |||||
} | |||||
private void OnRenderImage(RenderTexture source, RenderTexture destination) | |||||
{ | |||||
var useRGBM = Application.isMobilePlatform; | |||||
// source texture size | |||||
var tw = source.width; | |||||
var th = source.height; | |||||
// halve the texture size for the low quality mode | |||||
if (!settings.highQuality) | |||||
{ | |||||
tw /= 2; | |||||
th /= 2; | |||||
} | |||||
// blur buffer format | |||||
var rtFormat = useRGBM ? RenderTextureFormat.Default : RenderTextureFormat.DefaultHDR; | |||||
// determine the iteration count | |||||
var logh = Mathf.Log(th, 2) + settings.radius - 8; | |||||
var logh_i = (int)logh; | |||||
var iterations = Mathf.Clamp(logh_i, 1, kMaxIterations); | |||||
// update the shader properties | |||||
var threshold = settings.thresholdLinear; | |||||
material.SetFloat("_Threshold", threshold); | |||||
var knee = threshold * settings.softKnee + 1e-5f; | |||||
var curve = new Vector3(threshold - knee, knee * 2, 0.25f / knee); | |||||
material.SetVector("_Curve", curve); | |||||
var pfo = !settings.highQuality && settings.antiFlicker; | |||||
material.SetFloat("_PrefilterOffs", pfo ? -0.5f : 0.0f); | |||||
material.SetFloat("_SampleScale", 0.5f + logh - logh_i); | |||||
material.SetFloat("_Intensity", Mathf.Max(0.0f, settings.intensity)); | |||||
// prefilter pass | |||||
var prefiltered = RenderTexture.GetTemporary(tw, th, 0, rtFormat); | |||||
Graphics.Blit(source, prefiltered, material, settings.antiFlicker ? 1 : 0); | |||||
// construct a mip pyramid | |||||
var last = prefiltered; | |||||
for (var level = 0; level < iterations; level++) | |||||
{ | |||||
m_blurBuffer1[level] = RenderTexture.GetTemporary(last.width / 2, last.height / 2, 0, rtFormat); | |||||
Graphics.Blit(last, m_blurBuffer1[level], material, level == 0 ? (settings.antiFlicker ? 3 : 2) : 4); | |||||
last = m_blurBuffer1[level]; | |||||
} | |||||
// upsample and combine loop | |||||
for (var level = iterations - 2; level >= 0; level--) | |||||
{ | |||||
var basetex = m_blurBuffer1[level]; | |||||
material.SetTexture("_BaseTex", basetex); | |||||
m_blurBuffer2[level] = RenderTexture.GetTemporary(basetex.width, basetex.height, 0, rtFormat); | |||||
Graphics.Blit(last, m_blurBuffer2[level], material, settings.highQuality ? 6 : 5); | |||||
last = m_blurBuffer2[level]; | |||||
} | |||||
// finish process | |||||
material.SetTexture("_BaseTex", source); | |||||
Graphics.Blit(last, destination, material, settings.highQuality ? 8 : 7); | |||||
// release the temporary buffers | |||||
for (var i = 0; i < kMaxIterations; i++) | |||||
{ | |||||
if (m_blurBuffer1[i] != null) RenderTexture.ReleaseTemporary(m_blurBuffer1[i]); | |||||
if (m_blurBuffer2[i] != null) RenderTexture.ReleaseTemporary(m_blurBuffer2[i]); | |||||
m_blurBuffer1[i] = null; | |||||
m_blurBuffer2[i] = null; | |||||
} | |||||
RenderTexture.ReleaseTemporary(prefiltered); | |||||
} | |||||
#endregion | |||||
} | |||||
} |
@ -0,0 +1,13 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 32187365ced0c42219cde2b57c99b323 | |||||
timeCreated: 1454052338 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: | |||||
- _shader: {fileID: 4800000, guid: e45d4f28262b04d10a075856ab5fdb5e, type: 3} | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 92a024b1f1430409eb656f65969aa3d5 | |||||
folderAsset: yes | |||||
timeCreated: 1454052266 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,54 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using UnityEngine; | |||||
using UnityEditor; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
[CanEditMultipleObjects] | |||||
[CustomEditor(typeof(Bloom))] | |||||
public class BloomEditor : Editor | |||||
{ | |||||
[NonSerialized] | |||||
private List<SerializedProperty> m_Properties = new List<SerializedProperty>(); | |||||
BloomGraphDrawer _graph; | |||||
bool CheckHdr(Bloom target) | |||||
{ | |||||
var camera = target.GetComponent<Camera>(); | |||||
return camera != null && camera.hdr; | |||||
} | |||||
void OnEnable() | |||||
{ | |||||
var settings = FieldFinder<Bloom>.GetField(x => x.settings); | |||||
foreach (var setting in settings.FieldType.GetFields()) | |||||
{ | |||||
var prop = settings.Name + "." + setting.Name; | |||||
m_Properties.Add(serializedObject.FindProperty(prop)); | |||||
} | |||||
_graph = new BloomGraphDrawer(); | |||||
} | |||||
public override void OnInspectorGUI() | |||||
{ | |||||
serializedObject.Update(); | |||||
if (!serializedObject.isEditingMultipleObjects) | |||||
{ | |||||
EditorGUILayout.Space(); | |||||
var bloom = (Bloom)target; | |||||
_graph.Prepare(bloom.settings, CheckHdr(bloom)); | |||||
_graph.DrawGraph(); | |||||
EditorGUILayout.Space(); | |||||
} | |||||
foreach (var property in m_Properties) | |||||
EditorGUILayout.PropertyField(property); | |||||
serializedObject.ApplyModifiedProperties(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 38020a6029a85434a95a6f725a5aae5f | |||||
timeCreated: 1454052266 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,156 @@ | |||||
using UnityEngine; | |||||
using UnityEditor; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
// Class used for drawing the brightness response curve | |||||
public class BloomGraphDrawer | |||||
{ | |||||
#region Public Methods | |||||
// Update internal state from given settings. | |||||
public void Prepare(Bloom.Settings settings, bool isHdr) | |||||
{ | |||||
if (isHdr) | |||||
{ | |||||
_rangeX = 6; | |||||
_rangeY = 1.5f; | |||||
} | |||||
else | |||||
{ | |||||
_rangeX = 1; | |||||
_rangeY = 1; | |||||
} | |||||
_threshold = settings.thresholdLinear; | |||||
_knee = settings.softKnee * _threshold + 1e-5f; | |||||
// Intensity is capped to prevent sampling errors. | |||||
_intensity = Mathf.Min(settings.intensity, 10); | |||||
} | |||||
// Draw the graph at the current position. | |||||
public void DrawGraph() | |||||
{ | |||||
_rectGraph = GUILayoutUtility.GetRect(128, 80); | |||||
// Background | |||||
DrawRect(0, 0, _rangeX, _rangeY, 0.1f, 0.4f); | |||||
// Soft-knee range | |||||
DrawRect(_threshold - _knee, 0, _threshold + _knee, _rangeY, 0.25f, -1); | |||||
// Horizontal lines | |||||
for (var i = 1; i < _rangeY; i++) | |||||
DrawLine(0, i, _rangeX, i, 0.4f); | |||||
// Vertical lines | |||||
for (var i = 1; i < _rangeX; i++) | |||||
DrawLine(i, 0, i, _rangeY, 0.4f); | |||||
// Label | |||||
Handles.Label( | |||||
PointInRect(0, _rangeY) + Vector3.right, | |||||
"Brightness Response (linear)", EditorStyles.miniLabel | |||||
); | |||||
// Threshold line | |||||
DrawLine(_threshold, 0, _threshold, _rangeY, 0.6f); | |||||
// Response curve | |||||
var vcount = 0; | |||||
while (vcount < _curveResolution) | |||||
{ | |||||
var x = _rangeX * vcount / (_curveResolution - 1); | |||||
var y = ResponseFunction(x); | |||||
if (y < _rangeY) | |||||
{ | |||||
_curveVertices[vcount++] = PointInRect(x, y); | |||||
} | |||||
else | |||||
{ | |||||
if (vcount > 1) | |||||
{ | |||||
// Extend the last segment to the top edge of the rect. | |||||
var v1 = _curveVertices[vcount - 2]; | |||||
var v2 = _curveVertices[vcount - 1]; | |||||
var clip = (_rectGraph.y - v1.y) / (v2.y - v1.y); | |||||
_curveVertices[vcount - 1] = v1 + (v2 - v1) * clip; | |||||
} | |||||
break; | |||||
} | |||||
} | |||||
if (vcount > 1) | |||||
{ | |||||
Handles.color = Color.white * 0.9f; | |||||
Handles.DrawAAPolyLine(2.0f, vcount, _curveVertices); | |||||
} | |||||
} | |||||
#endregion | |||||
#region Response Function | |||||
float _threshold; | |||||
float _knee; | |||||
float _intensity; | |||||
float ResponseFunction(float x) | |||||
{ | |||||
var rq = Mathf.Clamp(x - _threshold + _knee, 0, _knee * 2); | |||||
rq = rq * rq * 0.25f / _knee; | |||||
return Mathf.Max(rq, x - _threshold) * _intensity; | |||||
} | |||||
#endregion | |||||
#region Graph Functions | |||||
// Number of vertices in curve | |||||
const int _curveResolution = 96; | |||||
// Vertex buffers | |||||
Vector3[] _rectVertices = new Vector3[4]; | |||||
Vector3[] _lineVertices = new Vector3[2]; | |||||
Vector3[] _curveVertices = new Vector3[_curveResolution]; | |||||
Rect _rectGraph; | |||||
float _rangeX; | |||||
float _rangeY; | |||||
// Transform a point into the graph rect. | |||||
Vector3 PointInRect(float x, float y) | |||||
{ | |||||
x = Mathf.Lerp(_rectGraph.x, _rectGraph.xMax, x / _rangeX); | |||||
y = Mathf.Lerp(_rectGraph.yMax, _rectGraph.y, y / _rangeY); | |||||
return new Vector3(x, y, 0); | |||||
} | |||||
// Draw a line in the graph rect. | |||||
void DrawLine(float x1, float y1, float x2, float y2, float grayscale) | |||||
{ | |||||
_lineVertices[0] = PointInRect(x1, y1); | |||||
_lineVertices[1] = PointInRect(x2, y2); | |||||
Handles.color = Color.white * grayscale; | |||||
Handles.DrawAAPolyLine(2.0f, _lineVertices); | |||||
} | |||||
// Draw a rect in the graph rect. | |||||
void DrawRect(float x1, float y1, float x2, float y2, float fill, float line) | |||||
{ | |||||
_rectVertices[0] = PointInRect(x1, y1); | |||||
_rectVertices[1] = PointInRect(x2, y1); | |||||
_rectVertices[2] = PointInRect(x2, y2); | |||||
_rectVertices[3] = PointInRect(x1, y2); | |||||
Handles.DrawSolidRectangleWithOutline( | |||||
_rectVertices, | |||||
fill < 0 ? Color.clear : Color.white * fill, | |||||
line < 0 ? Color.clear : Color.white * line | |||||
); | |||||
} | |||||
#endregion | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: e2d62b6f4e4eb4c6783477b5d99abdff | |||||
timeCreated: 1465439082 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 4af3202dbe79e460e9be42bcb6509fe0 | |||||
folderAsset: yes | |||||
timeCreated: 1454052266 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,238 @@ | |||||
#include "UnityCG.cginc" | |||||
// Mobile: use RGBM instead of float/half RGB | |||||
#define USE_RGBM defined(SHADER_API_MOBILE) | |||||
sampler2D _MainTex; | |||||
sampler2D _BaseTex; | |||||
float2 _MainTex_TexelSize; | |||||
float2 _BaseTex_TexelSize; | |||||
half4 _MainTex_ST; | |||||
half4 _BaseTex_ST; | |||||
float _PrefilterOffs; | |||||
half _Threshold; | |||||
half3 _Curve; | |||||
float _SampleScale; | |||||
half _Intensity; | |||||
// Brightness function | |||||
half Brightness(half3 c) | |||||
{ | |||||
return max(max(c.r, c.g), c.b); | |||||
} | |||||
// 3-tap median filter | |||||
half3 Median(half3 a, half3 b, half3 c) | |||||
{ | |||||
return a + b + c - min(min(a, b), c) - max(max(a, b), c); | |||||
} | |||||
// Clamp HDR value within a safe range | |||||
half3 SafeHDR(half3 c) { return min(c, 65000); } | |||||
half4 SafeHDR(half4 c) { return min(c, 65000); } | |||||
// RGBM encoding/decoding | |||||
half4 EncodeHDR(float3 rgb) | |||||
{ | |||||
#if USE_RGBM | |||||
rgb *= 1.0 / 8; | |||||
float m = max(max(rgb.r, rgb.g), max(rgb.b, 1e-6)); | |||||
m = ceil(m * 255) / 255; | |||||
return half4(rgb / m, m); | |||||
#else | |||||
return half4(rgb, 0); | |||||
#endif | |||||
} | |||||
float3 DecodeHDR(half4 rgba) | |||||
{ | |||||
#if USE_RGBM | |||||
return rgba.rgb * rgba.a * 8; | |||||
#else | |||||
return rgba.rgb; | |||||
#endif | |||||
} | |||||
// Downsample with a 4x4 box filter | |||||
half3 DownsampleFilter(float2 uv) | |||||
{ | |||||
float4 d = _MainTex_TexelSize.xyxy * float4(-1, -1, +1, +1); | |||||
half3 s; | |||||
s = DecodeHDR(tex2D(_MainTex, uv + d.xy)); | |||||
s += DecodeHDR(tex2D(_MainTex, uv + d.zy)); | |||||
s += DecodeHDR(tex2D(_MainTex, uv + d.xw)); | |||||
s += DecodeHDR(tex2D(_MainTex, uv + d.zw)); | |||||
return s * (1.0 / 4); | |||||
} | |||||
// Downsample with a 4x4 box filter + anti-flicker filter | |||||
half3 DownsampleAntiFlickerFilter(float2 uv) | |||||
{ | |||||
float4 d = _MainTex_TexelSize.xyxy * float4(-1, -1, +1, +1); | |||||
half3 s1 = DecodeHDR(tex2D(_MainTex, uv + d.xy)); | |||||
half3 s2 = DecodeHDR(tex2D(_MainTex, uv + d.zy)); | |||||
half3 s3 = DecodeHDR(tex2D(_MainTex, uv + d.xw)); | |||||
half3 s4 = DecodeHDR(tex2D(_MainTex, uv + d.zw)); | |||||
// Karis's luma weighted average (using brightness instead of luma) | |||||
half s1w = 1 / (Brightness(s1) + 1); | |||||
half s2w = 1 / (Brightness(s2) + 1); | |||||
half s3w = 1 / (Brightness(s3) + 1); | |||||
half s4w = 1 / (Brightness(s4) + 1); | |||||
half one_div_wsum = 1 / (s1w + s2w + s3w + s4w); | |||||
return (s1 * s1w + s2 * s2w + s3 * s3w + s4 * s4w) * one_div_wsum; | |||||
} | |||||
half3 UpsampleFilter(float2 uv) | |||||
{ | |||||
#if HIGH_QUALITY | |||||
// 9-tap bilinear upsampler (tent filter) | |||||
float4 d = _MainTex_TexelSize.xyxy * float4(1, 1, -1, 0) * _SampleScale; | |||||
half3 s; | |||||
s = DecodeHDR(tex2D(_MainTex, uv - d.xy)); | |||||
s += DecodeHDR(tex2D(_MainTex, uv - d.wy)) * 2; | |||||
s += DecodeHDR(tex2D(_MainTex, uv - d.zy)); | |||||
s += DecodeHDR(tex2D(_MainTex, uv + d.zw)) * 2; | |||||
s += DecodeHDR(tex2D(_MainTex, uv )) * 4; | |||||
s += DecodeHDR(tex2D(_MainTex, uv + d.xw)) * 2; | |||||
s += DecodeHDR(tex2D(_MainTex, uv + d.zy)); | |||||
s += DecodeHDR(tex2D(_MainTex, uv + d.wy)) * 2; | |||||
s += DecodeHDR(tex2D(_MainTex, uv + d.xy)); | |||||
return s * (1.0 / 16); | |||||
#else | |||||
// 4-tap bilinear upsampler | |||||
float4 d = _MainTex_TexelSize.xyxy * float4(-1, -1, +1, +1) * (_SampleScale * 0.5); | |||||
half3 s; | |||||
s = DecodeHDR(tex2D(_MainTex, uv + d.xy)); | |||||
s += DecodeHDR(tex2D(_MainTex, uv + d.zy)); | |||||
s += DecodeHDR(tex2D(_MainTex, uv + d.xw)); | |||||
s += DecodeHDR(tex2D(_MainTex, uv + d.zw)); | |||||
return s * (1.0 / 4); | |||||
#endif | |||||
} | |||||
// | |||||
// Vertex shader | |||||
// | |||||
v2f_img vert(appdata_img v) | |||||
{ | |||||
v2f_img o; | |||||
#if UNITY_VERSION >= 540 | |||||
o.pos = UnityObjectToClipPos(v.vertex); | |||||
o.uv = UnityStereoScreenSpaceUVAdjust(v.texcoord, _MainTex_ST); | |||||
#else | |||||
o.pos = mul(UNITY_MATRIX_MVP, v.vertex); | |||||
o.uv = v.texcoord; | |||||
#endif | |||||
return o; | |||||
} | |||||
struct v2f_multitex | |||||
{ | |||||
float4 pos : SV_POSITION; | |||||
float2 uvMain : TEXCOORD0; | |||||
float2 uvBase : TEXCOORD1; | |||||
}; | |||||
v2f_multitex vert_multitex(appdata_img v) | |||||
{ | |||||
v2f_multitex o; | |||||
#if UNITY_VERSION >= 540 | |||||
o.pos = UnityObjectToClipPos(v.vertex); | |||||
o.uvMain = UnityStereoScreenSpaceUVAdjust(v.texcoord, _MainTex_ST); | |||||
o.uvBase = UnityStereoScreenSpaceUVAdjust(v.texcoord, _BaseTex_ST); | |||||
#else | |||||
o.pos = mul(UNITY_MATRIX_MVP, v.vertex); | |||||
o.uvMain = v.texcoord; | |||||
o.uvBase = v.texcoord; | |||||
#endif | |||||
#if UNITY_UV_STARTS_AT_TOP | |||||
if (_BaseTex_TexelSize.y < 0.0) | |||||
o.uvBase.y = 1.0 - v.texcoord.y; | |||||
#endif | |||||
return o; | |||||
} | |||||
// | |||||
// fragment shader | |||||
// | |||||
half4 frag_prefilter(v2f_img i) : SV_Target | |||||
{ | |||||
float2 uv = i.uv + _MainTex_TexelSize.xy * _PrefilterOffs; | |||||
#if ANTI_FLICKER | |||||
float3 d = _MainTex_TexelSize.xyx * float3(1, 1, 0); | |||||
half4 s0 = SafeHDR(tex2D(_MainTex, uv)); | |||||
half3 s1 = SafeHDR(tex2D(_MainTex, uv - d.xz).rgb); | |||||
half3 s2 = SafeHDR(tex2D(_MainTex, uv + d.xz).rgb); | |||||
half3 s3 = SafeHDR(tex2D(_MainTex, uv - d.zy).rgb); | |||||
half3 s4 = SafeHDR(tex2D(_MainTex, uv + d.zy).rgb); | |||||
half3 m = Median(Median(s0.rgb, s1, s2), s3, s4); | |||||
#else | |||||
half4 s0 = SafeHDR(tex2D(_MainTex, uv)); | |||||
half3 m = s0.rgb; | |||||
#endif | |||||
#if UNITY_COLORSPACE_GAMMA | |||||
m = GammaToLinearSpace(m); | |||||
#endif | |||||
// Pixel brightness | |||||
half br = Brightness(m); | |||||
// Under-threshold part: quadratic curve | |||||
half rq = clamp(br - _Curve.x, 0, _Curve.y); | |||||
rq = _Curve.z * rq * rq; | |||||
// Combine and apply the brightness response curve. | |||||
m *= max(rq, br - _Threshold) / max(br, 1e-5); | |||||
return EncodeHDR(m); | |||||
} | |||||
half4 frag_downsample1(v2f_img i) : SV_Target | |||||
{ | |||||
#if ANTI_FLICKER | |||||
return EncodeHDR(DownsampleAntiFlickerFilter(i.uv)); | |||||
#else | |||||
return EncodeHDR(DownsampleFilter(i.uv)); | |||||
#endif | |||||
} | |||||
half4 frag_downsample2(v2f_img i) : SV_Target | |||||
{ | |||||
return EncodeHDR(DownsampleFilter(i.uv)); | |||||
} | |||||
half4 frag_upsample(v2f_multitex i) : SV_Target | |||||
{ | |||||
half3 base = DecodeHDR(tex2D(_BaseTex, i.uvBase)); | |||||
half3 blur = UpsampleFilter(i.uvMain); | |||||
return EncodeHDR(base + blur); | |||||
} | |||||
half4 frag_upsample_final(v2f_multitex i) : SV_Target | |||||
{ | |||||
half4 base = tex2D(_BaseTex, i.uvBase); | |||||
half3 blur = UpsampleFilter(i.uvMain); | |||||
#if UNITY_COLORSPACE_GAMMA | |||||
base.rgb = GammaToLinearSpace(base.rgb); | |||||
#endif | |||||
half3 cout = base.rgb + blur * _Intensity; | |||||
#if UNITY_COLORSPACE_GAMMA | |||||
cout = LinearToGammaSpace(cout); | |||||
#endif | |||||
return half4(cout, base.a); | |||||
} |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 386a80fc77a074d2ca8e6f097dd68ea3 | |||||
timeCreated: 1463538726 | |||||
licenseType: Store | |||||
ShaderImporter: | |||||
defaultTextures: [] | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,118 @@ | |||||
Shader "Hidden/Image Effects/Cinematic/Bloom" | |||||
{ | |||||
Properties | |||||
{ | |||||
_MainTex("", 2D) = "" {} | |||||
_BaseTex("", 2D) = "" {} | |||||
} | |||||
SubShader | |||||
{ | |||||
// 0: Prefilter | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#pragma multi_compile _ UNITY_COLORSPACE_GAMMA | |||||
#include "Bloom.cginc" | |||||
#pragma vertex vert | |||||
#pragma fragment frag_prefilter | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 1: Prefilter with anti-flicker | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#define ANTI_FLICKER 1 | |||||
#pragma multi_compile _ UNITY_COLORSPACE_GAMMA | |||||
#include "Bloom.cginc" | |||||
#pragma vertex vert | |||||
#pragma fragment frag_prefilter | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 2: First level downsampler | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#include "Bloom.cginc" | |||||
#pragma vertex vert | |||||
#pragma fragment frag_downsample1 | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 3: First level downsampler with anti-flicker | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#define ANTI_FLICKER 1 | |||||
#include "Bloom.cginc" | |||||
#pragma vertex vert | |||||
#pragma fragment frag_downsample1 | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 4: Second level downsampler | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#include "Bloom.cginc" | |||||
#pragma vertex vert | |||||
#pragma fragment frag_downsample2 | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 5: Upsampler | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#include "Bloom.cginc" | |||||
#pragma vertex vert_multitex | |||||
#pragma fragment frag_upsample | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 6: High quality upsampler | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#define HIGH_QUALITY 1 | |||||
#include "Bloom.cginc" | |||||
#pragma vertex vert_multitex | |||||
#pragma fragment frag_upsample | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 7: Combiner | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#pragma multi_compile _ UNITY_COLORSPACE_GAMMA | |||||
#include "Bloom.cginc" | |||||
#pragma vertex vert_multitex | |||||
#pragma fragment frag_upsample_final | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
// 8: High quality combiner | |||||
Pass | |||||
{ | |||||
ZTest Always Cull Off ZWrite Off | |||||
CGPROGRAM | |||||
#define HIGH_QUALITY 1 | |||||
#pragma multi_compile _ UNITY_COLORSPACE_GAMMA | |||||
#include "Bloom.cginc" | |||||
#pragma vertex vert_multitex | |||||
#pragma fragment frag_upsample_final | |||||
#pragma target 3.0 | |||||
ENDCG | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: e45d4f28262b04d10a075856ab5fdb5e | |||||
timeCreated: 1454052270 | |||||
licenseType: Store | |||||
ShaderImporter: | |||||
defaultTextures: [] | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 88d2f1c604c7f6d4bb80a72b2f0219a7 | |||||
folderAsset: yes | |||||
timeCreated: 1449044555 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: e862ecde714eb154ca2d86a9a0809732 | |||||
folderAsset: yes | |||||
timeCreated: 1453372226 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,62 @@ | |||||
using UnityEngine; | |||||
using UnityEditor; | |||||
using System; | |||||
using System.Linq.Expressions; | |||||
using System.Reflection; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
public static class EditorGUIHelper | |||||
{ | |||||
private static Styles s_Styles; | |||||
private class Styles | |||||
{ | |||||
public GUIStyle header = "ShurikenModuleTitle"; | |||||
public GUIStyle headerCheckbox = "ShurikenCheckMark"; | |||||
internal Styles() | |||||
{ | |||||
header.font = (new GUIStyle("Label")).font; | |||||
header.border = new RectOffset(15, 7, 4, 4); | |||||
header.fixedHeight = 22; | |||||
header.contentOffset = new Vector2(20f, -2f); | |||||
} | |||||
} | |||||
static EditorGUIHelper() | |||||
{ | |||||
s_Styles = new Styles(); | |||||
} | |||||
public static bool Header(SerializedProperty group, SerializedProperty enabledField) | |||||
{ | |||||
var display = group == null || group.isExpanded; | |||||
var enabled = enabledField != null && enabledField.boolValue; | |||||
var title = group == null ? "Unknown Group" : ObjectNames.NicifyVariableName(group.displayName); | |||||
Rect rect = GUILayoutUtility.GetRect(16f, 22f, s_Styles.header); | |||||
GUI.Box(rect, title, s_Styles.header); | |||||
Rect toggleRect = new Rect(rect.x + 4f, rect.y + 4f, 13f, 13f); | |||||
if (Event.current.type == EventType.Repaint) | |||||
s_Styles.headerCheckbox.Draw(toggleRect, false, false, enabled, false); | |||||
Event e = Event.current; | |||||
if (e.type == EventType.MouseDown) | |||||
{ | |||||
if (toggleRect.Contains(e.mousePosition) && enabledField != null) | |||||
{ | |||||
enabledField.boolValue = !enabledField.boolValue; | |||||
e.Use(); | |||||
} | |||||
else if (rect.Contains(e.mousePosition) && group != null) | |||||
{ | |||||
display = !display; | |||||
group.isExpanded = !group.isExpanded; | |||||
e.Use(); | |||||
} | |||||
} | |||||
return display; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 5b995f06a3ed14d449823cf7ab1c5a58 | |||||
timeCreated: 1454681943 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,25 @@ | |||||
using System; | |||||
using System.Linq.Expressions; | |||||
using System.Reflection; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
public static class FieldFinder<T> | |||||
{ | |||||
public static FieldInfo GetField<TValue>(Expression<Func<T, TValue>> selector) | |||||
{ | |||||
Expression body = selector; | |||||
if (body is LambdaExpression) | |||||
{ | |||||
body = ((LambdaExpression)body).Body; | |||||
} | |||||
switch (body.NodeType) | |||||
{ | |||||
case ExpressionType.MemberAccess: | |||||
return (FieldInfo)((MemberExpression)body).Member; | |||||
default: | |||||
throw new InvalidOperationException(); | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 39e54cb37a3a81a40b248f1cc25c4619 | |||||
timeCreated: 1454073160 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,29 @@ | |||||
using UnityEditor; | |||||
using UnityEngine; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
[CustomPropertyDrawer(typeof(MinAttribute))] | |||||
internal sealed class MinDrawer : PropertyDrawer | |||||
{ | |||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) | |||||
{ | |||||
MinAttribute attribute = (MinAttribute) base.attribute; | |||||
if (property.propertyType == SerializedPropertyType.Integer) | |||||
{ | |||||
int v = EditorGUI.IntField(position, label, property.intValue); | |||||
property.intValue = (int)Mathf.Max(v, attribute.min); | |||||
} | |||||
else if (property.propertyType == SerializedPropertyType.Float) | |||||
{ | |||||
float v = EditorGUI.FloatField(position, label, property.floatValue); | |||||
property.floatValue = Mathf.Max(v, attribute.min); | |||||
} | |||||
else | |||||
{ | |||||
EditorGUI.LabelField(position, label.text, "Use Min with float or int."); | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 9c615a85f13c6764fa4496d1d7f75f52 | |||||
timeCreated: 1453220014 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,63 @@ | |||||
using UnityEngine; | |||||
#if UNITY_EDITOR | |||||
using UnityEditor; | |||||
#endif | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
public static class ImageEffectHelper | |||||
{ | |||||
public static bool IsSupported(Shader s, bool needDepth, bool needHdr, MonoBehaviour effect) | |||||
{ | |||||
#if UNITY_EDITOR | |||||
// Don't check for shader compatibility while it's building as it would disable most effects | |||||
// on build farms without good-enough gaming hardware. | |||||
if (!BuildPipeline.isBuildingPlayer) | |||||
{ | |||||
#endif | |||||
if (s == null || !s.isSupported) | |||||
{ | |||||
Debug.LogWarningFormat("Missing shader for image effect {0}", effect); | |||||
return false; | |||||
} | |||||
if (!SystemInfo.supportsImageEffects || !SystemInfo.supportsRenderTextures) | |||||
{ | |||||
Debug.LogWarningFormat("Image effects aren't supported on this device ({0})", effect); | |||||
return false; | |||||
} | |||||
if (needDepth && !SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.Depth)) | |||||
{ | |||||
Debug.LogWarningFormat("Depth textures aren't supported on this device ({0})", effect); | |||||
return false; | |||||
} | |||||
if (needHdr && !SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf)) | |||||
{ | |||||
Debug.LogWarningFormat("Floating point textures aren't supported on this device ({0})", effect); | |||||
return false; | |||||
} | |||||
#if UNITY_EDITOR | |||||
} | |||||
#endif | |||||
return true; | |||||
} | |||||
public static Material CheckShaderAndCreateMaterial(Shader s) | |||||
{ | |||||
if (s == null || !s.isSupported) | |||||
return null; | |||||
var material = new Material(s); | |||||
material.hideFlags = HideFlags.DontSave; | |||||
return material; | |||||
} | |||||
public static bool supportsDX11 | |||||
{ | |||||
get { return SystemInfo.graphicsShaderLevel >= 50 && SystemInfo.supportsComputeShaders; } | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: ab6a3f50deeee984c88794eeeb901226 | |||||
timeCreated: 1448544124 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,14 @@ | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
using UnityEngine; | |||||
public sealed class MinAttribute : PropertyAttribute | |||||
{ | |||||
public readonly float min; | |||||
public MinAttribute(float min) | |||||
{ | |||||
this.min = min; | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: b07292ae638766047a6751da7552e566 | |||||
timeCreated: 1453220005 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,44 @@ | |||||
using System.Collections.Generic; | |||||
using UnityEngine; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
public class RenderTextureUtility | |||||
{ | |||||
//Temporary render texture handling | |||||
private List<RenderTexture> m_TemporaryRTs = new List<RenderTexture>(); | |||||
public RenderTexture GetTemporaryRenderTexture(int width, int height, int depthBuffer = 0, RenderTextureFormat format = RenderTextureFormat.ARGBHalf, FilterMode filterMode = FilterMode.Bilinear) | |||||
{ | |||||
var rt = RenderTexture.GetTemporary(width, height, depthBuffer, format); | |||||
rt.filterMode = filterMode; | |||||
rt.wrapMode = TextureWrapMode.Clamp; | |||||
rt.name = "RenderTextureUtilityTempTexture"; | |||||
m_TemporaryRTs.Add(rt); | |||||
return rt; | |||||
} | |||||
public void ReleaseTemporaryRenderTexture(RenderTexture rt) | |||||
{ | |||||
if (rt == null) | |||||
return; | |||||
if (!m_TemporaryRTs.Contains(rt)) | |||||
{ | |||||
Debug.LogErrorFormat("Attempting to remove texture that was not allocated: {0}", rt); | |||||
return; | |||||
} | |||||
m_TemporaryRTs.Remove(rt); | |||||
RenderTexture.ReleaseTemporary(rt); | |||||
} | |||||
public void ReleaseAllTemporaryRenderTextures() | |||||
{ | |||||
for (int i = 0; i < m_TemporaryRTs.Count; ++i) | |||||
RenderTexture.ReleaseTemporary(m_TemporaryRTs[i]); | |||||
m_TemporaryRTs.Clear(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 054e694bae00c374a97c2bc495fca66b | |||||
timeCreated: 1449148391 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 34369cdf66de04c65a8cef766bb1797b | |||||
folderAsset: yes | |||||
timeCreated: 1429220270 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,846 @@ | |||||
using UnityEngine; | |||||
using System; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
//Improvement ideas: | |||||
// Use rgba8 buffer in ldr / in some pass in hdr (in correlation to previous point and remapping coc from -1/0/1 to 0/0.5/1) | |||||
// Use temporal stabilisation | |||||
// Add a mode to do bokeh texture in quarter res as well | |||||
// Support different near and far blur for the bokeh texture | |||||
// Try distance field for the bokeh texture | |||||
// Try to separate the output of the blur pass to two rendertarget near+far, see the gain in quality vs loss in performance | |||||
// Try swirl effect on the samples of the circle blur | |||||
//References : | |||||
// This DOF implementation use ideas from public sources, a big thank to them : | |||||
// http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare | |||||
// http://www.crytek.com/download/Sousa_Graphics_Gems_CryENGINE3.pdf | |||||
// http://graphics.cs.williams.edu/papers/MedianShaderX6/ | |||||
// http://http.developer.nvidia.com/GPUGems/gpugems_ch24.html | |||||
// http://vec3.ca/bicubic-filtering-in-fewer-taps/ | |||||
[ExecuteInEditMode] | |||||
[AddComponentMenu("Image Effects/Cinematic/Depth Of Field")] | |||||
[RequireComponent(typeof(Camera))] | |||||
public class DepthOfField : MonoBehaviour | |||||
{ | |||||
private const float kMaxBlur = 40.0f; | |||||
#region Render passes | |||||
private enum Passes | |||||
{ | |||||
BlurAlphaWeighted, | |||||
BoxBlur, | |||||
DilateFgCocFromColor, | |||||
DilateFgCoc, | |||||
CaptureCocExplicit, | |||||
VisualizeCocExplicit, | |||||
CocPrefilter, | |||||
CircleBlur, | |||||
CircleBlurWithDilatedFg, | |||||
CircleBlurLowQuality, | |||||
CircleBlowLowQualityWithDilatedFg, | |||||
MergeExplicit, | |||||
ShapeLowQuality, | |||||
ShapeLowQualityDilateFg, | |||||
ShapeLowQualityMerge, | |||||
ShapeLowQualityMergeDilateFg, | |||||
ShapeMediumQuality, | |||||
ShapeMediumQualityDilateFg, | |||||
ShapeMediumQualityMerge, | |||||
ShapeMediumQualityMergeDilateFg, | |||||
ShapeHighQuality, | |||||
ShapeHighQualityDilateFg, | |||||
ShapeHighQualityMerge, | |||||
ShapeHighQualityMergeDilateFg | |||||
} | |||||
private enum MedianPasses | |||||
{ | |||||
Median3, | |||||
Median3X3 | |||||
} | |||||
private enum BokehTexturesPasses | |||||
{ | |||||
Apply, | |||||
Collect | |||||
} | |||||
#endregion | |||||
public enum TweakMode | |||||
{ | |||||
Range, | |||||
Explicit | |||||
} | |||||
public enum ApertureShape | |||||
{ | |||||
Circular, | |||||
Hexagonal, | |||||
Octogonal | |||||
} | |||||
public enum QualityPreset | |||||
{ | |||||
Low, | |||||
Medium, | |||||
High | |||||
} | |||||
public enum FilterQuality | |||||
{ | |||||
None, | |||||
Normal, | |||||
High | |||||
} | |||||
#region Settings | |||||
[Serializable] | |||||
public struct GlobalSettings | |||||
{ | |||||
[Tooltip("Allows to view where the blur will be applied. Yellow for near blur, blue for far blur.")] | |||||
public bool visualizeFocus; | |||||
[Tooltip("Setup mode. Use \"Advanced\" if you need more control on blur settings and/or want to use a bokeh texture. \"Explicit\" is the same as \"Advanced\" but makes use of \"Near Plane\" and \"Far Plane\" values instead of \"F-Stop\".")] | |||||
public TweakMode tweakMode; | |||||
[Tooltip("Quality presets. Use \"Custom\" for more advanced settings.")] | |||||
public QualityPreset filteringQuality; | |||||
[Tooltip("\"Circular\" is the fastest, followed by \"Hexagonal\" and \"Octogonal\".")] | |||||
public ApertureShape apertureShape; | |||||
[Range(0f, 179f), Tooltip("Rotates the aperture when working with \"Hexagonal\" and \"Ortogonal\".")] | |||||
public float apertureOrientation; | |||||
public static GlobalSettings defaultSettings | |||||
{ | |||||
get | |||||
{ | |||||
return new GlobalSettings | |||||
{ | |||||
visualizeFocus = false, | |||||
tweakMode = TweakMode.Range, | |||||
filteringQuality = QualityPreset.High, | |||||
apertureShape = ApertureShape.Circular, | |||||
apertureOrientation = 0f | |||||
}; | |||||
} | |||||
} | |||||
} | |||||
[Serializable] | |||||
public struct QualitySettings | |||||
{ | |||||
[Tooltip("Enable this to get smooth bokeh.")] | |||||
public bool prefilterBlur; | |||||
[Tooltip("Applies a median filter for even smoother bokeh.")] | |||||
public FilterQuality medianFilter; | |||||
[Tooltip("Dilates near blur over in focus area.")] | |||||
public bool dilateNearBlur; | |||||
public static QualitySettings[] presetQualitySettings = | |||||
{ | |||||
// Low | |||||
new QualitySettings | |||||
{ | |||||
prefilterBlur = false, | |||||
medianFilter = FilterQuality.None, | |||||
dilateNearBlur = false | |||||
}, | |||||
// Medium | |||||
new QualitySettings | |||||
{ | |||||
prefilterBlur = true, | |||||
medianFilter = FilterQuality.Normal, | |||||
dilateNearBlur = false | |||||
}, | |||||
// High | |||||
new QualitySettings | |||||
{ | |||||
prefilterBlur = true, | |||||
medianFilter = FilterQuality.High, | |||||
dilateNearBlur = true | |||||
} | |||||
}; | |||||
} | |||||
[Serializable] | |||||
public struct FocusSettings | |||||
{ | |||||
[Tooltip("Auto-focus on a selected transform.")] | |||||
public Transform transform; | |||||
[Min(0f), Tooltip("Focus distance (in world units).")] | |||||
public float focusPlane; | |||||
[Min(0.1f), Tooltip("Focus range (in world units). The focus plane is located in the center of the range.")] | |||||
public float range; | |||||
[Min(0f), Tooltip("Near focus distance (in world units).")] | |||||
public float nearPlane; | |||||
[Min(0f), Tooltip("Near blur falloff (in world units).")] | |||||
public float nearFalloff; | |||||
[Min(0f), Tooltip("Far focus distance (in world units).")] | |||||
public float farPlane; | |||||
[Min(0f), Tooltip("Far blur falloff (in world units).")] | |||||
public float farFalloff; | |||||
[Range(0f, kMaxBlur), Tooltip("Maximum blur radius for the near plane.")] | |||||
public float nearBlurRadius; | |||||
[Range(0f, kMaxBlur), Tooltip("Maximum blur radius for the far plane.")] | |||||
public float farBlurRadius; | |||||
public static FocusSettings defaultSettings | |||||
{ | |||||
get | |||||
{ | |||||
return new FocusSettings | |||||
{ | |||||
transform = null, | |||||
focusPlane = 20f, | |||||
range = 35f, | |||||
nearPlane = 2.5f, | |||||
nearFalloff = 15f, | |||||
farPlane = 37.5f, | |||||
farFalloff = 50f, | |||||
nearBlurRadius = 15f, | |||||
farBlurRadius = 20f | |||||
}; | |||||
} | |||||
} | |||||
} | |||||
[Serializable] | |||||
public struct BokehTextureSettings | |||||
{ | |||||
[Tooltip("Adding a texture to this field will enable the use of \"Bokeh Textures\". Use with care. This feature is only available on Shader Model 5 compatible-hardware and performance scale with the amount of bokeh.")] | |||||
public Texture2D texture; | |||||
[Range(0.01f, 10f), Tooltip("Maximum size of bokeh textures on screen.")] | |||||
public float scale; | |||||
[Range(0.01f, 100f), Tooltip("Bokeh brightness.")] | |||||
public float intensity; | |||||
[Range(0.01f, 5f), Tooltip("Controls the amount of bokeh textures. Lower values mean more bokeh splats.")] | |||||
public float threshold; | |||||
[Range(0.01f, 1f), Tooltip("Controls the spawn conditions. Lower values mean more visible bokeh.")] | |||||
public float spawnHeuristic; | |||||
public static BokehTextureSettings defaultSettings | |||||
{ | |||||
get | |||||
{ | |||||
return new BokehTextureSettings | |||||
{ | |||||
texture = null, | |||||
scale = 1f, | |||||
intensity = 50f, | |||||
threshold = 2f, | |||||
spawnHeuristic = 0.15f | |||||
}; | |||||
} | |||||
} | |||||
} | |||||
#endregion | |||||
public GlobalSettings settings = GlobalSettings.defaultSettings; | |||||
public FocusSettings focus = FocusSettings.defaultSettings; | |||||
public BokehTextureSettings bokehTexture = BokehTextureSettings.defaultSettings; | |||||
[SerializeField] | |||||
private Shader m_FilmicDepthOfFieldShader; | |||||
public Shader filmicDepthOfFieldShader | |||||
{ | |||||
get | |||||
{ | |||||
if (m_FilmicDepthOfFieldShader == null) | |||||
m_FilmicDepthOfFieldShader = Shader.Find("Hidden/DepthOfField/DepthOfField"); | |||||
return m_FilmicDepthOfFieldShader; | |||||
} | |||||
} | |||||
[SerializeField] | |||||
private Shader m_MedianFilterShader; | |||||
public Shader medianFilterShader | |||||
{ | |||||
get | |||||
{ | |||||
if (m_MedianFilterShader == null) | |||||
m_MedianFilterShader = Shader.Find("Hidden/DepthOfField/MedianFilter"); | |||||
return m_MedianFilterShader; | |||||
} | |||||
} | |||||
[SerializeField] | |||||
private Shader m_TextureBokehShader; | |||||
public Shader textureBokehShader | |||||
{ | |||||
get | |||||
{ | |||||
if (m_TextureBokehShader == null) | |||||
m_TextureBokehShader = Shader.Find("Hidden/DepthOfField/BokehSplatting"); | |||||
return m_TextureBokehShader; | |||||
} | |||||
} | |||||
private RenderTextureUtility m_RTU = new RenderTextureUtility(); | |||||
private Material m_FilmicDepthOfFieldMaterial; | |||||
public Material filmicDepthOfFieldMaterial | |||||
{ | |||||
get | |||||
{ | |||||
if (m_FilmicDepthOfFieldMaterial == null) | |||||
m_FilmicDepthOfFieldMaterial = ImageEffectHelper.CheckShaderAndCreateMaterial(filmicDepthOfFieldShader); | |||||
return m_FilmicDepthOfFieldMaterial; | |||||
} | |||||
} | |||||
private Material m_MedianFilterMaterial; | |||||
public Material medianFilterMaterial | |||||
{ | |||||
get | |||||
{ | |||||
if (m_MedianFilterMaterial == null) | |||||
m_MedianFilterMaterial = ImageEffectHelper.CheckShaderAndCreateMaterial(medianFilterShader); | |||||
return m_MedianFilterMaterial; | |||||
} | |||||
} | |||||
private Material m_TextureBokehMaterial; | |||||
public Material textureBokehMaterial | |||||
{ | |||||
get | |||||
{ | |||||
if (m_TextureBokehMaterial == null) | |||||
m_TextureBokehMaterial = ImageEffectHelper.CheckShaderAndCreateMaterial(textureBokehShader); | |||||
return m_TextureBokehMaterial; | |||||
} | |||||
} | |||||
private ComputeBuffer m_ComputeBufferDrawArgs; | |||||
public ComputeBuffer computeBufferDrawArgs | |||||
{ | |||||
get | |||||
{ | |||||
if (m_ComputeBufferDrawArgs == null) | |||||
{ | |||||
#if UNITY_5_4_OR_NEWER | |||||
m_ComputeBufferDrawArgs = new ComputeBuffer(1, 16, ComputeBufferType.IndirectArguments); | |||||
#else | |||||
m_ComputeBufferDrawArgs = new ComputeBuffer(1, 16, ComputeBufferType.DrawIndirect); | |||||
#endif | |||||
m_ComputeBufferDrawArgs.SetData(new[] {0, 1, 0, 0}); | |||||
} | |||||
return m_ComputeBufferDrawArgs; | |||||
} | |||||
} | |||||
private ComputeBuffer m_ComputeBufferPoints; | |||||
public ComputeBuffer computeBufferPoints | |||||
{ | |||||
get | |||||
{ | |||||
if (m_ComputeBufferPoints == null) | |||||
m_ComputeBufferPoints = new ComputeBuffer(90000, 12 + 16, ComputeBufferType.Append); | |||||
return m_ComputeBufferPoints; | |||||
} | |||||
} | |||||
private QualitySettings m_CurrentQualitySettings; | |||||
private float m_LastApertureOrientation; | |||||
private Vector4 m_OctogonalBokehDirection1; | |||||
private Vector4 m_OctogonalBokehDirection2; | |||||
private Vector4 m_OctogonalBokehDirection3; | |||||
private Vector4 m_OctogonalBokehDirection4; | |||||
private Vector4 m_HexagonalBokehDirection1; | |||||
private Vector4 m_HexagonalBokehDirection2; | |||||
private Vector4 m_HexagonalBokehDirection3; | |||||
private void OnEnable() | |||||
{ | |||||
if (!ImageEffectHelper.IsSupported(filmicDepthOfFieldShader, true, true, this) || !ImageEffectHelper.IsSupported(medianFilterShader, true, true, this)) | |||||
{ | |||||
enabled = false; | |||||
return; | |||||
} | |||||
if (ImageEffectHelper.supportsDX11 && !ImageEffectHelper.IsSupported(textureBokehShader, true, true, this)) | |||||
{ | |||||
enabled = false; | |||||
return; | |||||
} | |||||
ComputeBlurDirections(true); | |||||
GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth; | |||||
} | |||||
private void OnDisable() | |||||
{ | |||||
ReleaseComputeResources(); | |||||
if (m_FilmicDepthOfFieldMaterial != null) | |||||
DestroyImmediate(m_FilmicDepthOfFieldMaterial); | |||||
if (m_TextureBokehMaterial != null) | |||||
DestroyImmediate(m_TextureBokehMaterial); | |||||
if (m_MedianFilterMaterial != null) | |||||
DestroyImmediate(m_MedianFilterMaterial); | |||||
m_FilmicDepthOfFieldMaterial = null; | |||||
m_TextureBokehMaterial = null; | |||||
m_MedianFilterMaterial = null; | |||||
m_RTU.ReleaseAllTemporaryRenderTextures(); | |||||
} | |||||
//-------------------------------------------------------------------// | |||||
// Main entry point // | |||||
//-------------------------------------------------------------------// | |||||
private void OnRenderImage(RenderTexture source, RenderTexture destination) | |||||
{ | |||||
if (medianFilterMaterial == null || filmicDepthOfFieldMaterial == null) | |||||
{ | |||||
Graphics.Blit(source, destination); | |||||
return; | |||||
} | |||||
if (settings.visualizeFocus) | |||||
{ | |||||
Vector4 blurrinessParam; | |||||
Vector4 blurrinessCoe; | |||||
ComputeCocParameters(out blurrinessParam, out blurrinessCoe); | |||||
filmicDepthOfFieldMaterial.SetVector("_BlurParams", blurrinessParam); | |||||
filmicDepthOfFieldMaterial.SetVector("_BlurCoe", blurrinessCoe); | |||||
Graphics.Blit(null, destination, filmicDepthOfFieldMaterial, (int)Passes.VisualizeCocExplicit); | |||||
} | |||||
else | |||||
{ | |||||
DoDepthOfField(source, destination); | |||||
} | |||||
m_RTU.ReleaseAllTemporaryRenderTextures(); | |||||
} | |||||
private void DoDepthOfField(RenderTexture source, RenderTexture destination) | |||||
{ | |||||
m_CurrentQualitySettings = QualitySettings.presetQualitySettings[(int)settings.filteringQuality]; | |||||
float radiusAdjustement = source.height / 720f; | |||||
float textureBokehScale = radiusAdjustement; | |||||
float textureBokehMaxRadius = Mathf.Max(focus.nearBlurRadius, focus.farBlurRadius) * textureBokehScale * 0.75f; | |||||
float nearBlurRadius = focus.nearBlurRadius * radiusAdjustement; | |||||
float farBlurRadius = focus.farBlurRadius * radiusAdjustement; | |||||
float maxBlurRadius = Mathf.Max(nearBlurRadius, farBlurRadius); | |||||
switch (settings.apertureShape) | |||||
{ | |||||
case ApertureShape.Hexagonal: | |||||
maxBlurRadius *= 1.2f; | |||||
break; | |||||
case ApertureShape.Octogonal: | |||||
maxBlurRadius *= 1.15f; | |||||
break; | |||||
} | |||||
if (maxBlurRadius < 0.5f) | |||||
{ | |||||
Graphics.Blit(source, destination); | |||||
return; | |||||
} | |||||
// Quarter resolution | |||||
int rtW = source.width / 2; | |||||
int rtH = source.height / 2; | |||||
var blurrinessCoe = new Vector4(nearBlurRadius * 0.5f, farBlurRadius * 0.5f, 0f, 0f); | |||||
var colorAndCoc = m_RTU.GetTemporaryRenderTexture(rtW, rtH); | |||||
var colorAndCoc2 = m_RTU.GetTemporaryRenderTexture(rtW, rtH); | |||||
// Downsample to Color + COC buffer | |||||
Vector4 cocParam; | |||||
Vector4 cocCoe; | |||||
ComputeCocParameters(out cocParam, out cocCoe); | |||||
filmicDepthOfFieldMaterial.SetVector("_BlurParams", cocParam); | |||||
filmicDepthOfFieldMaterial.SetVector("_BlurCoe", cocCoe); | |||||
Graphics.Blit(source, colorAndCoc2, filmicDepthOfFieldMaterial, (int)Passes.CaptureCocExplicit); | |||||
var src = colorAndCoc2; | |||||
var dst = colorAndCoc; | |||||
// Collect texture bokeh candidates and replace with a darker pixel | |||||
if (shouldPerformBokeh) | |||||
{ | |||||
// Blur a bit so we can do a frequency check | |||||
var blurred = m_RTU.GetTemporaryRenderTexture(rtW, rtH); | |||||
Graphics.Blit(src, blurred, filmicDepthOfFieldMaterial, (int)Passes.BoxBlur); | |||||
filmicDepthOfFieldMaterial.SetVector("_Offsets", new Vector4(0f, 1.5f, 0f, 1.5f)); | |||||
Graphics.Blit(blurred, dst, filmicDepthOfFieldMaterial, (int)Passes.BlurAlphaWeighted); | |||||
filmicDepthOfFieldMaterial.SetVector("_Offsets", new Vector4(1.5f, 0f, 0f, 1.5f)); | |||||
Graphics.Blit(dst, blurred, filmicDepthOfFieldMaterial, (int)Passes.BlurAlphaWeighted); | |||||
// Collect texture bokeh candidates and replace with a darker pixel | |||||
textureBokehMaterial.SetTexture("_BlurredColor", blurred); | |||||
textureBokehMaterial.SetFloat("_SpawnHeuristic", bokehTexture.spawnHeuristic); | |||||
textureBokehMaterial.SetVector("_BokehParams", new Vector4(bokehTexture.scale * textureBokehScale, bokehTexture.intensity, bokehTexture.threshold, textureBokehMaxRadius)); | |||||
Graphics.SetRandomWriteTarget(1, computeBufferPoints); | |||||
Graphics.Blit(src, dst, textureBokehMaterial, (int)BokehTexturesPasses.Collect); | |||||
Graphics.ClearRandomWriteTargets(); | |||||
SwapRenderTexture(ref src, ref dst); | |||||
m_RTU.ReleaseTemporaryRenderTexture(blurred); | |||||
} | |||||
filmicDepthOfFieldMaterial.SetVector("_BlurParams", cocParam); | |||||
filmicDepthOfFieldMaterial.SetVector("_BlurCoe", blurrinessCoe); | |||||
// Dilate near blur factor | |||||
RenderTexture blurredFgCoc = null; | |||||
if (m_CurrentQualitySettings.dilateNearBlur) | |||||
{ | |||||
var blurredFgCoc2 = m_RTU.GetTemporaryRenderTexture(rtW, rtH, 0, RenderTextureFormat.RGHalf); | |||||
blurredFgCoc = m_RTU.GetTemporaryRenderTexture(rtW, rtH, 0, RenderTextureFormat.RGHalf); | |||||
filmicDepthOfFieldMaterial.SetVector("_Offsets", new Vector4(0f, nearBlurRadius * 0.75f, 0f, 0f)); | |||||
Graphics.Blit(src, blurredFgCoc2, filmicDepthOfFieldMaterial, (int)Passes.DilateFgCocFromColor); | |||||
filmicDepthOfFieldMaterial.SetVector("_Offsets", new Vector4(nearBlurRadius * 0.75f, 0f, 0f, 0f)); | |||||
Graphics.Blit(blurredFgCoc2, blurredFgCoc, filmicDepthOfFieldMaterial, (int)Passes.DilateFgCoc); | |||||
m_RTU.ReleaseTemporaryRenderTexture(blurredFgCoc2); | |||||
blurredFgCoc.filterMode = FilterMode.Point; | |||||
} | |||||
// Blur downsampled color to fill the gap between samples | |||||
if (m_CurrentQualitySettings.prefilterBlur) | |||||
{ | |||||
Graphics.Blit(src, dst, filmicDepthOfFieldMaterial, (int)Passes.CocPrefilter); | |||||
SwapRenderTexture(ref src, ref dst); | |||||
} | |||||
// Apply blur : Circle / Hexagonal or Octagonal (blur will create bokeh if bright pixel where not removed by "m_UseBokehTexture") | |||||
switch (settings.apertureShape) | |||||
{ | |||||
case ApertureShape.Circular: | |||||
DoCircularBlur(blurredFgCoc, ref src, ref dst, maxBlurRadius); | |||||
break; | |||||
case ApertureShape.Hexagonal: | |||||
DoHexagonalBlur(blurredFgCoc, ref src, ref dst, maxBlurRadius); | |||||
break; | |||||
case ApertureShape.Octogonal: | |||||
DoOctogonalBlur(blurredFgCoc, ref src, ref dst, maxBlurRadius); | |||||
break; | |||||
} | |||||
// Smooth result | |||||
switch (m_CurrentQualitySettings.medianFilter) | |||||
{ | |||||
case FilterQuality.Normal: | |||||
{ | |||||
medianFilterMaterial.SetVector("_Offsets", new Vector4(1f, 0f, 0f, 0f)); | |||||
Graphics.Blit(src, dst, medianFilterMaterial, (int)MedianPasses.Median3); | |||||
SwapRenderTexture(ref src, ref dst); | |||||
medianFilterMaterial.SetVector("_Offsets", new Vector4(0f, 1f, 0f, 0f)); | |||||
Graphics.Blit(src, dst, medianFilterMaterial, (int)MedianPasses.Median3); | |||||
SwapRenderTexture(ref src, ref dst); | |||||
break; | |||||
} | |||||
case FilterQuality.High: | |||||
{ | |||||
Graphics.Blit(src, dst, medianFilterMaterial, (int)MedianPasses.Median3X3); | |||||
SwapRenderTexture(ref src, ref dst); | |||||
break; | |||||
} | |||||
} | |||||
// Merge to full resolution (with boost) + upsampling (linear or bicubic) | |||||
filmicDepthOfFieldMaterial.SetVector("_BlurCoe", blurrinessCoe); | |||||
filmicDepthOfFieldMaterial.SetVector("_Convolved_TexelSize", new Vector4(src.width, src.height, 1f / src.width, 1f / src.height)); | |||||
filmicDepthOfFieldMaterial.SetTexture("_SecondTex", src); | |||||
int mergePass = (int)Passes.MergeExplicit; | |||||
// Apply texture bokeh | |||||
if (shouldPerformBokeh) | |||||
{ | |||||
var tmp = m_RTU.GetTemporaryRenderTexture(source.height, source.width, 0, source.format); | |||||
Graphics.Blit(source, tmp, filmicDepthOfFieldMaterial, mergePass); | |||||
Graphics.SetRenderTarget(tmp); | |||||
ComputeBuffer.CopyCount(computeBufferPoints, computeBufferDrawArgs, 0); | |||||
textureBokehMaterial.SetBuffer("pointBuffer", computeBufferPoints); | |||||
textureBokehMaterial.SetTexture("_MainTex", bokehTexture.texture); | |||||
textureBokehMaterial.SetVector("_Screen", new Vector3(1f / (1f * source.width), 1f / (1f * source.height), textureBokehMaxRadius)); | |||||
textureBokehMaterial.SetPass((int)BokehTexturesPasses.Apply); | |||||
Graphics.DrawProceduralIndirect(MeshTopology.Points, computeBufferDrawArgs, 0); | |||||
Graphics.Blit(tmp, destination); // Hackaround for DX11 flipfun (OPTIMIZEME) | |||||
} | |||||
else | |||||
{ | |||||
Graphics.Blit(source, destination, filmicDepthOfFieldMaterial, mergePass); | |||||
} | |||||
} | |||||
//-------------------------------------------------------------------// | |||||
// Blurs // | |||||
//-------------------------------------------------------------------// | |||||
private void DoHexagonalBlur(RenderTexture blurredFgCoc, ref RenderTexture src, ref RenderTexture dst, float maxRadius) | |||||
{ | |||||
ComputeBlurDirections(false); | |||||
int blurPass; | |||||
int blurPassMerge; | |||||
GetDirectionalBlurPassesFromRadius(blurredFgCoc, maxRadius, out blurPass, out blurPassMerge); | |||||
filmicDepthOfFieldMaterial.SetTexture("_SecondTex", blurredFgCoc); | |||||
var tmp = m_RTU.GetTemporaryRenderTexture(src.width, src.height, 0, src.format); | |||||
filmicDepthOfFieldMaterial.SetVector("_Offsets", m_HexagonalBokehDirection1); | |||||
Graphics.Blit(src, tmp, filmicDepthOfFieldMaterial, blurPass); | |||||
filmicDepthOfFieldMaterial.SetVector("_Offsets", m_HexagonalBokehDirection2); | |||||
Graphics.Blit(tmp, src, filmicDepthOfFieldMaterial, blurPass); | |||||
filmicDepthOfFieldMaterial.SetVector("_Offsets", m_HexagonalBokehDirection3); | |||||
filmicDepthOfFieldMaterial.SetTexture("_ThirdTex", src); | |||||
Graphics.Blit(tmp, dst, filmicDepthOfFieldMaterial, blurPassMerge); | |||||
m_RTU.ReleaseTemporaryRenderTexture(tmp); | |||||
SwapRenderTexture(ref src, ref dst); | |||||
} | |||||
private void DoOctogonalBlur(RenderTexture blurredFgCoc, ref RenderTexture src, ref RenderTexture dst, float maxRadius) | |||||
{ | |||||
ComputeBlurDirections(false); | |||||
int blurPass; | |||||
int blurPassMerge; | |||||
GetDirectionalBlurPassesFromRadius(blurredFgCoc, maxRadius, out blurPass, out blurPassMerge); | |||||
filmicDepthOfFieldMaterial.SetTexture("_SecondTex", blurredFgCoc); | |||||
var tmp = m_RTU.GetTemporaryRenderTexture(src.width, src.height, 0, src.format); | |||||
filmicDepthOfFieldMaterial.SetVector("_Offsets", m_OctogonalBokehDirection1); | |||||
Graphics.Blit(src, tmp, filmicDepthOfFieldMaterial, blurPass); | |||||
filmicDepthOfFieldMaterial.SetVector("_Offsets", m_OctogonalBokehDirection2); | |||||
Graphics.Blit(tmp, dst, filmicDepthOfFieldMaterial, blurPass); | |||||
filmicDepthOfFieldMaterial.SetVector("_Offsets", m_OctogonalBokehDirection3); | |||||
Graphics.Blit(src, tmp, filmicDepthOfFieldMaterial, blurPass); | |||||
filmicDepthOfFieldMaterial.SetVector("_Offsets", m_OctogonalBokehDirection4); | |||||
filmicDepthOfFieldMaterial.SetTexture("_ThirdTex", dst); | |||||
Graphics.Blit(tmp, src, filmicDepthOfFieldMaterial, blurPassMerge); | |||||
m_RTU.ReleaseTemporaryRenderTexture(tmp); | |||||
} | |||||
private void DoCircularBlur(RenderTexture blurredFgCoc, ref RenderTexture src, ref RenderTexture dst, float maxRadius) | |||||
{ | |||||
int bokehPass; | |||||
if (blurredFgCoc != null) | |||||
{ | |||||
filmicDepthOfFieldMaterial.SetTexture("_SecondTex", blurredFgCoc); | |||||
bokehPass = (maxRadius > 10f) ? (int)Passes.CircleBlurWithDilatedFg : (int)Passes.CircleBlowLowQualityWithDilatedFg; | |||||
} | |||||
else | |||||
{ | |||||
bokehPass = (maxRadius > 10f) ? (int)Passes.CircleBlur : (int)Passes.CircleBlurLowQuality; | |||||
} | |||||
Graphics.Blit(src, dst, filmicDepthOfFieldMaterial, bokehPass); | |||||
SwapRenderTexture(ref src, ref dst); | |||||
} | |||||
//-------------------------------------------------------------------// | |||||
// Helpers // | |||||
//-------------------------------------------------------------------// | |||||
private void ComputeCocParameters(out Vector4 blurParams, out Vector4 blurCoe) | |||||
{ | |||||
var sceneCamera = GetComponent<Camera>(); | |||||
float focusDistance; | |||||
float nearFalloff = focus.nearFalloff * 2f; | |||||
float farFalloff = focus.farFalloff * 2f; | |||||
float nearPlane = focus.nearPlane; | |||||
float farPlane = focus.farPlane; | |||||
if (settings.tweakMode == TweakMode.Range) | |||||
{ | |||||
if (focus.transform != null) | |||||
focusDistance = sceneCamera.WorldToViewportPoint(focus.transform.position).z; | |||||
else | |||||
focusDistance = focus.focusPlane; | |||||
float s = focus.range * 0.5f; | |||||
nearPlane = focusDistance - s; | |||||
farPlane = focusDistance + s; | |||||
} | |||||
nearPlane -= (nearFalloff * 0.5f); | |||||
farPlane += (farFalloff * 0.5f); | |||||
focusDistance = (nearPlane + farPlane) * 0.5f; | |||||
float focusDistance01 = focusDistance / sceneCamera.farClipPlane; | |||||
float nearDistance01 = nearPlane / sceneCamera.farClipPlane; | |||||
float farDistance01 = farPlane / sceneCamera.farClipPlane; | |||||
var dof = farPlane - nearPlane; | |||||
var dof01 = farDistance01 - nearDistance01; | |||||
var nearFalloff01 = nearFalloff / dof; | |||||
var farFalloff01 = farFalloff / dof; | |||||
float nearFocusRange01 = (1f - nearFalloff01) * (dof01 * 0.5f); | |||||
float farFocusRange01 = (1f - farFalloff01) * (dof01 * 0.5f); | |||||
if (focusDistance01 <= nearDistance01) | |||||
focusDistance01 = nearDistance01 + 1e-6f; | |||||
if (focusDistance01 >= farDistance01) | |||||
focusDistance01 = farDistance01 - 1e-6f; | |||||
if ((focusDistance01 - nearFocusRange01) <= nearDistance01) | |||||
nearFocusRange01 = focusDistance01 - nearDistance01 - 1e-6f; | |||||
if ((focusDistance01 + farFocusRange01) >= farDistance01) | |||||
farFocusRange01 = farDistance01 - focusDistance01 - 1e-6f; | |||||
float a1 = 1f / (nearDistance01 - focusDistance01 + nearFocusRange01); | |||||
float a2 = 1f / (farDistance01 - focusDistance01 - farFocusRange01); | |||||
float b1 = 1f - a1 * nearDistance01; | |||||
float b2 = 1f - a2 * farDistance01; | |||||
const float c1 = -1f; | |||||
const float c2 = 1f; | |||||
blurParams = new Vector4(c1 * a1, c1 * b1, c2 * a2, c2 * b2); | |||||
blurCoe = new Vector4(0f, 0f, (b2 - b1) / (a1 - a2), 0f); | |||||
// Save values so we can switch from one tweak mode to the other on the fly | |||||
focus.nearPlane = nearPlane + (nearFalloff * 0.5f); | |||||
focus.farPlane = farPlane - (farFalloff * 0.5f); | |||||
focus.focusPlane = (focus.nearPlane + focus.farPlane) * 0.5f; | |||||
focus.range = focus.farPlane - focus.nearPlane; | |||||
} | |||||
private void ReleaseComputeResources() | |||||
{ | |||||
if (m_ComputeBufferDrawArgs != null) | |||||
m_ComputeBufferDrawArgs.Release(); | |||||
if (m_ComputeBufferPoints != null) | |||||
m_ComputeBufferPoints.Release(); | |||||
m_ComputeBufferDrawArgs = null; | |||||
m_ComputeBufferPoints = null; | |||||
} | |||||
private void ComputeBlurDirections(bool force) | |||||
{ | |||||
if (!force && Math.Abs(m_LastApertureOrientation - settings.apertureOrientation) < float.Epsilon) | |||||
return; | |||||
m_LastApertureOrientation = settings.apertureOrientation; | |||||
float rotationRadian = settings.apertureOrientation * Mathf.Deg2Rad; | |||||
float cosinus = Mathf.Cos(rotationRadian); | |||||
float sinus = Mathf.Sin(rotationRadian); | |||||
m_OctogonalBokehDirection1 = new Vector4(0.5f, 0f, 0f, 0f); | |||||
m_OctogonalBokehDirection2 = new Vector4(0f, 0.5f, 1f, 0f); | |||||
m_OctogonalBokehDirection3 = new Vector4(-0.353553f, 0.353553f, 1f, 0f); | |||||
m_OctogonalBokehDirection4 = new Vector4(0.353553f, 0.353553f, 1f, 0f); | |||||
m_HexagonalBokehDirection1 = new Vector4(0.5f, 0f, 0f, 0f); | |||||
m_HexagonalBokehDirection2 = new Vector4(0.25f, 0.433013f, 1f, 0f); | |||||
m_HexagonalBokehDirection3 = new Vector4(0.25f, -0.433013f, 1f, 0f); | |||||
if (rotationRadian > float.Epsilon) | |||||
{ | |||||
Rotate2D(ref m_OctogonalBokehDirection1, cosinus, sinus); | |||||
Rotate2D(ref m_OctogonalBokehDirection2, cosinus, sinus); | |||||
Rotate2D(ref m_OctogonalBokehDirection3, cosinus, sinus); | |||||
Rotate2D(ref m_OctogonalBokehDirection4, cosinus, sinus); | |||||
Rotate2D(ref m_HexagonalBokehDirection1, cosinus, sinus); | |||||
Rotate2D(ref m_HexagonalBokehDirection2, cosinus, sinus); | |||||
Rotate2D(ref m_HexagonalBokehDirection3, cosinus, sinus); | |||||
} | |||||
} | |||||
private bool shouldPerformBokeh | |||||
{ | |||||
get { return ImageEffectHelper.supportsDX11 && bokehTexture.texture != null && textureBokehMaterial; } | |||||
} | |||||
private static void Rotate2D(ref Vector4 direction, float cosinus, float sinus) | |||||
{ | |||||
var source = direction; | |||||
direction.x = source.x * cosinus - source.y * sinus; | |||||
direction.y = source.x * sinus + source.y * cosinus; | |||||
} | |||||
private static void SwapRenderTexture(ref RenderTexture src, ref RenderTexture dst) | |||||
{ | |||||
RenderTexture tmp = dst; | |||||
dst = src; | |||||
src = tmp; | |||||
} | |||||
private static void GetDirectionalBlurPassesFromRadius(RenderTexture blurredFgCoc, float maxRadius, out int blurPass, out int blurAndMergePass) | |||||
{ | |||||
if (blurredFgCoc == null) | |||||
{ | |||||
if (maxRadius > 10f) | |||||
{ | |||||
blurPass = (int)Passes.ShapeHighQuality; | |||||
blurAndMergePass = (int)Passes.ShapeHighQualityMerge; | |||||
} | |||||
else if (maxRadius > 5f) | |||||
{ | |||||
blurPass = (int)Passes.ShapeMediumQuality; | |||||
blurAndMergePass = (int)Passes.ShapeMediumQualityMerge; | |||||
} | |||||
else | |||||
{ | |||||
blurPass = (int)Passes.ShapeLowQuality; | |||||
blurAndMergePass = (int)Passes.ShapeLowQualityMerge; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
if (maxRadius > 10f) | |||||
{ | |||||
blurPass = (int)Passes.ShapeHighQualityDilateFg; | |||||
blurAndMergePass = (int)Passes.ShapeHighQualityMergeDilateFg; | |||||
} | |||||
else if (maxRadius > 5f) | |||||
{ | |||||
blurPass = (int)Passes.ShapeMediumQualityDilateFg; | |||||
blurAndMergePass = (int)Passes.ShapeMediumQualityMergeDilateFg; | |||||
} | |||||
else | |||||
{ | |||||
blurPass = (int)Passes.ShapeLowQualityDilateFg; | |||||
blurAndMergePass = (int)Passes.ShapeLowQualityMergeDilateFg; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,18 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 8a338d679868f45439ea43c7ae035e36 | |||||
timeCreated: 1453985420 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: | |||||
- m_FilmicDepthOfFieldShader: {fileID: 4800000, guid: bff0e5458fb914f5c9985ba4f09171d2, | |||||
type: 3} | |||||
- m_MedianFilterShader: {fileID: 4800000, guid: a058b22d4123add4b807e832604e5e7b, | |||||
type: 3} | |||||
- m_TextureBokehShader: {fileID: 4800000, guid: c961b8ed1f00f924d804ada5143bd0e8, | |||||
type: 3} | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 1a8273952ce5743428d8c42b25cb0458 | |||||
folderAsset: yes | |||||
timeCreated: 1449046242 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,139 @@ | |||||
using UnityEditor; | |||||
using UnityEngine; | |||||
namespace UnityStandardAssets.CinematicEffects | |||||
{ | |||||
[CustomEditor(typeof(DepthOfField))] | |||||
class DepthOfFieldEditor : Editor | |||||
{ | |||||
private SerializedProperty m_VisualizeFocus; | |||||
private SerializedProperty m_TweakMode; | |||||
private SerializedProperty m_FilteringQuality; | |||||
private SerializedProperty m_ApertureShape; | |||||
private SerializedProperty m_ApertureOrientation; | |||||
private SerializedProperty m_Transform; | |||||
private SerializedProperty m_FocusPlane; | |||||
private SerializedProperty m_Range; | |||||
private SerializedProperty m_NearPlane; | |||||
private SerializedProperty m_NearFalloff; | |||||
private SerializedProperty m_FarPlane; | |||||
private SerializedProperty m_FarFalloff; | |||||
private SerializedProperty m_NearBlurRadius; | |||||
private SerializedProperty m_FarBlurRadius; | |||||
private SerializedProperty m_Texture; | |||||
private SerializedProperty m_Scale; | |||||
private SerializedProperty m_Intensity; | |||||
private SerializedProperty m_Threshold; | |||||
private SerializedProperty m_SpawnHeuristic; | |||||
private void OnEnable() | |||||
{ | |||||
var o = serializedObject; | |||||
m_VisualizeFocus = o.FindProperty("settings.visualizeFocus"); | |||||
m_TweakMode = o.FindProperty("settings.tweakMode"); | |||||
m_FilteringQuality = o.FindProperty("settings.filteringQuality"); | |||||
m_ApertureShape = o.FindProperty("settings.apertureShape"); | |||||
m_ApertureOrientation = o.FindProperty("settings.apertureOrientation"); | |||||
m_Transform = o.FindProperty("focus.transform"); | |||||
m_FocusPlane = o.FindProperty("focus.focusPlane"); | |||||
m_Range = o.FindProperty("focus.range"); | |||||
m_NearPlane = o.FindProperty("focus.nearPlane"); | |||||
m_NearFalloff = o.FindProperty("focus.nearFalloff"); | |||||
m_FarPlane = o.FindProperty("focus.farPlane"); | |||||
m_FarFalloff = o.FindProperty("focus.farFalloff"); | |||||
m_NearBlurRadius = o.FindProperty("focus.nearBlurRadius"); | |||||
m_FarBlurRadius = o.FindProperty("focus.farBlurRadius"); | |||||
m_Texture = o.FindProperty("bokehTexture.texture"); | |||||
m_Scale = o.FindProperty("bokehTexture.scale"); | |||||
m_Intensity = o.FindProperty("bokehTexture.intensity"); | |||||
m_Threshold = o.FindProperty("bokehTexture.threshold"); | |||||
m_SpawnHeuristic = o.FindProperty("bokehTexture.spawnHeuristic"); | |||||
} | |||||
public override void OnInspectorGUI() | |||||
{ | |||||
serializedObject.Update(); | |||||
EditorGUILayout.PropertyField(m_VisualizeFocus); | |||||
EditorGUILayout.PropertyField(m_TweakMode); | |||||
EditorGUILayout.PropertyField(m_FilteringQuality); | |||||
EditorGUILayout.PropertyField(m_ApertureShape); | |||||
if (m_ApertureShape.intValue != (int)DepthOfField.ApertureShape.Circular) | |||||
{ | |||||
EditorGUI.indentLevel++; | |||||
EditorGUILayout.PropertyField(m_ApertureOrientation, new GUIContent("Orientation")); | |||||
EditorGUI.indentLevel--; | |||||
} | |||||
EditorGUILayout.Space(); | |||||
EditorGUILayout.LabelField("Focus", EditorStyles.boldLabel); | |||||
var falloff = new GUIContent("Falloff"); | |||||
var blurRadius = new GUIContent("Blur Radius"); | |||||
EditorGUI.indentLevel++; | |||||
if (m_TweakMode.intValue == (int)DepthOfField.TweakMode.Range) | |||||
{ | |||||
EditorGUILayout.PropertyField(m_Transform); | |||||
using (new EditorGUI.DisabledGroupScope(m_Transform.objectReferenceValue != null)) | |||||
{ | |||||
EditorGUILayout.PropertyField(m_FocusPlane); | |||||
} | |||||
EditorGUILayout.PropertyField(m_Range); | |||||
EditorGUILayout.LabelField(m_NearPlane.displayName); | |||||
EditorGUI.indentLevel++; | |||||
EditorGUILayout.PropertyField(m_NearFalloff, falloff); | |||||
EditorGUILayout.PropertyField(m_NearBlurRadius, blurRadius); | |||||
EditorGUI.indentLevel--; | |||||
EditorGUILayout.LabelField(m_FarPlane.displayName); | |||||
EditorGUI.indentLevel++; | |||||
EditorGUILayout.PropertyField(m_FarFalloff, falloff); | |||||
EditorGUILayout.PropertyField(m_FarBlurRadius, blurRadius); | |||||
EditorGUI.indentLevel--; | |||||
} | |||||
else | |||||
{ | |||||
EditorGUILayout.PropertyField(m_NearPlane); | |||||
EditorGUI.indentLevel++; | |||||
EditorGUILayout.PropertyField(m_NearFalloff, falloff); | |||||
EditorGUILayout.PropertyField(m_NearBlurRadius, blurRadius); | |||||
EditorGUI.indentLevel--; | |||||
EditorGUILayout.PropertyField(m_FarPlane); | |||||
EditorGUI.indentLevel++; | |||||
EditorGUILayout.PropertyField(m_FarFalloff, falloff); | |||||
EditorGUILayout.PropertyField(m_FarBlurRadius, blurRadius); | |||||
EditorGUI.indentLevel--; | |||||
} | |||||
EditorGUI.indentLevel--; | |||||
EditorGUILayout.Space(); | |||||
EditorGUILayout.LabelField("Bokeh", EditorStyles.boldLabel); | |||||
EditorGUI.indentLevel++; | |||||
EditorGUILayout.PropertyField(m_Texture); | |||||
if (m_Texture.objectReferenceValue != null) | |||||
{ | |||||
EditorGUILayout.PropertyField(m_Scale); | |||||
EditorGUILayout.PropertyField(m_Intensity); | |||||
EditorGUILayout.PropertyField(m_Threshold); | |||||
EditorGUILayout.PropertyField(m_SpawnHeuristic); | |||||
} | |||||
EditorGUI.indentLevel--; | |||||
serializedObject.ApplyModifiedProperties(); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 3eb07ef5681934662814cc768bcdb60c | |||||
timeCreated: 1429220270 | |||||
licenseType: Store | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,9 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 5427d9a5126366c42b9509de8233bff7 | |||||
folderAsset: yes | |||||
timeCreated: 1453985653 | |||||
licenseType: Store | |||||
DefaultImporter: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@ -0,0 +1,55 @@ | |||||
fileFormatVersion: 2 | |||||
guid: a4cdca73d61814d33ac1587f6c163bca | |||||
TextureImporter: | |||||
fileIDToRecycleName: {} | |||||
serializedVersion: 2 | |||||
mipmaps: | |||||
mipMapMode: 0 | |||||
enableMipMap: 1 | |||||
linearTexture: 0 | |||||
correctGamma: 0 | |||||
fadeOut: 0 | |||||
borderMipMap: 0 | |||||
mipMapFadeDistanceStart: 1 | |||||
mipMapFadeDistanceEnd: 3 | |||||
bumpmap: | |||||
convertToNormalMap: 0 | |||||
externalNormalMap: 0 | |||||
heightScale: 0.25 | |||||
normalMapFilter: 0 | |||||
isReadable: 0 | |||||
grayScaleToAlpha: 0 | |||||
generateCubemap: 0 | |||||
cubemapConvolution: 0 | |||||
cubemapConvolutionSteps: 7 | |||||
cubemapConvolutionExponent: 1.5 | |||||
seamlessCubemap: 0 | |||||
textureFormat: -1 | |||||
maxTextureSize: 64 | |||||
textureSettings: | |||||
filterMode: -1 | |||||
aniso: 0 | |||||
mipBias: -1 | |||||
wrapMode: 1 | |||||
nPOTScale: 1 | |||||
lightmap: 0 | |||||
rGBM: 0 | |||||
compressionQuality: 50 | |||||
allowsAlphaSplitting: 0 | |||||
spriteMode: 0 | |||||
spriteExtrude: 1 | |||||
spriteMeshType: 1 | |||||
alignment: 0 | |||||
spritePivot: {x: 0.5, y: 0.5} | |||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0} | |||||
spritePixelsToUnits: 100 | |||||
alphaIsTransparency: 0 | |||||
textureType: -1 | |||||
buildTargetSettings: [] | |||||
spriteSheet: | |||||
sprites: [] | |||||
outline: [] | |||||
spritePackingTag: | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |