这个东西是trace在群里提到的,然后我看了一些相关资源Filtering Approaches for
Real-Time Anti-Aliasing(很多sig course好棒好棒)、High Quality Temporal Supersampling、CryENGINE3 Graphics Gems。在这么多资料(其实是现成代码…)的帮助下,我主要参考CryEngine里的SMAA 1TX山寨了下,UE4的那个有点过于麻烦了。
备忘
- Unity中矩阵是左乘的,和UE4里相反,所以在对projectionMatrix做jitter的时候要反下
UNITY_MATRIX_MVP
在之前的post里已提过,这个是卡我最久的地方(╯‵□′)╯︵┻━┻
- Depth Buffer也在同一个post里提了,这是卡我第二久的地方……
OnRenderImage
里如果不手动设置RenderTexture.active
的话,最好要保证最后对dest调用下Graphics.Blit
,不然之后画的就乱七八糟了
其实都是一些API上的东西,搞的我连蒙带猜的…
效果
自我感觉效果还行吧…一开始边缘一直有闪动,慢慢改对代码之后降低了不少,最后就调参数了只能…
Temporal AA:
W/O AA:
Nexus5真机
Temporal AA |
W/O AA |
|
|
放大出来还是能看出来的
性能
在Nexus 5上跑了下Shadow Gun Sample Level这个场景,每帧消耗时间大概增加了7ms;从profiler上来看主要是因为用到了Depth Texture,而且看起来不是直接用的ZBuffer导致的(见Unity中矩阵变换、深度纹理及杂项),话说还是Defer大法好-_,-
NVidia在Far Cry 4 Graphics, Performance & Tweaking Guide中有不同AA效果对比;TweakGuides的Crysis 3 Tweak Guide中有一节专门讲Antialiasing,里面有性能图标。后来问了下老大,她意思就是AA还是比较费的,等有性能余地的话才加;而且比较尴尬的是手机屏幕上其实看不太出区别,看来还是要配合动态分辨率+upscale~
TemporalAA1Tx.shaderview rawShader "Hidden/TemporalAA1Tx" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { ZTest Off Cull Off ZWrite Off Blend Off Fog { Mode off } Pass { CGPROGRAM #include "UnityCG.cginc" #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 projPos : TEXCOORD1; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.projPos = o.pos.xy / o.pos.w; o.uv = v.texcoord.xy; return o; } sampler2D _MainTex, _MainTex1; sampler2D _CameraDepthTexture; float4x4 combinedVP; float2 texel; float4 vParams;
float4 frag(v2f i) : SV_Target { float3 PosN = float3(i.projPos.xy, 0.0f); PosN.z = tex2D(_CameraDepthTexture, i.uv.xy).r; float4 temp = mul(combinedVP, float4(PosN, 1)); float2 BackN = temp.xy / temp.w; float2 v = BackN.xy - PosN.xy; v *= 0.5f; v.y *= _ProjectionParams.x; float fMaxFramesL = vParams.z; float fMaxFramesH = vParams.w; half3 cM = tex2D(_MainTex, i.uv.xy); half3 cTL = tex2D(_MainTex, i.uv.xy + texel * float2(-0.5f, -0.5f)); half3 cTR = tex2D(_MainTex, i.uv.xy + texel * float2( 0.5f, -0.5f)); half3 cBL = tex2D(_MainTex, i.uv.xy + texel * float2(-0.5f, 0.5f)); half3 cBR = tex2D(_MainTex, i.uv.xy + texel * float2( 0.5f, 0.5f)); half3 cBlur = (cTL + cTR + cBL + cBR) * 0.25f; cM.rgb = lerp(cBlur, cM, vParams.x); half3 cMin = min(min(min(min(cTL, cTR), cBL), cBR), cM); half3 cMax = max(max(max(max(cTL, cTR), cBL), cBR), cM); float3 cAcc = tex2D(_MainTex1, i.uv.xy + v); cAcc.rgb = clamp(cAcc, cMin, cMax); half3 cHiFreq = sqrt(abs( cBlur.rgb - cM.rgb)); float4 c = 0; c.rgb = lerp(cAcc, cM, saturate(lerp(fMaxFramesL, fMaxFramesH, cHiFreq)) ); return c; } ENDCG } } FallBack Off }
|
TemporalAA.csview rawusing UnityEngine; using System.Collections;
[RequireComponent (typeof (Camera))] public class TemporalAA : MonoBehaviour { public Shader shader = null; private Material mat = null;
private bool initialized = false, usingRT1 = true; private RenderTexture rt1 = null, rt2 = null;
private Matrix4x4 prevViewProj; private int JitterIdx = 0;
private Matrix4x4 getViewProjMatrix() { return GL.GetGPUProjectionMatrix(camera.projectionMatrix, false) * camera.worldToCameraMatrix; }
void Start () { mat = new Material(shader); mat.hideFlags = HideFlags.HideAndDontSave; }
void OnEnable() { prevViewProj = getViewProjMatrix(); camera.depthTextureMode |= DepthTextureMode.Depth;
initialized = false; usingRT1 = true; } void OnDisable() { RenderTexture.Destroy(rt1); rt1 = null; RenderTexture.Destroy(rt2); rt2 = null; } private float []JitterOffsetX = { -8.0f/16.0f, 0.0f/16.0f }; private float []JitterOffsetY = { 0.0f/16.0f, 8.0f/16.0f }; float Halton( int Index, int Base ) { float Result = 0.0f; float InvBase = 1.0f / Base; float Fraction = InvBase; while( Index > 0 ) { Result += ( Index % Base ) * Fraction; Index /= Base; Fraction *= InvBase; } return Result; }
void Update () { JitterIdx = (JitterIdx + 1) % 8;
Matrix4x4 matrix = camera.projectionMatrix; matrix[0,2] += (Halton(JitterIdx+1, 2)-0.5f)/Screen.width; matrix[1,2] += (Halton(JitterIdx+1, 3)-0.5f)/Screen.height; camera.projectionMatrix = matrix; }
public Vector4 vParams = new Vector4(0.8f, 0, 24.0f, 32.0f); void OnRenderImage(RenderTexture src, RenderTexture dest) { if(rt1 == null) { rt1 = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGBHalf); rt1.Create(); } if(rt2 == null) { rt2 = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGBHalf); rt2.Create(); } camera.ResetProjectionMatrix(); Matrix4x4 ViewProj = getViewProjMatrix(); mat.SetMatrix("combinedVP", prevViewProj * Matrix4x4.Inverse(ViewProj)); prevViewProj = ViewProj;
RenderTexture prev = null, current = null; if(usingRT1) { current = rt1; prev = rt2; usingRT1 = false; } else { current = rt2; prev = rt1; usingRT1 = true; }
if(!initialized){ prev = src; initialized = true; }
mat.SetVector("texel", new Vector4(1.0f/src.width, 1.0f/src.height, 0, 0)); mat.SetVector("vParams", new Vector4(vParams.x , 0, 1.0f / vParams.z, 1.0f / vParams.w)); mat.SetTexture("_MainTex1", prev); current.DiscardContents(); Graphics.Blit(src, current, mat);
Graphics.Blit(current, dest); } }
|