You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
415 lines
12 KiB
415 lines
12 KiB
|
|
// This Ambient Occlusion image effect is based on "Scalable Ambient Obscurance": |
|
|
|
/** |
|
|
|
\author Morgan McGuire and Michael Mara, NVIDIA and Williams College, http://research.nvidia.com, http://graphics.cs.williams.edu |
|
|
|
Open Source under the "BSD" license: http://www.opensource.org/licenses/bsd-license.php |
|
|
|
Copyright (c) 2011-2012, NVIDIA |
|
All rights reserved. |
|
|
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: |
|
|
|
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. |
|
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. |
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
|
|
*/ |
|
|
|
Shader "Hidden/ScreenSpaceAmbientObscurance" |
|
{ |
|
Properties { |
|
_MainTex ("Base (RGB)", 2D) = "white" {} |
|
_AOTex("", 2D) = "" {} |
|
_Rand("", 2D) = "" {} |
|
} |
|
|
|
CGINCLUDE |
|
|
|
#include "UnityCG.cginc" |
|
|
|
#if defined(SHADER_API_D3D11) || defined(SHADER_API_GLCORE) |
|
#define NUM_SAMPLES (15) |
|
#else |
|
#define NUM_SAMPLES (11) |
|
#endif |
|
|
|
#define FAR_PLANE_Z (300.0) |
|
#define NUM_SPIRAL_TURNS (7) |
|
#define bias (0.01) |
|
|
|
float _Radius; |
|
float _Radius2; // _Radius * _Radius; |
|
float _Intensity; |
|
float4 _ProjInfo; |
|
float4 _ProjInfoLeft; |
|
float4 _ProjInfoRight; |
|
float4x4 _ProjectionInv; // ref only |
|
|
|
sampler2D_float _CameraDepthTexture; |
|
sampler2D _Rand; |
|
sampler2D _AOTex; |
|
sampler2D _MainTex; |
|
|
|
float4 _MainTex_TexelSize; |
|
half4 _MainTex_ST; |
|
|
|
half4 _AOTex_ST; |
|
half4 _CameraDepthTexture_ST; |
|
|
|
static const float gaussian[5] = { 0.153170, 0.144893, 0.122649, 0.092902, 0.062970 }; // stddev = 2.0 |
|
|
|
float2 _Axis; |
|
|
|
/** Increase to make edges crisper. Decrease to reduce temporal flicker. */ |
|
#define EDGE_SHARPNESS (1.0) |
|
|
|
float _BlurFilterDistance; |
|
#define SCALE _BlurFilterDistance |
|
|
|
/** Filter _Radius in pixels. This will be multiplied by SCALE. */ |
|
#define R (4) |
|
|
|
struct v2f |
|
{ |
|
float4 pos : SV_POSITION; |
|
float2 uv : TEXCOORD0; |
|
float2 uv2 : TEXCOORD1; |
|
}; |
|
|
|
v2f vert( appdata_img v ) |
|
{ |
|
v2f o; |
|
o.pos = UnityObjectToClipPos(v.vertex); |
|
o.uv = v.texcoord.xy; |
|
o.uv2 = v.texcoord.xy; |
|
#if UNITY_UV_STARTS_AT_TOP |
|
if (_MainTex_TexelSize.y < 0) |
|
o.uv2.y = 1-o.uv2.y; |
|
#endif |
|
return o; |
|
} |
|
|
|
float3 ReconstructCSPosition(float2 S, float z) |
|
{ |
|
float linEyeZ = LinearEyeDepth(z); |
|
#ifdef UNITY_SINGLE_PASS_STEREO |
|
float4 projInfo = (unity_StereoEyeIndex == 0) ? _ProjInfoLeft : _ProjInfoRight; |
|
return float3((S.xy * projInfo.xy + projInfo.zw) * linEyeZ, linEyeZ); |
|
#else |
|
return float3(( S.xy * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ); |
|
#endif |
|
|
|
/* |
|
// for reference |
|
float4 clipPos = float4(S*2.0-1.0, (z*2-1), 1); |
|
float4 viewPos; |
|
viewPos.x = dot((float4)_ProjectionInv[0], clipPos); |
|
viewPos.y = dot((float4)_ProjectionInv[1], clipPos); |
|
viewPos.w = dot((float4)_ProjectionInv[3], clipPos); |
|
viewPos.z = z; |
|
viewPos = viewPos/viewPos.w; |
|
return viewPos.xyz; |
|
*/ |
|
} |
|
|
|
float3 ReconstructCSFaceNormal(float3 C) { |
|
return normalize(cross(ddy(C), ddx(C))); |
|
} |
|
|
|
|
|
/** Returns a unit vector and a screen-space _Radius for the tap on a unit disk (the caller should scale by the actual disk _Radius) */ |
|
|
|
float2 TapLocation(int sampleNumber, float spinAngle, out float ssR){ |
|
// Radius relative to ssR |
|
float alpha = float(sampleNumber + 0.5) * (1.0 / NUM_SAMPLES); |
|
float angle = alpha * (NUM_SPIRAL_TURNS * 6.28) + spinAngle; |
|
|
|
ssR = alpha; |
|
return float2(cos(angle), sin(angle)); |
|
} |
|
|
|
/** Used for packing Z into the GB channels */ |
|
float CSZToKey(float z) { |
|
return saturate(z * (1.0 / FAR_PLANE_Z)); |
|
} |
|
|
|
/** Used for packing Z into the GB channels */ |
|
void packKey(float key, out float2 p) { |
|
// Round to the nearest 1/256.0 |
|
float temp = floor(key * 256.0); |
|
|
|
// Integer part |
|
p.x = temp * (1.0 / 256.0); |
|
|
|
// Fractional part |
|
p.y = key * 256.0 - temp; |
|
} |
|
|
|
/** Returns a number on (0, 1) */ |
|
float UnpackKey(float2 p) |
|
{ |
|
return p.x * (256.0 / 257.0) + p.y * (1.0 / 257.0); |
|
} |
|
|
|
|
|
/** Read the camera-space position of the point at screen-space pixel ssP */ |
|
float3 GetPosition(float2 ssP) { |
|
float3 P; |
|
|
|
P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, UnityStereoScreenSpaceUVAdjust(ssP.xy, _CameraDepthTexture_ST)); |
|
|
|
// Offset to pixel center |
|
P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z); |
|
return P; |
|
} |
|
|
|
/** Read the camera-space position of the point at screen-space pixel ssP + unitOffset * ssR. Assumes length(unitOffset) == 1 */ |
|
float3 GetOffsetPosition(float2 ssC, float2 unitOffset, float ssR) |
|
{ |
|
float2 ssP = saturate(float2(ssR*unitOffset) + ssC); |
|
|
|
float3 P; |
|
P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, UnityStereoScreenSpaceUVAdjust(ssP.xy, _CameraDepthTexture_ST)); |
|
|
|
// Offset to pixel center |
|
P = ReconstructCSPosition(float2(ssP)/* + float2(0.5, 0.5)*/, P.z); |
|
|
|
return P; |
|
} |
|
|
|
/** Compute the occlusion due to sample with index \a i about the pixel at \a ssC that corresponds |
|
to camera-space point \a C with unit normal \a n_C, using maximum screen-space sampling _Radius \a ssDiskRadius */ |
|
|
|
float SampleAO(in float2 ssC, in float3 C, in float3 n_C, in float ssDiskRadius, in int tapIndex, in float randomPatternRotationAngle) |
|
{ |
|
// Offset on the unit disk, spun for this pixel |
|
float ssR; |
|
float2 unitOffset = TapLocation(tapIndex, randomPatternRotationAngle, ssR); |
|
ssR *= ssDiskRadius; |
|
|
|
// The occluding point in camera space |
|
float3 Q = GetOffsetPosition(ssC, unitOffset, ssR); |
|
|
|
float3 v = Q - C; |
|
|
|
float vv = dot(v, v); |
|
float vn = dot(v, n_C); |
|
|
|
const float epsilon = 0.01; |
|
float f = max(_Radius2 - vv, 0.0); |
|
return f * f * f * max((vn - bias) / (epsilon + vv), 0.0); |
|
} |
|
|
|
float4 fragAO(v2f i) : SV_Target |
|
{ |
|
float4 fragment = fixed4(1,1,1,1); |
|
|
|
// Pixel being shaded |
|
float2 ssC = i.uv2.xy;// * _MainTex_TexelSize.zw; |
|
|
|
// View space point being shaded |
|
float3 C = GetPosition(ssC); |
|
|
|
//return abs(float4(C.xyz,0)); |
|
//if(abs(C.z)<0.31) |
|
// return 1; |
|
//return abs(C.z); |
|
|
|
packKey(CSZToKey(C.z), fragment.gb); |
|
//packKey(CSZToKey(C.z), bilateralKey); |
|
|
|
float randomPatternRotationAngle = 1.0; |
|
#if defined(SHADER_API_D3D11) || defined(SHADER_API_GLCORE) |
|
int2 ssCInt = ssC.xy * _MainTex_TexelSize.zw; |
|
randomPatternRotationAngle = frac(sin(dot(i.uv, float2(12.9898, 78.233))) * 43758.5453) * 1000.0; |
|
#else |
|
// TODO: make dx9 rand better |
|
randomPatternRotationAngle = tex2D(_Rand, i.uv*12.0).x * 1000.0; |
|
#endif |
|
|
|
// Reconstruct normals from positions. These will lead to 1-pixel black lines |
|
// at depth discontinuities, however the blur will wipe those out so they are not visible |
|
// in the final image. |
|
float3 n_C = ReconstructCSFaceNormal(C); |
|
|
|
//return float4((n_C),0); |
|
|
|
// Choose the screen-space sample _Radius |
|
// proportional to the projected area of the sphere |
|
float ssDiskRadius = -_Radius / C.z; // -projScale * _Radius / C.z; // <::::: |
|
|
|
float sum = 0.0; |
|
for (int l = 0; l < NUM_SAMPLES; ++l) { |
|
sum += SampleAO(ssC, C, n_C, (ssDiskRadius), l, randomPatternRotationAngle); |
|
} |
|
|
|
float temp = _Radius2 * _Radius; |
|
sum /= temp * temp; |
|
|
|
float A = max(0.0, 1.0 - sum * _Intensity * (5.0 / NUM_SAMPLES)); |
|
fragment.ra = float2(A,A); |
|
|
|
return fragment; |
|
} |
|
|
|
float4 fragUpsample (v2f i) : SV_Target |
|
{ |
|
float4 fragment = fixed4(1,1,1,1); |
|
|
|
// View space point being shaded |
|
float3 C = GetPosition(i.uv.xy); |
|
|
|
packKey(CSZToKey(C.z), fragment.gb); |
|
fragment.ra = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv.xy, _MainTex_ST)).ra; |
|
|
|
return fragment; |
|
} |
|
|
|
float4 fragApply (v2f i) : SV_Target |
|
{ |
|
float4 ao = tex2D(_AOTex, UnityStereoScreenSpaceUVAdjust(i.uv2.xy, _AOTex_ST)); |
|
return tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv.xy, _MainTex_ST)) * ao.rrrr; |
|
} |
|
|
|
float4 fragApplySoft (v2f i) : SV_Target |
|
{ |
|
float4 color = tex2D(_MainTex, UnityStereoScreenSpaceUVAdjust(i.uv.xy, _MainTex_ST)); |
|
|
|
float ao = tex2D(_AOTex, UnityStereoScreenSpaceUVAdjust(i.uv2.xy, _AOTex_ST)).r; |
|
ao += tex2D(_AOTex, UnityStereoScreenSpaceUVAdjust(i.uv2.xy, _AOTex_ST) + _MainTex_TexelSize.xy * 0.75).r; |
|
ao += tex2D(_AOTex, UnityStereoScreenSpaceUVAdjust(i.uv2.xy, _AOTex_ST) - _MainTex_TexelSize.xy * 0.75).r; |
|
ao += tex2D(_AOTex, UnityStereoScreenSpaceUVAdjust(i.uv2.xy, _AOTex_ST) + _MainTex_TexelSize.xy * float2(-0.75,0.75)).r; |
|
ao += tex2D(_AOTex, UnityStereoScreenSpaceUVAdjust(i.uv2.xy, _AOTex_ST) - _MainTex_TexelSize.xy * float2(-0.75,0.75)).r; |
|
|
|
return color * float4(ao,ao,ao,5)/5; |
|
} |
|
|
|
float4 fragBlurBL (v2f i) : SV_Target |
|
{ |
|
float4 fragment = float4(1,1,1,1); |
|
|
|
float2 ssC = UnityStereoScreenSpaceUVAdjust(i.uv.xy, _MainTex_ST); |
|
|
|
float4 temp = tex2Dlod(_MainTex, float4(UnityStereoScreenSpaceUVAdjust(i.uv.xy, _MainTex_ST),0,0)); |
|
|
|
float2 passthrough2 = temp.gb; |
|
float key = UnpackKey(passthrough2); |
|
|
|
float sum = temp.r; |
|
|
|
/* |
|
if (key >= 0.999) { |
|
// Sky pixel (if you aren't using depth keying, disable this test) |
|
fragment.gb = passthrough2; |
|
return fragment; |
|
} |
|
*/ |
|
|
|
// Base weight for depth falloff. Increase this for more blurriness, decrease it for better edge discrimination |
|
|
|
float BASE = gaussian[0] * 0.5; // ole: i decreased |
|
float totalWeight = BASE; |
|
sum *= totalWeight; |
|
|
|
for (int r = -R; r <= R; ++r) { |
|
// We already handled the zero case above. This loop should be unrolled and the branch discarded |
|
if (r != 0) { |
|
temp = tex2Dlod(_MainTex, float4(ssC + _Axis * _MainTex_TexelSize.xy * (r * SCALE),0,0) ); |
|
float tapKey = UnpackKey(temp.gb); |
|
float value = temp.r; |
|
|
|
// spatial domain: offset gaussian tap |
|
float weight = 0.3 + gaussian[abs(r)]; |
|
|
|
// range domain (the "bilateral" weight). As depth difference increases, decrease weight. |
|
weight *= max(0.0, 1.0 - (2000.0 * EDGE_SHARPNESS) * abs(tapKey - key)); |
|
|
|
sum += value * weight; |
|
totalWeight += weight; |
|
} |
|
} |
|
|
|
const float epsilon = 0.0001; |
|
fragment = sum / (totalWeight + epsilon); |
|
|
|
fragment.gb = passthrough2; |
|
|
|
return fragment; |
|
} |
|
|
|
ENDCG |
|
|
|
SubShader { |
|
|
|
// 0: get ao |
|
Pass { |
|
ZTest Always Cull Off ZWrite Off |
|
|
|
CGPROGRAM |
|
|
|
#pragma vertex vert |
|
#pragma fragment fragAO |
|
#pragma target 3.0 |
|
|
|
ENDCG |
|
} |
|
|
|
// 1: bilateral blur |
|
Pass { |
|
ZTest Always Cull Off ZWrite Off |
|
|
|
CGPROGRAM |
|
|
|
#pragma vertex vert |
|
#pragma fragment fragBlurBL |
|
#pragma target 3.0 |
|
|
|
ENDCG |
|
} |
|
|
|
// 2: apply ao |
|
Pass { |
|
ZTest Always Cull Off ZWrite Off |
|
|
|
CGPROGRAM |
|
|
|
#pragma vertex vert |
|
#pragma fragment fragApply |
|
#pragma target 3.0 |
|
|
|
ENDCG |
|
} |
|
|
|
// 3: apply with a slight box filter |
|
Pass { |
|
ZTest Always Cull Off ZWrite Off |
|
|
|
CGPROGRAM |
|
|
|
#pragma vertex vert |
|
#pragma fragment fragApplySoft |
|
#pragma target 3.0 |
|
|
|
ENDCG |
|
} |
|
|
|
// 4: in case you want to blur in high rez for nicer z borders |
|
Pass { |
|
ZTest Always Cull Off ZWrite Off |
|
|
|
CGPROGRAM |
|
|
|
#pragma vertex vert |
|
#pragma fragment fragUpsample |
|
#pragma target 3.0 |
|
|
|
ENDCG |
|
} |
|
} |
|
|
|
Fallback off |
|
|
|
}
|
|
|