본문 바로가기

Graphics/Unity

Chat GPT로 유니티 셰이더 만들기

(유니티 빌트인 렌더 파이프라인)

 

개요

요즈음 뜨거운 이슈인 Chat GPT를 통해 무엇을 해볼까 하다가, 셰이더를 만드는데에도 사용할 수 있을 지 문득 궁금해졌습니다. 백문이 불여일견이라고, 궁금증이 희미해지기 전에 바로 시도해보았습니다.

 


 

깜빡이는 간단한 셰이더 만들기

첫 번째로, 먼저 깜빡이는 간단한 셰이더를 Chat GPT에게 요청해보았습니다.

이전 시도에서는 깜빡이는 셰이더를 요청했을 때, 단순하게 Sin(Time) 값을 비교연산해 하얀색과 검정색을 반복하는 셰이더를 작성했는데 (심지어 비교연산도 if문을 통해 하고있었습니다), 이번 시도에서는 어떠한 효과를 원하는지 구체적으로 묻는 답변이 나왔습니다.

상세한 답변 덕분인지, 원하던 시간에 따라 값이 같이 변화하는 셰이더를 성공적으로 작성해냈습니다.

 

요청에 대한 Chat GPT의 답변

 

Chat GPT가 작성한 셰이더 적용 결과

 


 

촛불처럼 움직이는 버텍스 셰이더 만들기

다음 차례는, 조금 더 복잡한 셰이더를 요청해보기로 했습니다.

촛불처럼 일렁이듯이 움직이는 셰이더를 생각하고 요청했는데, 예상과는 달리 적절한 결과를 얻지는 못했습니다. 버텍스를 움직이긴 하는데, 괴상한 무언가가 탄생해버렸습니다. 심지어 답변도 중간에 끊어졌습니다...

 

Chat GPT의 답변과 그 결과물

 

Shader "Custom/CandleFlame" {
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
        _FlameSpeed ("Flame Speed", Range(0.1, 10)) = 1
        _FlameHeight ("Flame Height", Range(0, 2)) = 1
        _FlameIntensity ("Flame Intensity", Range(0, 1)) = 1
        _Color ("Color", Color) = (1, 1, 1, 1)
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            float _FlameSpeed;
            float _FlameHeight;
            float _FlameIntensity;

            v2f vert (appdata v) {
                v.vertex.y += _FlameHeight * (1 - (cos(_Time.y * _FlameSpeed) * 0.5 + 0.5));
                float2 offset = float2(
                    sin(v.vertex.y * 15 + _Time.y * 3),
                    sin(v.vertex.y * 22 + _Time.y * 4)
                ) * _FlameIntensity;
                v.vertex.xyz += float3(offset * v.vertex.w, 0);
                v.uv.y += (1 - (cos(_Time.y * _FlameSpeed) * 0.5 + 0.5));
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;
            float4 _Color;

            fixed4 frag (v2f i) : SV_Target {
                fixed4 col = tex2D(_MainTex, i.uv) * _Color;
                return col;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

 

위는 Chat GPT가 작성한 촛불 버텍스 셰이더입니다. 이해하기... 힘드네요 😵

 


 

GLSL → HLSL 변환하기

마지막으로는 GLSL을 유니티 셰이더로 변환하는 것을 시도해 보았습니다.
아래 링크의 하운즈투스 패턴 셰이더 코드를 Chat GPT에게 보내, 유니티 셰이더로 변환해달라고 요청했습니다.

 

 

Shadertoy

 

www.shadertoy.com

 

Shader "Custom/HoundToothPattern"
{
    Properties
    {
        _Sharpness ("Sharpness", Range(1, 100)) = 100
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            uniform float _Sharpness;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //float2 uv = frac(i.uv / _ScreenParams.y * 8); // Chat GPT가 작성한 코드
                float2 uv = frac(i.uv * 8);
                // Tiling 해주는 부분인데 왜 ScreenParams 를 추가로 곱했는지 모르겠음.
                // 원본 코드가 i.uv 대신 화면 해상도를 사용하는데,
                // 해당 부분을 동일한 역할인 _ScreenParams 로 변환해준듯함.

                float2 mask = clamp((uv - 0.5) * _Sharpness, 0, 1);

                //float color = clamp((abs((fract(abs(uv.x - uv.y) * 2.0 + 0.25) - 0.5) * 2.0) - 0.5) * _Sharpness / 5.0, 0, 1); // Chat GPT가 작성한 코드
                float color = clamp((abs((frac(abs(uv.x - uv.y) * 2.0 + 0.25) - 0.5) * 2.0) - 0.5) * _Sharpness / 5.0, 0, 1);
                // clamp(abs()) 부분 괄호 내 fract 는 GLSL 에서 HLSL로 변환하지 못함.

                color = max(color, min(mask.x, mask.y)) * max(mask.x, mask.y);

                return fixed4(color, color, color, 1);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

 

위 코드는 Chat GPT가 변환한 코드를 작동하도록 한번 더 수정한 결과물입니다. 원본 코드의 주석도 제거해주면서 대부분을 유니티 셰이더(HLSL)로 변환해주기는 했습니다.

다만 문맥을 읽지 못하고 단순히 해상도 값에 대응되는 ScreenParams 로 변환하는 부분도 있었고, 주석에도 표시해두었듯 color 값을 넣어주는 부분에서 fract 를 frac 으로 변환하지 못한 것과 같이 언어 간에 누락되는 부분도 있었습니다.

 

추가로 코드를 수정한 뒤 정상적으로 출력된 하운즈투스 패턴

 


 

마치며

Chat GPT를 통해 셰이더 작성이 가능하다는 것은 확실히 놀라운 일이기는 했습니다. 유니티 셰이더의 전체적인 구조를 이해한 듯 프로퍼티와 구조체, 함수와 변수들을 적절하게 작성해냈는데, 세간의 화제답게 유용한 도구이기는 한 것 같습니다.

그러나, Chat GPT의 한계 또한 명확히 느낄 수 있었습니다. 촛불처럼 움직이는 셰이더를 Chat GPT에게 요청했을 때, 예상치 못한 괴상한 결과물이 나왔었고, GLSL을 HLSL로 변환하는 과정에서도 Chat GPT의 한계를 경험했습니다. 결국, Chat GPT가 작성한 작업물을 다시 보며 체크할 수 있도록 셰이더에 대한 이해와 경험을 가지고 있는 것이 더 중요할 것 같습니다.

 

이 글 또한 Chat GPT에게 대략적인 단락과 구성을 설명한 뒤에 작성을 시도해보았지만, Chat GPT가 작성해준 내용은 전체의 10%도 채 사용하지 못한 것 같습니다. 아쉽네요.

자료수집이나 일반적인 검색에도 Chat GPT는 조금은 결이 맞지 않기는 합니다. 자료수집의 경우 출처나 자료를 자신이 만들어내 제공하기도 하고, 검색 또한 마찬가지기는 했습니다. 인터넷에서 많이 보았듯이, 허무맹랑한 이야기를 물어보면 그것이 진실인 양 답변해주기도 하고요.

그래도 생성형 AI 답게 함수명, 변수명, 한→영 고유명사 의역 등의 작문에는 확실히 강점이 있다고 느낍니다. '맥락'을 알아듣는다는 게 그 강점을 뒷받침해주는 부분 같습니다. 덧붙여서, 프로그래밍 언어에 대한 설명이나 유니티 내 기능에 대한 설명과 같이 기술적인 정보는 정말 잘 답변해주는 것 같습니다.

(이제부터 변수명이나 함수명 지을 때 고민은 Chat GPT에게 맡기는 걸로)

 

Chat GPT 의 함수명 작명 / 유니티 파티클 시스템에 대한 설명