본문 바로가기
Shader CG

2.3 Noise 미로 만들기

by SimonLee 2023. 8. 11.

이번에 Random 함수를 사용해서 아래 모양을 만들어 봅시다.

 

Random 사용하여 미로만들기

위 그림을 처음 보면 복잡해 보여서 미로를 구성하는 벽이 굉장히 다양하게 있을것 같습니다.

선의 방향이 달라지는 부분을 기준으로 칸을 나누어 보면

한칸에 해당하는 선은 y=x 방향 y=-x+1 두개의 직선만 존재하는 것을 알수 있습니다.

 

음 2개 제외하고 다른 직선은 없습니다.

 

먼저 라인을 그려보는 것을 복습해 봅시다.

모르시는 분은 아래 링크 클릭!

https://graphicsimon.tistory.com/18 

 

10. Draw Lines

라인도 대각선 방향으로된 사각형의 영역이라고 생각하시면 됩니다. 사각형의 경우 렌더링 방법은 좌측하단 꼭지점과 우측 상단 꼭지점을 기준으로 영역을 지정하고, 좌측하단 꼭지점 영역 -

graphicsimon.tistory.com

 

아래 코드는 라인을 그리는 함수 인데, reverse라는 파라메터가 있습니다.

위 파라메터가 false 이면, y = x 그래프 모양의 라인을 그립니다.

위 파라메터가 true 이면, y = -x + 1 그래프 모양의 라인을 그립니다.

당연하게 y = -x + 1 그래프는 아래 모양이겠지요 ? ^^

float Line(float2 st, float thick, float blur, bool reverse) 
{
    if (reverse) 
    {
        st.x = -st.x + 1;
    }
    return smoothstep(st.x - thick - blur, st.x - thick + blur, st.y) -
        smoothstep(st.x + thick - blur, st.x + thick + blur, st.y);
}

 

이제 저 선을 반복해서 그려야 하는데,

반복되는 구간이 가로 10칸, 세로 10칸 입니다.

 

좌표를 확장하는 방법은 어떻게 하지요 ? 좌표에 몇배 확장 할 것인지 곱해주면 됩니다.

i.uv *= 10;

 

그리고 나서 frac함수의 리턴 값인 소수값을 좌표로 사용하면, 10개의 구간이 나뉘어 지는 것을 

저번 챕터에서 했습니다.

i.uv *= 10;
float2 fpos = frac(i.uv);
float3 color = float3(1, 0, 0);
color *= Line(fpos, 0.1, 0.05, false);
return float4(color, 1.0);

다음 처럼 선이 렌더링 됩니다.

reverse = true
reverse = false

랜덤함수를 적용해 봅시다.

랜덤 함수는 저번에 사용한 것과 동일한 것을 사용합니다.

 

랜덤 값의 리턴값은 float 이며, 이 값의 범위는 0 ~ 1 사이 입니다.

이 랜덤함수의 리턴 값은 확률이라고도 볼수가 있습니다.

float random(float2 pt) {
    const float a = 12.9898;
    const float b = 78.233;
    const float c = 43758.543123;
    return frac(sin(dot(pt, float2(a, b))) * c);
}

 

이 확률 값으로 비교하여

50%는 y = x 모양의 라인을 그리고, 50% 이하는 y = x - 1 모양의 라인을 그리면 될 것 같습니다.

 

그런데 저 10개로 나뉘어진 한칸의 영역을 어떻게 구분할수 있을까요 ?

쉐이더 개념에서 다시 말해보면,

가장 좌측 하단 선분이 있는 칸을 (0,0) 칸이라고 가정합시다.

저 한칸의 영역에 해당하는 수백개의 픽셀들이 동일하게 (0, 0) 칸에 포함 되는 것을 알 수 있어야 합니다.

그래야 (0, 0) 칸 에 해당하는 픽셀들에 대해  y = x 라인을 그려! 라고 할수 있는 것이지요

 

floor 함수를 사용하면 정수부분을 가져옵니다.

위에서 좌표를 10배로 확장시켰으니, 각 영역마다 0~9의 정수를 가져오겠네요.

 

float2 pos = floor (i.uv) 식을 적용하게 되면,

(0, 0)칸에 있는 모든 픽셀들의 pos 값은 pos.x = 0, pos.y = 0 입니다.

(1, 0)칸에 있는 모든 픽셀들의 pos 값은 pox.x = 1, pox.y = 0 입니다.

칸에 포함된 동일한 픽셀들은 pos 값을 동일하게 가져 가게 됩니다.

 

오케이,

그러면 이 pos 값을 가지고 random 함수의 첫번째 인자로 사용하게 되면,

random 함수의 리턴값은 위에서 나눈 칸마다 동일하게 되겠네요 !

 

i.uv *= 10;
float2 ipos = floor(i.uv);
float2 fpos = frac(i.uv);
float3 color = float3(1, 0, 0);

if (random(ipos) > 0.5) 
{
    color *= Line(fpos, 0.1, 0.05, true);
}
else {
    color *= Line(fpos, 0.1, 0.05, false);
}
return float4(color, 1.0);

random 함수에 floor(i.uv)를 적용한 값을 넣어 줘서 각 칸마다 동일한 확률값을 가져옵니다.

확률값은 0.5로 되어있는데 위 값을 변경하여, 

y=x 라인과, y=-x+1 라인이 조화롭게 나오는지 확인해보세요~

 

전체 소스 입니다.

Shader "SimonLee/Shader27Unlit"
{
    Properties
    {
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #define PI 3.14159265359
            #define PI2 6.28318530718


            #include "UnityCG.cginc"

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float4 screenPos: TEXCOORD2;
                float4 position: TEXCOORD1;
                float2 uv: TEXCOORD0;
            };

            v2f vert(appdata_base v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.screenPos = ComputeScreenPos(o.vertex);
                o.position = v.vertex;
                o.uv = v.texcoord;
                return o;
            }

            float random(float2 pt) {
                const float a = 12.9898;
                const float b = 78.233;
                const float c = 43758.543123;
                return frac(sin(dot(pt, float2(a, b))) * c);
            }

            float random(float2 pt, float seed) {
                const float a = 12.9898;
                const float b = 78.233;
                const float c = 543758.543123;
                return frac(sin(dot(pt, float2(a, b)) * seed) * c);
            }

            float Line(float2 st, float thick, float blur, bool reserve) 
            {
                if (reserve) 
                {
                    st.x = -st.x + 1;
                }
                return smoothstep(st.x - thick - blur, st.x - thick + blur, st.y) -
                    smoothstep(st.x + thick - blur, st.x + thick + blur, st.y);
            }

            fixed4 frag(v2f i) : SV_Target
            {
                i.uv *= 10;
                float2 ipos = floor(i.uv);
                float2 fpos = frac(i.uv);
                float3 color = float3(1, 0, 0);

                if (random(ipos) > 0.5) 
                {
                    color *= Line(fpos, 0.1, 0.05, true);
                }
                else {
                    color *= Line(fpos, 0.1, 0.05, false);
                }
                return float4(color, 1.0);
            }
            ENDCG
        }
    }
}

 

위 예제는 ShaderBook에서 참조했습니다.

https://thebookofshaders.com/

 

The Book of Shaders

Gentle step-by-step guide through the abstract and complex universe of Fragment Shaders.

thebookofshaders.com

 

'Shader CG' 카테고리의 다른 글

2.5 Gradient Noise  (0) 2023.08.17
2.4 Noise 함수 사용해보기  (0) 2023.08.12
2.2 Noise 모자이크 효과  (0) 2023.08.09
2.1 Noise TV 화면 만들어보기  (0) 2023.08.06
1.6 도형 - 라인 그려보기  (0) 2023.07.20