화면 블러 처리 관련 자료 메모
정규 분포 함수도 가우스 함수 중 하나. 흐려짐 수치에 곱하기 유용함.
https://www.desmos.com/calculator/trhru2deav?lang=ko
최대값을 1로 한 함수 : https://www.desmos.com/calculator/gkpftewc06
위 함수 값을 가져다가 텍스쳐의 텍셀을 샘플링 한다.
블러의 설질상 해상도를 축소해도 별 문제가 없기 때문에 0.5 스케일로 샘플링 해도 됨.
아래는 셰이더 코드
Shader "Custom/BlurEffect"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
ZTest Off
ZWrite Off
Cull Back
CGPROGRAM
#pragma ARB_precision_hint_fastest
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
}
;
struct v2f
{
float4 vertex
:
SV_POSI _MainTex;
half4 _Offsets;
static const int samplingCount = 10;
half _Weights[samplingCount];
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
half4 col = 0;
[unroll]
for (int j = samplingCount - 1; j > 0; j--)
{
col += tex2D(_MainTex, i.uv - (_Offsets.xy * j )) * _Weights[j];
}
[unroll]
for (int j = 0; j < samplingCount; j++)
{
col += tex2D(_MainTex, i.uv + (_Offsets.xy * j)) * _Weights[j] ;
}
return col;
}
ENDCG
}
}
}
half4 _Offsets;
half _Weights[samplingCount];
위 두 개 값은 C# 스크립트에서 전달해서 처리.
frag 셰이더에서 samplingCount 만큼 루프 돌면서 tex2D 함수로 _MainTex 의 특정 uv 컬러값을 가져와서 연산.
C# 코드. 정상동작하지 않는 코드고, 참고만.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering ;
namespace Sample {
public class GaussianBlur : MonoBehaviour
{
[SerializeField]
private Texture _texture ;
[ SerializeField ] 10f )]
private float _offset = 1f ;
[SerializeField, Range( 10f , 1000f )]
private float _blur = 100f ;
private Material _material;
private Renderer _renderer;
// Apply sevral blur efect .
RenderTexture _rt2;
private float [] _weights = new float [ 10 ];
private bool _isInitialized = false ;
#region ### MonoBehaviour ###
private void Awake( )
{
Initialize ( ) ! Application.isPlaying)
{ return ;
}
UpdateWeights();
Blur();
}
#endregion ### MonoBehaviour ### /// < summary > /// Initialize (setup) /// </ summary >
private void Initialize( )
{
if (_isInitialized)
{
return ;
}
_material = new Material (_shader);
_material.hideFlags = HideFlags.HideAndDontSave ; // Down scale.
_rt1 = new RenderTexture(_texture.width / 2, _texture.height / 2, 0, RenderTextureFormat.ARGB32);
_rt2 = new RenderTexture(_texture.width / 2, _texture.height / 2, 0, RenderTextureFormat.ARGB32);
_renderer = GetComponent<Renderer>();
UpdateWeights(); _is / < summary > /// Do blur to the texture. /// </ summary >
public void Blur()
{
if ( ! _isInitialized)
{
Initialize();
}
Graphics.Blit(_texture, _rt1);
_material.SetFloatArray( "_Weights" , _weights);
float x = _offset / _rt1.width;
float y = _offset / _rt1.height ;
// for horizontal blur.
_material.SetVector( "_Offsets" , new Vector4(x, 0 , 0 , 0 );
Graphics.Blit(_rt1, _rt2, _material);
// for vertical blur.
_material.SetVector( "_Offsets" , new Vector4( 0 , y, 0 , 0 ));
Graphics.Blit(_rt2, _rt1, _material) ;
_renderer.material.mainTexture = _rt1;
}
private void UpdateWeights()
{
float total = 0 ;
float d = _blur * _blur * 0.001f ;
for ( int i = 0 ; i < _weights.Length; i ++ )
{
// Offset position per x.
float x = i * 2f ;
float w = Mathf.Exp( - 0.5f * (x * x) / d);
_weights[i] = w; if (i > 0 )
{
w *= 2.0f ;
}
total += w;
}
for ( int i = 0 ; i < _weights.Length; i ++ )
{
_weights[i] /= total;
}
}
}
}
이렇게 적용하면 끝.
원문 : Unityでガウシアンブラーを実装する
(유니티로 가우시안 블러 구현하기)
https://edom18.hateblo.jp/entry/2018/12/30/171214
위 방법은 Graphics.Blit 을 써서 GPU의 텍스쳐 데이터를 렌더 텍스쳐로 복사해서 처리하는 방식. 아래는 GrapPass 를 사용해서 멀티 패스 쉐이더로 처리하는 방식.
아래는 GrabPass 를 써서 화면을 캡쳐하는 기본 셰이더 샘플.
Shader "Custom/GrabPass"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
SubShader
{
Tags{ "Queue" = "Transparent" }
GrabPass
{
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
fixed4 color : COLOR;
};
struct v2f
{
float4 grabPos : TEXCOORD0;
float4 pos : SV_POSITION;
float4 vertColor : COLOR;
};
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.grabPos = ComputeGrabScreenPos(o.pos);
o.vertColor = v.color;
return o;
}
sampler2D _GrabTexture;
half4 frag(v2f i) : SV_Target
{
half4 col = tex2Dproj(_GrabTexture, i.grabPos);
return col;
}
ENDCG
}
}
}
https://docs.unity3d.com/kr/2022.3/Manual/SL-GrabPass.html
URP 에선 지원 안 함. Built-in Render Pipiline 에서만 지원.
아래는 셰이더 코드 전문
Shader "Custom/Blur"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_Blur("Blur", Float) = 10
}
SubShader
{
Tags{ "Queue" = "Transparent" }
GrabPass
{
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
fixed4 color : COLOR;
};
struct v2f
{
float4 grabPos : TEXCOORD0;
float4 pos : SV_POSITION;
float4 vertColor : COLOR;
};
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.grabPos = ComputeGrabScreenPos(o.pos);
o.vertColor = v.color;
return o;
}
sampler2D _GrabTexture;
fixed4 _GrabTexture_TexelSize;
float _Blur;
half4 frag(v2f i) : SV_Target
{
float blur = _Blur;
blur = max(1, blur);
fixed4 col = (0, 0, 0, 0);
float weight_total = 0;
[loop]
for (float x = -blur; x <= blur; x += 1)
{
float distance_normalized = abs(x / blur);
float weight = exp(-0.5 * pow(distance_normalized, 2) * 5.0);
weight_total += weight;
col += tex2Dproj(_GrabTexture, i.grabPos + float4(x * _GrabTexture_TexelSize.x, 0, 0, 0)) * weight;
}
col /= weight_total;
return col;
}
ENDCG
}
GrabPass
{
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
fixed4 color : COLOR;
};
struct v2f
{
float4 grabPos : TEXCOORD0;
float4 pos : SV_POSITION;
float4 vertColor : COLOR;
};
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.grabPos = ComputeGrabScreenPos(o.pos);
o.vertColor = v.color;
return o;
}
sampler2D _GrabTexture;
fixed4 _GrabTexture_TexelSize;
float _Blur;
half4 frag(v2f i) : SV_Target
{
float blur = _Blur;
blur = max(1, blur);
fixed4 col = (0, 0, 0, 0);
float weight_total = 0;
[loop]
for (float y = -blur; y <= blur; y += 1)
{
float distance_normalized = abs(y / blur);
float weight = exp(-0.5 * pow(distance_normalized, 2) * 5.0);
weight_total += weight;
col += tex2Dproj(_GrabTexture, i.grabPos + float4(0, y * _GrabTexture_TexelSize.y, 0, 0)) * weight;
}
col /= weight_total;
return col;
}
ENDCG
}
}
}
Unity로 손쉽게 만드는 Blur 쉐이더
https://ichinisann.tistory.com/37
URP 블러 처리
https://github.com/lukakldiashvili/Unified-Universal-Blur
게임 개발에 필수적인 내용을 담는 명서들을 소개합니다.
<유니티 교과서 개정6판>(유니티 최신 버전)
https://link.coupang.com/a/be3P0t
<대마왕의 유니티 URP 셰이더 그래프 스타트업>
https://link.coupang.com/a/bs8qyC
<리얼-타임 렌더링(REAL-TIME RENDERING) 4/e>
https://link.coupang.com/a/8VWas
<이득우의 게임 수학:39가지 예제로 배운다! 메타버스를 구성하는 게임 수학의 모든 것>
https://link.coupang.com/a/9BqLd
유니티 에셋 스토어 링크
https://assetstore.unity.com?aid=1011lvz7h
(링크를 통해 도서/에셋 구입시 일정액의 수수료를 지급받습니다.)