버텍스 및 프래그먼트 기초 셰이더 #2


월드 공간에 메시 노멀을 표시하는 셰이더


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
    // no Properties block this time!
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // include file that contains UnityObjectToWorldNormal helper function
            #include "UnityCG.cginc"
 
            struct v2f {
                // we'll output world space normal as one of regular ("texcoord") interpolators
                half3 worldNormal : TEXCOORD0;
                float4 pos : SV_POSITION;
            };
 
            // vertex shader: takes object space normal as input too
            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // UnityCG.cginc file contains function to transform
                // normal from object to world space, use that
                o.worldNormal = UnityObjectToWorldNormal(normal);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 c = 0;
                // normal is a 3D vector with xyz components; in -1..1
                // range. To display it as color, bring the range into 0..1
                // and put into red, green, blue components
                c.rgb = i.worldNormal*0.5+0.5;
                return c;
            }
            ENDCG
        }
    }
}
cs

노멀은 컬러를 만들어내는 것 외에도 모든 종류의 그래픽스 이펙트(조명,반사,실루엣 등등)에 사용된다.


#include "UnityCG.cgin" : UnityObjectToWorldNormal을 쓰기 위한 include


TEXCOORD0, TEXCOORD1 등은 텍스처 좌표 및 포지션과 같은 임의의 고정밀도 데이터를 나타낼 때 사용


UnityObjectToClipPos 오브젝트 공간의 버텍스를 화면으로 변환하는 유틸리티 함수

UnityObjectToWorldNormal :  객체 공간에서 화면으로 정점을 변형시키는 유틸리티 함수 


정규화된 벡터를 컬러로 시각화(-1.0~ +1.0 범위)

절반을 곱하고 나서 절반을 +


<적용 후>


월드 - 공간 노멀을 사용한 환경 반사


씬에서 반사 소스로 스카이박스가 사용 될 때 이 스카이박스 데이터를 담고 있는 

"디폴트"반사 프로브가 생성된다. (반사 프로브 : 내부적으로는 큐브맵 텍스처이다.)


큐브맵

__Cubemap__은 환경에 대한 반사를 나타내는 여섯 개의 사각형 텍스처 컬렉션입니다. 여섯 개의 사각형은 오브젝트를 둘러싸는 가상 큐브면을 형성합니다. 각각의 면은 월드 축의 방향을 따른 뷰를 나타냅니다 (위, 아래, 좌, 우, 앞, 뒤).

큐브맵은 오브젝트의 반사나 “주변 환경”을 캡처하는 데 사용됩니다. 예를 들어, skyboxes  environment reflections는 주로 큐브맵을 사용합니다.

유니티 도큐 : https://docs.unity3d.com/kr/current/Manual/class-Cubemap.html


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
 SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            struct v2f {
                half3 worldRefl : TEXCOORD0;
                float4 pos : SV_POSITION;
            };
 
            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // compute world space position of the vertex
                float3 worldPos = mul(_Object2World, vertex).xyz;
                // compute world space view direction
                float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                // world space normal
                float3 worldNormal = UnityObjectToWorldNormal(normal);
                // world space reflection vector
                o.worldRefl = reflect(-worldViewDir, worldNormal);
                return o;
            }
        
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the default reflection cubemap, using the reflection vector
                half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.worldRefl);
                // decode cubemap data into actual color
                half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR);
                // output it!
                fixed4 c = 0;
                c.rgb = skyColor;
                return c;
            }
            ENDCG
        }
    }
}
cs


mul(x,y)

: 두 행렬의 곱을 계산한다.

worldPos = mul(unity_ObjectToWorld, vertex).xyz;

월드공간 관측 방향 계산

worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos)); 

: 지정된 객체 공간 정점 위치에서 카메라 방향으로 월드 공간 방향을 계산하고 정규화 함

worldNormal = UnityObjectToWorldNormal(normal);

: 월드 공간을 노멀로 변환

reflect(-worldViewDir, worldNormal);

: 정반사광의 방향 벡터를 구하는 벡터반사 함수.

첫 번째 인자로 입사광의 방향벡터를 두 번째 인자로 반사면의 법선을 받는다.


UNITY_SAMPLE_TEXCUBE

 : 큐브맵을 샘플링하기 위한 빌트인 매크로

__unity_SpecCube0

: (셰이더 변수)사용중인 반사 프로브의 데이터를 포함하고있다

DecodeHDR 

: 반사 프로브 데이터에서 실제 컬러를 얻기 위함,반사 프로브 큐브맵을 특별하게 인코딩된 방식으로 저장


<적용 후>



노멀 맵을 사용한 환경 반영


노멀맵(Normal Maps)은 오브젝트들에 추가 지오메트리 생성 없이 추가 디테일을 생성하기 위해 종종 사용된다. 

노멀 맵 텍스처를 가지고 환경을 반사하는 셰이더를 만드는 예제


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
 SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            struct v2f {
                half3 worldRefl : TEXCOORD0;
                float4 pos : SV_POSITION;
            };
 
            v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // compute world space position of the vertex
                float3 worldPos = mul(_Object2World, vertex).xyz;
                // compute world space view direction
                float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
                // world space normal
                float3 worldNormal = UnityObjectToWorldNormal(normal);
                // world space reflection vector
                o.worldRefl = reflect(-worldViewDir, worldNormal);
                return o;
            }
        
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the default reflection cubemap, using the reflection vector
                half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.worldRefl);
                // decode cubemap data into actual color
                half3 skyColor = DecodeHDR (skyData, unity_SpecCube0_HDR);
                // output it!
                fixed4 c = 0;
                c.rgb = skyColor;
                return c;
            }
            ENDCG
        }
    }
}
cs


노멀맵을 사용하기 시작하면 표면 노멀 자체가 피셀당 기준으로 계산되어야한다. 

즉 환경이 픽셀마다 어떻게 반사되는지도 계산해야한다.

이전 쉐이더 예제코드와 크게 변한 것은 없다. 다른 점은 이 셰이더는 모델의 각 버텍스에 대해서가 아니라 화면의 모든 픽셀 각각 대해 계산을 한다. 


노멀 맵 텍스처는 대부분의 경우 좌표 공간에서 표현되는데 이는 모델의 "표면을 따르는"것으로 생각할 수 있다. 지금 만들려는 셰이더는 탄젠트 공간 기반 벡터를 알아야 하고 텍스처로부터 노멀벡터를 읽어야하고, 이 벡터를 월드 공간으로 변환하고 그 후 위 셰이더에서 모든 수학적 계산을 해야한다. 

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
    Properties{
        // normal map texture on the material,
        // default to dummy "flat surface" normalmap
        _BumpMap("Normal Map", 2D) = "bump" {}
    }
        SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            struct v2f {
                float3 worldPos : TEXCOORD0;
                // these three vectors will hold a 3x3 rotation matrix
                // that transforms from tangent to world space
                half3 tspace0 : TEXCOORD1; // tangent.x, bitangent.x, normal.x
                half3 tspace1 : TEXCOORD2; // tangent.y, bitangent.y, normal.y
                half3 tspace2 : TEXCOORD3; // tangent.z, bitangent.z, normal.z
                // texture coordinate for the normal map
                float2 uv : TEXCOORD4;
                float4 pos : SV_POSITION;
            };
 
    // vertex shader now also needs a per-vertex tangent vector.
    // in Unity tangents are 4D vectors, with the .w component used to
    // indicate direction of the bitangent vector.
    // we also need the texture coordinate.
    v2f vert(float4 vertex : POSITION, float3 normal : NORMAL, float4 tangent : TANGENT, float2 uv : TEXCOORD0)
    {
        v2f o;
        o.pos = UnityObjectToClipPos(vertex);
        o.worldPos = mul(_Object2World, vertex).xyz;
        half3 wNormal = UnityObjectToWorldNormal(normal);
        half3 wTangent = UnityObjectToWorldDir(tangent.xyz);
        // compute bitangent from cross product of normal and tangent
        half tangentSign = tangent.w * unity_WorldTransformParams.w;
        half3 wBitangent = cross(wNormal, wTangent) * tangentSign;
        // output the tangent space matrix
        o.tspace0 = half3(wTangent.x, wBitangent.x, wNormal.x);
        o.tspace1 = half3(wTangent.y, wBitangent.y, wNormal.y);
        o.tspace2 = half3(wTangent.z, wBitangent.z, wNormal.z);
        o.uv = uv;
        return o;
    }
 
    // normal map texture from shader properties
    sampler2D _BumpMap;
 
    fixed4 frag(v2f i) : SV_Target
    {
        // sample the normal map, and decode from the Unity encoding
        half3 tnormal = UnpackNormal(tex2D(_BumpMap, i.uv));
        // transform normal from tangent to world space
        half3 worldNormal;
        worldNormal.x = dot(i.tspace0, tnormal);
        worldNormal.y = dot(i.tspace1, tnormal);
        worldNormal.z = dot(i.tspace2, tnormal);
 
        // rest the same as in previous shader
        half3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
        half3 worldRefl = reflect(-worldViewDir, worldNormal);
        half4 skyData = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, worldRefl);
        half3 skyColor = DecodeHDR(skyData, unity_SpecCube0_HDR);
        fixed4 c = 0;
        c.rgb = skyColor;
        return c;
    }
    ENDCG
}
    }
}
cs


학습참고

1) https://docs.unity3d.com/kr/current/Manual/SL-VertexFragmentShaderExamples.html

반응형

+ Recent posts