1 using System;
2 using UnityEngine;
3
4 namespace UnityStandardAssets.ImageEffects
5 {
6 [ExecuteInEditMode]
7 [RequireComponent (typeof(Camera))]
8 [AddComponentMenu("Image Effects/Rendering/Screen Space Ambient Occlusion")]
9 public class ScreenSpaceAmbientOcclusion : MonoBehaviour
10 {
11 public enum SSAOSamples
12 {
13 Low = 0,
14 Medium = 1,
15 High = 2,
16 }
17
18 public float m_Radius = 0.4f;
19 public SSAOSamples m_SampleCount = SSAOSamples.Medium;
20 public float m_OcclusionIntensity = 1.5f;
21 public int m_Blur = 2;
22 public int m_Downsampling = 2;
23 public float m_OcclusionAttenuation = 1.0f;
24 public float m_MinZ = 0.01f;
25
26 public Shader m_SSAOShader;
27 private Material m_SSAOMaterial;
28
29 public Texture2D m_RandomTexture;
30
31 private bool m_Supported;
32
33 private static Material CreateMaterial (Shader shader)
34 {
35 if (!shader)
36 return null;
37 Material m = new Material (shader);
38 m.hideFlags = HideFlags.HideAndDontSave;
39 return m;
40 }
41 private static void DestroyMaterial (Material mat)
42 {
43 if (mat)
44 {
45 DestroyImmediate (mat);
46 mat = null;
47 }
48 }
49
50
51 void OnDisable()
52 {
53 DestroyMaterial (m_SSAOMaterial);
54 }
55
56 void Start()
57 {
58 if (!SystemInfo.supportsImageEffects || !SystemInfo.SupportsRenderTextureFormat (RenderTextureFormat.Depth))
59 {
60 m_Supported = false;
61 enabled = false;
62 return;
63 }
64
65 CreateMaterials ();
66 if (!m_SSAOMaterial || m_SSAOMaterial.passCount != 5)
67 {
68 m_Supported = false;
69 enabled = false;
70 return;
71 }
72
73 //CreateRandomTable (26, 0.2f);
74
75 m_Supported = true;
76 }
77
78 void OnEnable () {
79 GetComponent<Camera>().depthTextureMode |= DepthTextureMode.DepthNormals;
80 }
81
82 private void CreateMaterials ()
83 {
84 if (!m_SSAOMaterial && m_SSAOShader.isSupported)
85 {
86 m_SSAOMaterial = CreateMaterial (m_SSAOShader);
87 m_SSAOMaterial.SetTexture ("_RandomTexture", m_RandomTexture);
88 }
89 }
90
91 [ImageEffectOpaque]
92 void OnRenderImage (RenderTexture source, RenderTexture destination)
93 {
94 if (!m_Supported || !m_SSAOShader.isSupported) {
95 enabled = false;
96 return;
97 }
98 CreateMaterials ();
99
100 m_Downsampling = Mathf.Clamp (m_Downsampling, 1, 6);
101 m_Radius = Mathf.Clamp (m_Radius, 0.05f, 1.0f);
102 m_MinZ = Mathf.Clamp (m_MinZ, 0.00001f, 0.5f);
103 m_OcclusionIntensity = Mathf.Clamp (m_OcclusionIntensity, 0.5f, 4.0f);
104 m_OcclusionAttenuation = Mathf.Clamp (m_OcclusionAttenuation, 0.2f, 2.0f);
105 m_Blur = Mathf.Clamp (m_Blur, 0, 4);
106
107 // Render SSAO term into a smaller texture
108 RenderTexture rtAO = RenderTexture.GetTemporary (source.width / m_Downsampling, source.height / m_Downsampling, 0);
109 float fovY = GetComponent<Camera>().fieldOfView;
110 float far = GetComponent<Camera>().farClipPlane;
111 float y = Mathf.Tan (fovY * Mathf.Deg2Rad * 0.5f) * far;
112 float x = y * GetComponent<Camera>().aspect;
113 m_SSAOMaterial.SetVector ("_FarCorner", new Vector3(x,y,far));
114 int noiseWidth, noiseHeight;
115 if (m_RandomTexture) {
116 noiseWidth = m_RandomTexture.width;
117 noiseHeight = m_RandomTexture.height;
118 } else {
119 noiseWidth = 1; noiseHeight = 1;
120 }
121 m_SSAOMaterial.SetVector ("_NoiseScale", new Vector3 ((float)rtAO.width / noiseWidth, (float)rtAO.height / noiseHeight, 0.0f));
122 m_SSAOMaterial.SetVector ("_Params", new Vector4(
123 m_Radius,
124 m_MinZ,
125 1.0f / m_OcclusionAttenuation,
126 m_OcclusionIntensity));
127
128 bool doBlur = m_Blur > 0;
129 Graphics.Blit (doBlur ? null : source, rtAO, m_SSAOMaterial, (int)m_SampleCount);
130
131 if (doBlur)
132 {
133 // Blur SSAO horizontally
134 RenderTexture rtBlurX = RenderTexture.GetTemporary (source.width, source.height, 0);
135 m_SSAOMaterial.SetVector ("_TexelOffsetScale",
136 new Vector4 ((float)m_Blur / source.width, 0,0,0));
137 m_SSAOMaterial.SetTexture ("_SSAO", rtAO);
138 Graphics.Blit (null, rtBlurX, m_SSAOMaterial, 3);
139 RenderTexture.ReleaseTemporary (rtAO); // original rtAO not needed anymore
140
141 // Blur SSAO vertically
142 RenderTexture rtBlurY = RenderTexture.GetTemporary (source.width, source.height, 0);
143 m_SSAOMaterial.SetVector ("_TexelOffsetScale",
144 new Vector4 (0, (float)m_Blur/source.height, 0,0));
145 m_SSAOMaterial.SetTexture ("_SSAO", rtBlurX);
146 Graphics.Blit (source, rtBlurY, m_SSAOMaterial, 3);
147 RenderTexture.ReleaseTemporary (rtBlurX); // blurX RT not needed anymore
148
149 rtAO = rtBlurY; // AO is the blurred one now
150 }
151
152 // Modulate scene rendering with SSAO
153 m_SSAOMaterial.SetTexture ("_SSAO", rtAO);
154 Graphics.Blit (source, destination, m_SSAOMaterial, 4);
155
156 RenderTexture.ReleaseTemporary (rtAO);
157 }
158
159 /*
160 private void CreateRandomTable (int count, float minLength)
161 {
162 Random.seed = 1337;
163 Vector3[] samples = new Vector3[count];
164 // initial samples
165 for (int i = 0; i < count; ++i)
166 samples[i] = Random.onUnitSphere;
167 // energy minimization: push samples away from others
168 int iterations = 100;
169 while (iterations-- > 0) {
170 for (int i = 0; i < count; ++i) {
171 Vector3 vec = samples[i];
172 Vector3 res = Vector3.zero;
173 // minimize with other samples
174 for (int j = 0; j < count; ++j) {
175 Vector3 force = vec - samples[j];
176 float fac = Vector3.Dot (force, force);
177 if (fac > 0.00001f)
178 res += force * (1.0f / fac);
179 }
180 samples[i] = (samples[i] + res * 0.5f).normalized;
181 }
182 }
183 // now scale samples between minLength and 1.0
184 for (int i = 0; i < count; ++i) {
185 samples[i] = samples[i] * Random.Range (minLength, 1.0f);
186 }
187
188 string table = string.Format ("#define SAMPLE_COUNT {0}\n", count);
189 table += "const float3 RAND_SAMPLES[SAMPLE_COUNT] = {\n";
190 for (int i = 0; i < count; ++i) {
191 Vector3 v = samples[i];
192 table += string.Format("\tfloat3({0},{1},{2}),\n", v.x, v.y, v.z);
193 }
194 table += "};\n";
195 Debug.Log (table);
196 }
197 */
198 }
199 }
CreateRandomTable (26, 0.2f);
Render SSAO term into a smaller texture
Blur SSAO horizontally
RenderTexture.ReleaseTemporary (rtAO); original rtAO not needed anymore
Blur SSAO vertically
RenderTexture.ReleaseTemporary (rtBlurX); blurX RT not needed anymore
rtAO = rtBlurY; AO is the blurred one now
Modulate scene rendering with SSAO
initial samples
energy minimization: push samples away from others
minimize with other samples
now scale samples between minLength and 1.0