201 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using UnityEngine;
 | 
						|
 | 
						|
[ExecuteInEditMode]
 | 
						|
[RequireComponent(typeof(Camera))]
 | 
						|
[AddComponentMenu("Image Effects/SSAO")]
 | 
						|
public class SSAOEffect : MonoBehaviour
 | 
						|
{
 | 
						|
    public enum SSAOSamples
 | 
						|
    {
 | 
						|
        Low = 0,
 | 
						|
        Medium = 1,
 | 
						|
        High = 2,
 | 
						|
    }
 | 
						|
 | 
						|
    public float m_Radius = 0.4f;
 | 
						|
    public SSAOSamples m_SampleCount = SSAOSamples.Medium;
 | 
						|
    public float m_OcclusionIntensity = 1.5f;
 | 
						|
    public int m_Blur = 2;
 | 
						|
    public int m_Downsampling = 2;
 | 
						|
    public float m_OcclusionAttenuation = 1.0f;
 | 
						|
    public float m_MinZ = 0.01f;
 | 
						|
 | 
						|
    public Shader m_SSAOShader;
 | 
						|
    private Material m_SSAOMaterial;
 | 
						|
 | 
						|
    public Texture2D m_RandomTexture;
 | 
						|
 | 
						|
    private bool m_Supported;
 | 
						|
    private bool m_IsOpenGL;
 | 
						|
 | 
						|
    private static Material CreateMaterial(Shader shader)
 | 
						|
    {
 | 
						|
        if (!shader)
 | 
						|
            return null;
 | 
						|
        Material m = new Material(shader);
 | 
						|
        m.hideFlags = HideFlags.HideAndDontSave;
 | 
						|
        return m;
 | 
						|
    }
 | 
						|
    private static void DestroyMaterial(Material mat)
 | 
						|
    {
 | 
						|
        if (mat)
 | 
						|
        {
 | 
						|
            DestroyImmediate(mat);
 | 
						|
            mat = null;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    void OnDisable()
 | 
						|
    {
 | 
						|
        DestroyMaterial(m_SSAOMaterial);
 | 
						|
    }
 | 
						|
 | 
						|
    void Start()
 | 
						|
    {
 | 
						|
        if (!SystemInfo.supportsImageEffects || !SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.Depth))
 | 
						|
        {
 | 
						|
            m_Supported = false;
 | 
						|
            enabled = false;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        CreateMaterials();
 | 
						|
        if (!m_SSAOMaterial || m_SSAOMaterial.passCount != 5)
 | 
						|
        {
 | 
						|
            m_Supported = false;
 | 
						|
            enabled = false;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        //CreateRandomTable (26, 0.2f);
 | 
						|
 | 
						|
        camera.depthTextureMode = DepthTextureMode.DepthNormals;
 | 
						|
        m_Supported = true;
 | 
						|
        m_IsOpenGL = SystemInfo.graphicsDeviceVersion.StartsWith("OpenGL");
 | 
						|
    }
 | 
						|
 | 
						|
    private void CreateMaterials()
 | 
						|
    {
 | 
						|
        if (!m_SSAOMaterial && m_SSAOShader.isSupported)
 | 
						|
        {
 | 
						|
            m_SSAOMaterial = CreateMaterial(m_SSAOShader);
 | 
						|
            m_SSAOMaterial.SetTexture("_RandomTexture", m_RandomTexture);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    void OnRenderImage(RenderTexture source, RenderTexture destination)
 | 
						|
    {
 | 
						|
        if (!m_Supported || !m_SSAOShader.isSupported)
 | 
						|
        {
 | 
						|
            enabled = false;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        CreateMaterials();
 | 
						|
 | 
						|
        m_Downsampling = Mathf.Clamp(m_Downsampling, 1, 6);
 | 
						|
        m_Radius = Mathf.Clamp(m_Radius, 0.05f, 1.0f);
 | 
						|
        m_MinZ = Mathf.Clamp(m_MinZ, 0.00001f, 0.5f);
 | 
						|
        m_OcclusionIntensity = Mathf.Clamp(m_OcclusionIntensity, 0.5f, 4.0f);
 | 
						|
        m_OcclusionAttenuation = Mathf.Clamp(m_OcclusionAttenuation, 0.2f, 2.0f);
 | 
						|
        m_Blur = Mathf.Clamp(m_Blur, 0, 4);
 | 
						|
 | 
						|
        // Render SSAO term into a smaller texture
 | 
						|
        RenderTexture rtAO = RenderTexture.GetTemporary(source.width / m_Downsampling, source.height / m_Downsampling, 0);
 | 
						|
        float fovY = camera.fieldOfView;
 | 
						|
        float far = camera.farClipPlane;
 | 
						|
        float y = Mathf.Tan(fovY * Mathf.Deg2Rad * 0.5f) * far;
 | 
						|
        float x = y * camera.aspect;
 | 
						|
        m_SSAOMaterial.SetVector("_FarCorner", new Vector3(x, y, far));
 | 
						|
        int noiseWidth, noiseHeight;
 | 
						|
        if (m_RandomTexture)
 | 
						|
        {
 | 
						|
            noiseWidth = m_RandomTexture.width;
 | 
						|
            noiseHeight = m_RandomTexture.height;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            noiseWidth = 1; noiseHeight = 1;
 | 
						|
        }
 | 
						|
        m_SSAOMaterial.SetVector("_NoiseScale", new Vector3((float)rtAO.width / noiseWidth, (float)rtAO.height / noiseHeight, 0.0f));
 | 
						|
        m_SSAOMaterial.SetVector("_Params", new Vector4(
 | 
						|
            m_Radius,
 | 
						|
            m_MinZ,
 | 
						|
            1.0f / m_OcclusionAttenuation,
 | 
						|
            m_OcclusionIntensity));
 | 
						|
 | 
						|
        bool doBlur = m_Blur > 0;
 | 
						|
        Graphics.Blit(doBlur ? null : source, rtAO, m_SSAOMaterial, (int)m_SampleCount);
 | 
						|
 | 
						|
        if (doBlur)
 | 
						|
        {
 | 
						|
            // Blur SSAO horizontally
 | 
						|
            RenderTexture rtBlurX = RenderTexture.GetTemporary(source.width, source.height, 0);
 | 
						|
            m_SSAOMaterial.SetVector("_TexelOffsetScale",
 | 
						|
                m_IsOpenGL ?
 | 
						|
                new Vector4(m_Blur, 0, 1.0f / m_Downsampling, 0) :
 | 
						|
                new Vector4((float)m_Blur / source.width, 0, 0, 0));
 | 
						|
            m_SSAOMaterial.SetTexture("_SSAO", rtAO);
 | 
						|
            Graphics.Blit(null, rtBlurX, m_SSAOMaterial, 3);
 | 
						|
            RenderTexture.ReleaseTemporary(rtAO); // original rtAO not needed anymore
 | 
						|
 | 
						|
            // Blur SSAO vertically
 | 
						|
            RenderTexture rtBlurY = RenderTexture.GetTemporary(source.width, source.height, 0);
 | 
						|
            m_SSAOMaterial.SetVector("_TexelOffsetScale",
 | 
						|
                m_IsOpenGL ?
 | 
						|
                new Vector4(0, m_Blur, 1, 0) :
 | 
						|
                new Vector4(0, (float)m_Blur / source.height, 0, 0));
 | 
						|
            m_SSAOMaterial.SetTexture("_SSAO", rtBlurX);
 | 
						|
            Graphics.Blit(source, rtBlurY, m_SSAOMaterial, 3);
 | 
						|
            RenderTexture.ReleaseTemporary(rtBlurX); // blurX RT not needed anymore
 | 
						|
 | 
						|
            rtAO = rtBlurY; // AO is the blurred one now
 | 
						|
        }
 | 
						|
 | 
						|
        // Modulate scene rendering with SSAO
 | 
						|
        m_SSAOMaterial.SetTexture("_SSAO", rtAO);
 | 
						|
        Graphics.Blit(source, destination, m_SSAOMaterial, 4);
 | 
						|
 | 
						|
        RenderTexture.ReleaseTemporary(rtAO);
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
    private void CreateRandomTable (int count, float minLength)
 | 
						|
    {
 | 
						|
        Random.seed = 1337;
 | 
						|
        Vector3[] samples = new Vector3[count];
 | 
						|
        // initial samples
 | 
						|
        for (int i = 0; i < count; ++i)
 | 
						|
            samples[i] = Random.onUnitSphere;
 | 
						|
        // energy minimization: push samples away from others
 | 
						|
        int iterations = 100;
 | 
						|
        while (iterations-- > 0) {
 | 
						|
            for (int i = 0; i < count; ++i) {
 | 
						|
                Vector3 vec = samples[i];
 | 
						|
                Vector3 res = Vector3.zero;
 | 
						|
                // minimize with other samples
 | 
						|
                for (int j = 0; j < count; ++j) {
 | 
						|
                    Vector3 force = vec - samples[j];
 | 
						|
                    float fac = Vector3.Dot (force, force);
 | 
						|
                    if (fac > 0.00001f)
 | 
						|
                        res += force * (1.0f / fac);
 | 
						|
                }
 | 
						|
                samples[i] = (samples[i] + res * 0.5f).normalized;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // now scale samples between minLength and 1.0
 | 
						|
        for (int i = 0; i < count; ++i) {
 | 
						|
            samples[i] = samples[i] * Random.Range (minLength, 1.0f);
 | 
						|
        }		
 | 
						|
 | 
						|
        string table = string.Format ("#define SAMPLE_COUNT {0}\n", count);
 | 
						|
        table += "const float3 RAND_SAMPLES[SAMPLE_COUNT] = {\n";
 | 
						|
        for (int i = 0; i < count; ++i) {
 | 
						|
            Vector3 v = samples[i];
 | 
						|
            table += string.Format("\tfloat3({0},{1},{2}),\n", v.x, v.y, v.z);
 | 
						|
        }
 | 
						|
        table += "};\n";
 | 
						|
        Debug.Log (table);
 | 
						|
    }
 | 
						|
    */
 | 
						|
} |