WinCNT

Alpha 값으로 Blue Noise를 이용한 Dither를 해보자! 본문

Unity/URP or Shader 관련

Alpha 값으로 Blue Noise를 이용한 Dither를 해보자!

WinCNT_SSS 2024. 6. 24. 13:43

서론

예전에 다른 프로젝트에서 Dithering를 구현해본 적이 있다

https://wincnt-shim.tistory.com/395

 

카메라와 오브젝트가 가까울 때 Dithering해서 반투명처럼 보이게 하는 셰이더 구현해보기!

서론 이번에는 카메라와 오브젝트가 가까워졌을 때, 디더링을 통해 오브젝트를 반투명처럼 보이게 만드는 셰이더에 대해서 정리하고자 한다 특허에 대한 얘기를 듣기도 했고, 왜 이러한 이런

wincnt-shim.tistory.com

 

그 때는 카메라와의 거리에 따라 Bayer Matrix 알고리즘을 이용한 디더링이었다

하지만 이번에는 방식을 바꿔서 Alpha 값에 따라 Blue Noise를 통한 디더링를 구현하고자 한다!


Bayer Matrix의 문제점들

사실 Blue Noise 방식으로 변경한 건 Bayer Matrix의 단점들을 회피할 수 있지 않을까라는 기대감에서였다

 

예를 들어 다음의 텍스처를 Bayer Matrix 방식으로 디더링했다고 해보자

 

그러면 다음과 같은 결과가 나온다

 

우선 첫번째 문제점으로 경계 주변에서 이상한 아티팩트가 생긴다는 것이다

ditherOut = saturate(_BaseTex.a) < 1.0 ? ditherOut : 1.0;

 

일단 이것은 다음과 같이 경계값을 조정하니 눈에 띄는 아티팩트는 사라졌지만 미세하게는 존재하는 것 같기도 하지만...

아무튼 문제점 중 하나라 할 수 있다

ditherOut = saturate(_BaseTex.a) < 0.99 ? ditherOut : 1.0;

 

두번째 문제점은 Banding 현상이다

여기서의 Banding이란 디더링 패턴이 레이어를 이루는 것을 말한다

 

참고로 이 Banding 현상은 실제로 게임 플레이 중에도 본 적이 있었다


Blue Noise!

그래서 이번에 사용할 것은 Blue Noise를 이용한 Dithering!!

블루 노이즈가 무엇인지는 Wiki로 갈음하려고 한다

사실 필자도 잘 모른다

Colors of noise

 

Colors of noise - Wikipedia

From Wikipedia, the free encyclopedia Power spectrum of a noise signal "Black noise" redirects here. For other uses, see Black Noise. In audio engineering, electronics, physics, and many other fields, the color of noise or noise spectrum refers to the powe

en.wikipedia.org

 

아무튼 세세한 건 넘어가고 Wiki의 Blue Noise 항목에 따르면, CG에서 블루 노이즈는 스파이크가 없는 노이즈를 의미하며 Dithering에 적합할 수 있다고 나와 있다

 

참고로 URP의 패키지에는 Blue Noise가 디폴트로 들어있다!

 

원래는 LOD Cross Fase에 사용되는 것 같은데 아무튼 감사히 잘 이용하도록 하자


샘플 코드!

아래는 그다지 관련 없는 부분은 최대한 생략한 샘플 코드이다

Properties
{
    // ...생략...
    _BlueNoiseTexture("Blue Noise", 2D) = "black" {}
    // ...생략...
}

SubShader
{
    // ...생략...
    Pass
    {
        HLSLPROGRAM
        
        // ...생략...
        TEXTURE2D(_BlueNoiseTexture);
        // ...생략...
            
        half4 frag(Varyings IN) : SV_Target
        {
            const half4 _BaseTex = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.texcoord);

            // Blue Noise Dither
            float alpha = _BaseTex.a * _BaseColor.a;
            half4 blueNoise = SAMPLE_TEXTURE2D(_BlueNoiseTexture, sampler_BaseMap, IN.texcoord * 8.0);

            float dithered = blueNoise.a;
            dithered = alpha - dithered;
            dithered = alpha > 0.05 ? dithered : -1.0;
            clip(dithered);
            
            return _BaseTex;
        }
        ENDHLSL
    }
}

 

기존의 Alpha에 blueNoise를 샘플링한 값을 빼주면 된다

물론 이번엔 딱히 참고한 게 없어서 필자는 쉽진 않았다

 

half4 blueNoise = SAMPLE_TEXTURE2D(_BlueNoiseTexture, sampler_BaseMap, IN.texcoord * 8.0);

여기서 UV좌표에 8.0을 곱하는 것은 Blue Noise 텍스처를 스케일링하기 위해서이다

(8.0은 매직 넘버라 딱히 적당한 숫자여도 된다)

이러면 해상도가 낮은 Blue Noise Texture라도 큰 차이 없이 사용할 수 있을테니 상황에 알맞게 사용하면 된다

 

dithered = alpha > 0.05 ? dithered : -1.0;

이 부분은 디더링한 알파 값이 일정 수준 이하가 되면 확실히 폐기하기 위한 코드이다

Base Color나 Texture에선 알파 값이 0인데도 Noise에 의해 일부 픽셀이 남는 케이스가 있어서 추가했다


결과


어디에 사용할 수 있을까?

아쉽게도 오브젝트의 반투명, 예를 들어 유리 등을 대체할 수는 없을 듯 했다

한 번 적용해봤는데 생각 이상으로 위화감이 엄청났다

 

대표적인 사용처로는 이전과 마찬가지로 카메라와 오브젝트가 가까울 때의 Dithering이라 생각한다

Bayer Matrix를 사용하는 것보다 사각적인 퀄리티는 더 높아진다고 생각한다

예를 들어 2024년 2월에 발매한 그랑블루 판타지 리링크에서도 Blue Noise를 이용한 Dithering으로 보이는 구현이 확인되었다

(카메라와 캐릭터 사이에 오브젝트가 끼어들면 그 Blue Noise Dithering으로 사라진다)

또는 원형 그림자(Shadow Blob)에도 사용할 수 있지 않을까?

 

그 외에는…아직 생각나는 건 없지만 언젠간 유용하게 쓰이지 않을까?


사족) URP 패키지에 있는 Blue Noise를 그대로 쓸 수 없나?

일단 샘플에서는 URP 패키지에 있는 Blue Noise를 복사해봐서 사용했지만, 이미 패키지에 있는 거 그대로 쓰고 싶다라는 생각이 들었을 수도 있겠다

 

결론부터 말하자면 가능했다

물론 Blue Noise가 설정된 변수는 internal(private이었나?)라서 바로 접근할 수는 없었기에 Reflection를 이용할 필요가 있었다

 

아래는 그 샘플 코드이다

필자는 최대한 셰이더와라는 범위 내에서 해결하고 싶었기에 ShaderGUI에서 구현해봤는데 문제 없이 작동했다

public class SampleShaderGUI : ShaderGUI
{
		public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
		{
		    if (materialEditor == null)
		        throw new ArgumentNullException("materialEditor");
		
		    _materialEditor = materialEditor;
		    Material material = materialEditor.target as Material;
		
		    FindProperties(properties);
		
		    if (_firstTimeApply)
		    {
		        RegisterHeader(material, _materialEditor);
		        _defaultInspector = false;
		        _firstTimeApply = false;
		        
		        // URP Asset -> Renderer Data List -> Post Process Data에 있는
		        // Blue Noise를 Reflection을 통해서 가져온다
		        UniversalRenderPipelineAsset pipelineAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset;
		        if (pipelineAsset != null)
		        {
		            FieldInfo propertyInfo = pipelineAsset.GetType().GetField("m_RendererDataList", BindingFlags.Instance | BindingFlags.NonPublic);
		            ScriptableRendererData[] rendererDatas = (ScriptableRendererData[])propertyInfo.GetValue(pipelineAsset);
		            if (rendererDatas != null && rendererDatas.Length > 0)
		            {
		                var urd = rendererDatas[0] as UniversalRendererData;
		                if (urd != null)
		                    blueNoise16LTex = urd.postProcessData.textures.blueNoise16LTex[0];
		            }
		        }
		        Texture texture = blueNoise16LTex;
		        material.SetTexture("_BlueNoiseTexture", texture);
		    }
		}
		// ...생략...
}

 

일단 빌드해서도 잘 작동하는 것까지는 확인!

물론 동적으로 키워드를 바꿀 때도 작동하는지까지는 확인하진 않았다…

(딱히 동적으로 머티리얼을 생성하는 게 아니면 되지 않을까? …아마)


마무리

만들어 놓은 코드를 보면 별 거 없지만 그를 위한 삽질이 좀 많았다

뭐 이런 것도 모두 결국에는 실력이 되는 것이겠지…?


참고 사이트

Dithered Shading Tutorial

 

Dithered Shading Tutorial

Dithering made simple

medium.com

https://www.youtube.com/watch?v=VG-Ux8RHMoA

Shader Advanced - Color Banding and Dithering | GPU Shader Tutorial

 

Shader Advanced - Color Banding and Dithering | GPU Shader Tutorial

A look into what color banding is and how to mitigate it using dithering.

shader-tutorial.dev

Dither Gradient Shader - Godot Shaders

 

Dither Gradient Shader - Godot Shaders

This is a dither shader inspired by Lucas Pope’s Return...

godotshaders.com

Godot Dither Shader

 

Godot Dither Shader by Sam Bigos

Post-process shader inspired by Obra Dinn, with demo project.

sambigos.itch.io

Blue-noise Dithered Samplingの実験をしてみた - 穴日記

 

Blue-noise Dithered Samplingの実験をしてみた - 穴日記

これはレイトレ合宿4!?のアドベントカレンダー記事です。 Blue-noise Dithered Sampling 先日開催されたSIGGRAPH2016には私も参加しましたが、興味深いものの実装が大変そうなものから何の役に立つの

raytracing.hatenablog.com

Blue noise crossfade / dither / lod

 

Blue noise crossfade / dither / lod

https://www.shadertoy.com/view/MdGfDz (animated test). I believe Chris said it's so good it's basically cheating - and he is right. [IMG] Source:...

forum.unity.com

Shadertoy

 

Shadertoy

0.00 00.0 fps 0 x 0

www.shadertoy.com