Home 10. 시멘틱과 전처리, 컨벤션과 좌표계
Post
Cancel

10. 시멘틱과 전처리, 컨벤션과 좌표계

HLSL

Fallback

SubShader들 중 하드웨어에 지원하는게 없다면

최종적으로 무엇을 사용할 지를 명시하는 구문입니다.

거의 Exception handling 수준이라고 볼 수 있죠.

시멘틱(Sementics)

9주차 과제중 일부

1
2
3
4
struct structName
{
    type name : SEMANTIC;
};

다음과 같이 정의할 수 있으며,

만약 Vertex shader에서 특수한 값들을 받고 싶다면, SEMANTIC을 작성해야합니다.

대게 다음과 같은 데이터를 사용합니다.

1
2
3
4
5
TEXCOORD
SV_POSITION //POSITION
NORMAL
TANGENT
COLOR

관련내용은 다음에 다시 제대로 정리합니다.

저번에 잠깐 언급했던 시멘틱입니다.

컴파일러 입장에서는 자료형만 알고있으므로,

변수의 의미(의도)를 표시하기위해 사용합니다.

셰이더 단계간에 전달되는 변수에 필요합니다.

https://docs.microsoft.com/ko-kr/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics?redirectedfrom=MSDN

버텍스 셰이더 입력

구조체는 모든 입력 멤버 변수의 의미를 명시해야합니다.

구조체가 뭐냐고요? 나중에 제대로 설명하겠습니다.

버텍스 셰이더의 출력

버텍스 셰이더의 출력은 프래그먼트 셰이더로 전달되는 과정에 보간기(Interpolator)를 거칩니다.

버텍스 셰이더에서 최종 계산한 버텍스의 클립 공간(CS) 좌표 – SV_POSITION (float4) SV는 System Value라는 뜻이며, DX10부터 들어왔습니다.

주요 시멘틱

1
2
3
4
Position (object space) - POSITION0, POSITION1, POSITION2, 
UV coordination          TEXCOORD0, TEXCOORD1, TEXCOORD2, 
Diffuse, Specular color - COLOR0, COLOR1, COLOR2, 
Normal vector            NORMAL0, NORMAL1, NORMAL2, 

TEXCOORD : 포지션 좌표, 텍스처 UV 등 높은 정밀도를 요구할 때 COLOR : 색상 범위의 낮은 정밀도도 괜찮을 때

이때 인덱스 0에 대해,

TEXCOORD0 = TEXCOORD (동일)

COLOR0 = COLOR (동일)

인터폴레이터 인덱스

키워드 끝의 숫자 0, 1, 2, 3, … 를 의미합니다.

일련번호는 인터폴레이터 번호이며 플랫폼 및 GPU에 따라 달라집니다.

8개까지는 안전하게 사용할 수 있으나(0~7) ,

그 이상 사용하려면 아래 소개한 링크의 문서를 참고하여 개수를 잘 설정하면 됩니다.

Untitled

참고로 URP shader그래프에도 인터폴레이터가 추가되었는데, (URP 12, 작성일 기준 따끈따끈)

Untitled

이렇게 customInterpolator를 사용하여

HLSL에서 작성한 것처럼 정점 쉐이더 단계에서 사용한 데이터를

픽셀 쉐이더 단계로 전달할 수 있습니다.

프래그먼트 셰이더의 출력

SV_Target을 사용합니다.

다수의 렌더 타겟을 사용할 경우,

SV_Target0, SV_Target1, SV_Target2, … 의 형태로 사용할 수 있습니다.

구조체(Struct)

흐흐흐… 그나저나 여러분은 철학을 좋아하시나요?

구조체 얘기를 할 때는 항상 의자가 생각납니다.

형이상학이란 무엇일까요?

앙리 베르그송은 형이상학을 ‘실재에 대한 절대적 인식’이라고 정의합니다.

실재에 접근하기 위한 두가지 방법, 직관과 분석에서 분석은 과학의 방법이며 형이상학은 직관을 목표로 해야한다는 이야기를 하면서 말이죠.

재차 형이상학이 무엇이냐 묻는다면, 또 다른 말로는 meta(페이스북 말고요), 인식론에서 ‘~에 대해서’ 라고 다른 개념으로부터의 추상화를 가르킵니다.

저희는 쇠로 만들어진 프레임 위에 얹혀진 나무나 플라스틱을, 체중을 싣고 다리를 쉬게 할 수 있는 목적으로써 사용할 때가 있습니다. 그리고 때때로 이것을 ‘의자’라고 부르죠.

여기서 쇠로 만들어진 .. 은 실체입니다. 하지만 저희는 이것을 ‘의자’ 라는 개념으로 말하죠.

제조공정상 달라질 수 밖에 없는 실체들은, 모두 같은 ‘의자’ 라는 개념으로 일컬어집니다.

….

아무튼 그래서 구조체는 이 개념을 말합니다.

어떤 영역을 설정해서, 영역에 어떠한 형태의 물건들이 배치될 것인지,

그런 어떠한 미리 정의한 형태를 의미합니다.

그래서 구조체를 정의하는 행위는

추상적 ‘개념’을 약속하며 기록한 것이라고 생각하면 될 것 같습니다.

1
2
3
4
5
6
7
8
struct Float5
{
	float a;
	float b;
	float c;
	float d;
	float e;
}

이 ‘개념’을 실제로 만들게 되면, 하나의 ‘실체’를 갖게 되는 것이죠.

1
2
3
4
Float5 floats;
floats.a = 1;
floats.b = 2;
...

Scope

Lexical Scope와 Dynamic scope

Lexical Scope는 선언된 위치를 기준,

Dynamic Scope는 호출된 시점을 기준으로 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var x = 1;

void print() 
{
    console.log(x); // dynamic : 999, Lexcical : 1
}

void dummy() 
{
    var x = 999;
    print();
}

dummy();

전처리 지시어(Preprocessor Directives)

  • 조건 분기 - #elif #else #endif #if #ifdef #ifndef → 정적 분기
  • #define - 상수나 매크로 정의
  • #undef - #define으로 정의된 상수나 매크로를 제거
  • #include - 외부 파일의 코드 삽입
  • #pragma - 하드웨어나 OS별 기능을 제공

  • #error - 컴파일시 에러 메시지를 생성. 이 지시어가 발동되면 컴파일이 중단됨.
  • #line - 컴파일러 내부에 저장되는 라인 번호와 파일명을 특정 값으로 설정

전처리 - PropertyDrawer

MaterialPropertyDrawer - [Toggle]

Toggle 키워드는 On 에 해당되는 키워드 하나만 세팅.

프로퍼티명_ON 의 규칙으로 설정됨

컨벤션

프로퍼티는 밑줄+대문자

구조체는 대문자, 멤버는 소문자

함수명은 파스칼

변수와 함수파라메터는 카멜

키워드나 사전정의는 대문자

그리고 보통 이런 컨벤션을 한번에 알 수 있는

가이드라인문서가 있으면 아주 좋습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Shader "Example/NamingConvention"
{
    Properties
    { 
        [MainTexture] _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalRenderPipeline" }

        Pass
        {
            
            HLSLPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"            

            //samplers
            sampler2D _MainTex;
            
            CBUFFER_START(UnityPerMaterial)
                float4 _MainTex_ST;
            CBUFFER_END

            struct Attributes
            {
                float4 positionObjectSpace   : POSITION;   
                float2 uvObjectSpace : TEXCOORD0;              
            };

            struct Varyings
            {
                float4 positionHClipSpace  : SV_POSITION;
                float2 uv : TEXCOORD0;
            };            

            Varyings vert(Attributes attributes)
            {
                Varyings output;
                output.positionHClipSpace = TransformObjectToHClip(attributes.positionObjectSpace.xyz);
                output.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return output;
            }

            half4 frag(Varyings i) : SV_Target
            {
                half4 customColor = tex2D(_MainTex, i.uv);
                return customColor;
            }

            ENDHLSL
        }
    }
}

좌표계


데카르트 좌표계(cartesian coordination system)

데카르트,카테시안 좌표계는 이를 포함하는 직교좌표계중 하나로 생각하는게 더 편합니다.

https://ashuatz.github.io/assets/(Graphic)10/Untitled%202.png

자주 보아왔던 이런 2차원 평면의 직교좌표계는 R2\mathbb R^2, 즉 R\mathbb RR\mathbb R의 곱집합입니다.

곱집합은 두 집합 A,BA,B의 원소들로 만들어지는 모든 순서쌍(a,b)(a,b)의 집합 이며,

다른말로는 카테시안/데카트르 곱(cartesian Product)이라고도 합니다.

즉 데카르트 좌표계는 실수 두 집합의 원소들로 만들어지는 순서쌍의 집합입니다.

엔진에서는 쉽게 (x,y), .xy의 꼴로 나타내는 vector2, float2가 이에 해당하겠죠.

실습


이번에는 강의중에 잠시 언급된 GrassShader를 구현해 볼 것입니다.

재료는….

https://roystan.net/articles/grass-shader.html

https://catlikecoding.com/unity/tutorials/advanced-rendering/tessellation/

여기있습니다.

구성은 단순합니다.

  1. 지오메트리 쉐이더를 통해 삼각형 생성하기
  2. 색상 및 회전 처리 (행렬곱)
  3. 테셀레이션
  4. 윈드
  5. 바닥점에만 해당하는 바닥맞추기-변환
  6. 스트립 형식을 통한 풀의 추가 버텍스 - 곡률 설정
  7. 그림자 설정
  8. 조명 설정
  9. 포그 설정

추가적으로 고려할 사항

  1. 거리기반 테셀레이션
  2. LOD (텍스처매핑)
  3. 렌더 텍스쳐를 통한 상호작용

이걸 다 정리하는건 말이 안되기 때문에 그냥 묵묵히 따라합니다.

단순합니다. 하라는대로 하는데 HLSL코드로만 잘 작성하면 됩니다.

구현

전처리

몇가지 전처리가 필요합니다.

일단 테셀레이션과 지오메트리를 사용하겠다는 명령어가 필요합니다

또한 vertex, hull, domain, geometry,fragment 에 대한 지정이 필요하죠.

1
2
3
4
5
6
7
8
9
10
#pragma require geometry
#pragma require tessellation tessHW

#pragma vertex geomVert
#pragma hull hull
//tessellation
#pragma domain domain
#pragma geometry geom

#pragma fragment frag

그리고 헐, 도메인 쉐이더등에 추가 어트리뷰트가 존재하는데,

이는 HLSL, 쉐이더 모델5에 존재하는 어트리뷰트입니다.

Untitled

패치 유형에 관한 설정을 할 수 있기 때문에,

삼각형으로 만들것이므로

Untitled

다음과 같이 설정합니다.

다른건 다 그렇다 쳐도 cw와 ccw는 무엇일까요?

cw는 clock wise, 시계방향이며, ccw는 counter-clockwise, 반시계방향입니다.

토폴로지에서 삼각형을 어떻게 생성할 것인가에 대하며, 쉽게생각하면 앞면과 뒷면에 대한 것 입니다

(backFaceCull이 외적을 통해 앞면과 뒷면을 검사하는것과 유사합니다.)

VertexShading

항상 버텍스 쉐이더에서의 작업은 단순합니다.

바로 위치를 변환하는 행동인데요,

일단 대부분의 정보를 월드공간 기준으로 변환합니다.

Untitled

Hull

InputPatch라는 새로운 데이터타입이 있습니다.

제어점 배열을 나타내는데, 간단하게 어떤 버텍스를 뱉을 것 인지만 알려주면 됩니다.

Untitled

여기서 패치 상수 데이터를 계산하는식은 다음과 같습니다.

Untitled

Domain

HullShader에서 받은 제어점의 정보로 보간 작업을 해줘야합니다.

이때 무게중심좌표를 사용하는데…

11-Convex-Line-Clipping

무게중심좌표는 기존 정리된 내용을 재사용하겠습니다.

아무튼 그렇게 무게중심좌표를 통한 삼각형 안에서의 특정 점의 보간을 위해 각 버텍스와 노멀, 탄젠트와 UV를 보간합니다.

그 후 해당정보로 버텍스정보를 만들어 넘깁니다.

Untitled

Untitled

Geometry

대망의 지오메트리 쉐이더입니다

생각보다 복잡하지만, 단순합니다.

탄젠트 공간을 만들어 로컬과 탄젠트공간을 변환할 수 있는 행렬을 만들고,

풀의 다향성을 주기위한 랜덤회전, 랜덤 꺾임, 바람에 대한 회전행렬을 구성하고

입력값을 기반으로 쌓아올립니다. 대신 strip으로 쌓아올리는데,

최종적으로 RestartStrip을 해서 토폴로지를 변환합니다.

아래 기술할 문제를 해결한 코드

아래 기술할 문제를 해결한 코드

1차 결과

그렇게 잘 넣으면..?

GIF 2022-05-16 오후 4-41-58.gif

안됩니다.

Untitled

SRP 배처를 끄면 위치만 조금 다른것을 확인합니다.

코드에서 무언가 공간변환이 잘못된 것일까요?

Untitled

input의 vertexPosition을 가져와 사용하는데, 해당 position은 world변환된 좌표.

하지만 TransformGeomToClip에서 world를 다시 modelToWorld로 재변환합니다.

최종적으로 TransformGeomToClip에 modeling space의 vertexPosition을 넣어주면 해결되는 문제.

msPos를 추가하여 해당데이터를 넣어줍니다.

Untitled

빛과 그림자

Untitled

일단 멀티컴파일 처리부터 합시다.

Untitled

_MAIN_LIGHT_SHADOWS 가 정의되어있거나,

_MAIN_LIGHT_SHADOWS_CASCADE가 정의되어있을 때,

저희는 이제 그림자를 받아와서 적용해야합니다.

Untitled

Untitled

하지만 그림자 색상이 야매입니다.

Untitled

이걸 가져올 수 있는 방법이 있을까요?

Input.hlsl을 뜯어보면 다음과 같은 데이터들이 있습니다.

Untitled

_SubtractiveShadowColor? 왠지 말이 되는 이름입니다.

Untitled

Attenuation의 값은 0에 가까울수록 그림자색상이 나와야하므로, lerp의 앞에 넣어봅니다.

Untitled

Environment Lighting이 안먹습니다.

Untitled

Untitled

그런데 이렇게넣으면

GIF 2022-05-18 오후 12-33-24.gif

뭔가 여전히 문제가 있습니다.

잘생각해보면 이것은 그림자와 관련이 없습니다.

Untitled

단순히 더해줍니다.

GIF 2022-05-18 오후 12-35-38.gif

_SubtractiveShadowColor 대신 unity_ShadowColor를 사용해줍니다.

이게 찐퉁인듯 하네요.

Untitled

environment reflection과 light와 normal의 내적은 적용하지 못했지만 대충 이정도면 ok인 것 같습니다.

포그 적용

아무래도 기존 버텍스-프래그먼트 쉐이더가 아니다 보니 조금 애로사항이 있습니다.

포그의 개념자체는 아주 단순합니다.

픽셀의 거리값을 기준으로 linear 혹은 exp로 최대 최소치를 설정한 0-1의 값을 구해낸 다음,

fogcolor랑 linear interpolation 처리하는 것이죠.

일단 멀티컴파일 처리를 한 후

Untitled

포그의 값을 넣을 수 있도록

최종 fragment 단으로 넘어가는 데이터에서 fogCoordination을 추가합니다.

Untitled

ComputeFogFactor의 인자는 clipSpace Position Z.

그러면 포그설정에 따라 0-1의 값으로 변환해줍니다

Untitled

Untitled

내부적으론

ComputeFogIntensity를 통해 exp 처리한 값으로 처리해주거나 하는데

Linear는 단순히 값을 반환하며, 이를 Lerp하여 뱉음.

Untitled

최종적으로 포그를 처리해서 뱉으면 짜잔

Untitled

포그가 달렸습니다.

플랫폼 문제

일단 OpenGLES 3.2에서 동작을 하지 않습니다.

불칸과 DX11에서는 잘 동작하고요.

OpenGL ES 3.2도 일단 geometry shader를 지원하는데 말이죠.

Untitled

이건근데 뭔가 할 수 있는게 없습니다

최종 결과물 (2차)

GIF 2022-05-18 오후 2-05-47.gif

거리에 따른 최적화

깊이를 가져와서, 거리가 멀면 테셀레이션을 적게 하도록 처리합니다.

Untitled

GIF 2022-05-18 오후 3-05-34.gif

이쯤되면 드디어 사람이 미쳤구나 싶습니다.

진짜 최종결과물 (3차)

GIF 2022-05-18 오후 3-08-24.gif

맺음말

머리가 깨지는 줄 알았씁니다. 진통제로 도핑하고 써서 글에 약기운이 있을수도 있습니다.

지오메트리 쉐이더는 검색해도 잘 안나와요.

특히 GLES3.2에선 왜 안되는지도 몰라서 잘 짠건지 뭘 실수한건지 세팅이 추가로 필요한지 긴가민가 했습니다…

생각보다 실제로 동작하고, 진짜 이게 되네?? 하는것들이 많아 재미있고 즐겁습니다.

environment reflection이 미적용된 부분은 나중에 cubemap을 배우며 메꿔봐도 괜찮을 것 같습니다.

코드

https://gist.github.com/ashuatz/ed87305c0e1bf7b88d2bce139b41dcc8

+2023.12 글을 옮겨적으며 코드에 문제가 있던 일부 부분을 gist상에서 수정했습니다.

+2023.12 아카이빙을 위해 과제당시의 글을 그대로 사용했기 때문에 당시의 개념적오류가 조금 존재할 수 있습니다.

This post is licensed under CC BY 4.0 by the author.
Contents