- Home /
How to blur objects based on distance from the camera using Unity's optimized blur effect?
Hello everybody,
I'm currently working on a 2D game for Android with Unity 5.4.3, in which the environment consists of several layers of sprites. Each layer is placed at a different Z-Position and I'm using an orthographic camera to render the scene. In order to make distant objects appear to be farther away, I want to blur the several background layers based on their distance from the camera (or their Z-Value). I found the Optimized Blur script and Fast Blur shader that come with Unity and want to adapt these to not blur the whole screen output to the same amount but to blur each layer at a different amount. I'm not much of a shader expert and despite some efforts and research I couldn't get anywhere near what I want to do. So, I would really appreciate any help you guys can offer me to make progress on this. I'm posting the script and shader code below.
This is the script I added to my main camera:
using System;
using UnityEngine;
namespace UnityStandardAssets.ImageEffects
{
[ExecuteInEditMode]
[RequireComponent (typeof(Camera))]
[AddComponentMenu ("Image Effects/Blur/Blur (Optimized)")]
public class CustomBlurOptimized : PostEffectsBase
{
[Range(0, 2)]
public int downsample = 1;
public enum BlurType {
StandardGauss = 0,
SgxGauss = 1,
}
[Range(0.0f, 10.0f)]
public float blurSize = 3.0f;
[Range(1, 4)]
public int blurIterations = 2;
public BlurType blurType= BlurType.StandardGauss;
public Shader blurShader = null;
private Material blurMaterial = null;
public override bool CheckResources () {
CheckSupport (false);
blurMaterial = CheckShaderAndCreateMaterial (blurShader, blurMaterial);
if (!isSupported)
ReportAutoDisable ();
return isSupported;
}
// This is the only thing I added to the original Unity version
public void OnEnable()
{
GetComponent<Camera>().depthTextureMode = DepthTextureMode.Depth;
}
public void OnDisable () {
if (blurMaterial)
DestroyImmediate (blurMaterial);
}
public void OnRenderImage (RenderTexture source, RenderTexture destination) {
if (CheckResources() == false) {
Graphics.Blit (source, destination);
return;
}
float widthMod = 1.0f / (1.0f * (1<<downsample));
blurMaterial.SetVector ("_Parameter", new Vector4 (blurSize * widthMod, -blurSize * widthMod, 0.0f, 0.0f));
source.filterMode = FilterMode.Bilinear;
int rtW = source.width >> downsample;
int rtH = source.height >> downsample;
// downsample
RenderTexture rt = RenderTexture.GetTemporary (rtW, rtH, 0, source.format);
rt.filterMode = FilterMode.Bilinear;
Graphics.Blit (source, rt, blurMaterial, 0);
var passOffs= blurType == BlurType.StandardGauss ? 0 : 2;
for(int i = 0; i < blurIterations; i++) {
float iterationOffs = (i*1.0f);
blurMaterial.SetVector ("_Parameter", new Vector4 (blurSize * widthMod + iterationOffs, -blurSize * widthMod - iterationOffs, 0.0f, 0.0f));
// vertical blur
RenderTexture rt2 = RenderTexture.GetTemporary (rtW, rtH, 0, source.format);
rt2.filterMode = FilterMode.Bilinear;
Graphics.Blit (rt, rt2, blurMaterial, 1 + passOffs);
RenderTexture.ReleaseTemporary (rt);
rt = rt2;
// horizontal blur
rt2 = RenderTexture.GetTemporary (rtW, rtH, 0, source.format);
rt2.filterMode = FilterMode.Bilinear;
Graphics.Blit (rt, rt2, blurMaterial, 2 + passOffs);
RenderTexture.ReleaseTemporary (rt);
rt = rt2;
}
Graphics.Blit (rt, destination);
RenderTexture.ReleaseTemporary (rt);
}
}
}
And here's the shader code (I want to use the "normal" version, not the SGX stuff):
Shader "Hidden/FastBlur" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Bloom ("Bloom (RGB)", 2D) = "black" {}
}
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _Bloom;
uniform half4 _MainTex_TexelSize;
uniform half4 _Parameter;
struct v2f_tap
{
float4 pos : SV_POSITION;
half2 uv20 : TEXCOORD0;
half2 uv21 : TEXCOORD1;
half2 uv22 : TEXCOORD2;
half2 uv23 : TEXCOORD3;
};
v2f_tap vert4Tap ( appdata_img v )
{
v2f_tap o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv20 = v.texcoord + _MainTex_TexelSize.xy;
o.uv21 = v.texcoord + _MainTex_TexelSize.xy * half2(-0.5h,-0.5h);
o.uv22 = v.texcoord + _MainTex_TexelSize.xy * half2(0.5h,-0.5h);
o.uv23 = v.texcoord + _MainTex_TexelSize.xy * half2(-0.5h,0.5h);
return o;
}
fixed4 fragDownsample ( v2f_tap i ) : SV_Target
{
fixed4 color = tex2D (_MainTex, i.uv20);
color += tex2D (_MainTex, i.uv21);
color += tex2D (_MainTex, i.uv22);
color += tex2D (_MainTex, i.uv23);
return color / 4;
}
// weight curves
static const half curve[7] = { 0.0205, 0.0855, 0.232, 0.324, 0.232, 0.0855, 0.0205 }; // gauss'ish blur weights
static const half4 curve4[7] = { half4(0.0205,0.0205,0.0205,0), half4(0.0855,0.0855,0.0855,0), half4(0.232,0.232,0.232,0),
half4(0.324,0.324,0.324,1), half4(0.232,0.232,0.232,0), half4(0.0855,0.0855,0.0855,0), half4(0.0205,0.0205,0.0205,0) };
struct v2f_withBlurCoords8
{
float4 pos : SV_POSITION;
half4 uv : TEXCOORD0;
half2 offs : TEXCOORD1;
};
struct v2f_withBlurCoordsSGX
{
float4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
half4 offs[3] : TEXCOORD1;
};
v2f_withBlurCoords8 vertBlurHorizontal (appdata_img v)
{
v2f_withBlurCoords8 o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = half4(v.texcoord.xy,1,1);
o.offs = _MainTex_TexelSize.xy * half2(1.0, 0.0) * _Parameter.x;
return o;
}
v2f_withBlurCoords8 vertBlurVertical (appdata_img v)
{
v2f_withBlurCoords8 o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = half4(v.texcoord.xy,1,1);
o.offs = _MainTex_TexelSize.xy * half2(0.0, 1.0) * _Parameter.x;
return o;
}
half4 fragBlur8 ( v2f_withBlurCoords8 i ) : SV_Target
{
half2 uv = i.uv.xy;
half2 netFilterWidth = i.offs;
half2 coords = uv - netFilterWidth * 3.0;
half4 color = 0;
for( int l = 0; l < 7; l++ )
{
half4 tap = tex2D(_MainTex, coords);
color += tap * curve4[l];
coords += netFilterWidth;
}
return color;
}
v2f_withBlurCoordsSGX vertBlurHorizontalSGX (appdata_img v)
{
v2f_withBlurCoordsSGX o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.xy;
half2 netFilterWidth = _MainTex_TexelSize.xy * half2(1.0, 0.0) * _Parameter.x;
half4 coords = -netFilterWidth.xyxy * 3.0;
o.offs[0] = v.texcoord.xyxy + coords * half4(1.0h,1.0h,-1.0h,-1.0h);
coords += netFilterWidth.xyxy;
o.offs[1] = v.texcoord.xyxy + coords * half4(1.0h,1.0h,-1.0h,-1.0h);
coords += netFilterWidth.xyxy;
o.offs[2] = v.texcoord.xyxy + coords * half4(1.0h,1.0h,-1.0h,-1.0h);
return o;
}
v2f_withBlurCoordsSGX vertBlurVerticalSGX (appdata_img v)
{
v2f_withBlurCoordsSGX o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv = half4(v.texcoord.xy,1,1);
half2 netFilterWidth = _MainTex_TexelSize.xy * half2(0.0, 1.0) * _Parameter.x;
half4 coords = -netFilterWidth.xyxy * 3.0;
o.offs[0] = v.texcoord.xyxy + coords * half4(1.0h,1.0h,-1.0h,-1.0h);
coords += netFilterWidth.xyxy;
o.offs[1] = v.texcoord.xyxy + coords * half4(1.0h,1.0h,-1.0h,-1.0h);
coords += netFilterWidth.xyxy;
o.offs[2] = v.texcoord.xyxy + coords * half4(1.0h,1.0h,-1.0h,-1.0h);
return o;
}
half4 fragBlurSGX ( v2f_withBlurCoordsSGX i ) : SV_Target
{
half2 uv = i.uv.xy;
half4 color = tex2D(_MainTex, i.uv) * curve4[3];
for( int l = 0; l < 3; l++ )
{
half4 tapA = tex2D(_MainTex, i.offs[l].xy);
half4 tapB = tex2D(_MainTex, i.offs[l].zw);
color += (tapA + tapB) * curve4[l];
}
return color;
}
ENDCG
SubShader {
ZTest Off Cull Off ZWrite Off Blend Off
// 0
Pass {
CGPROGRAM
#pragma vertex vert4Tap
#pragma fragment fragDownsample
ENDCG
}
// 1
Pass {
ZTest Always
Cull Off
CGPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur8
ENDCG
}
// 2
Pass {
ZTest Always
Cull Off
CGPROGRAM
#pragma vertex vertBlurHorizontal
#pragma fragment fragBlur8
ENDCG
}
// alternate blur
// 3
Pass {
ZTest Always
Cull Off
CGPROGRAM
#pragma vertex vertBlurVerticalSGX
#pragma fragment fragBlurSGX
ENDCG
}
// 4
Pass {
ZTest Always
Cull Off
CGPROGRAM
#pragma vertex vertBlurHorizontalSGX
#pragma fragment fragBlurSGX
ENDCG
}
}
FallBack Off
}
My guess would be that I have to make the original Z-Value of the vertices available in the fragBlur8 function and then to tweak the calculations there depending on that value but I couldn't even make the first bit work. I tried some things but all I would ever get is a totally white/black/grey screen. I also tried rendering the depth to the screen as a starting point but couldn't manage that either although I sticked to code I found online. I'm quite lost at the moment and hope you guys can help me. If you need any further information concerning what I'm trying to achieve, just ask. Thank you very much in advance!
Your answer
Follow this Question
Related Questions
Custom sprite Shader works in 5.5, not 5.6 3 Answers
Disable angular shadow recieving in 2D? 0 Answers
Apply a shader to multiple 2D sprites 2 Answers
2D Lighting With Unity Free 2 Answers
2D displacement maps? 1 Answer