본문 바로가기

프로그래밍/Unity

[Unity] 테셀레이션

Tessellation

https://docs.unity3d.com/kr/2018.1/Manual/SL-SurfaceShaderTessellation.html

 

DX11/OpenGL Core 테셀레이션 지원 표면 셰이더 - Unity 매뉴얼

표면 셰이더는 DirectX 11/OpenGL Core GPU 테셀레이션을 다음과 같이 일부 지원합니다.

docs.unity3d.com

Displacement Texture : Height map

 

 

Shader "Tessellation Sample" {
   Properties {

      _Tess ("Tessellation", Range(1,32)) = 4
      _MainTex ("Base (RGB)", 2D) = "white" {}
      _DispTex ("Disp Texture", 2D) = "gray" {}
      _NormalMap ("Normalmap", 2D) = "bump" {}
      _Displacement ("Displacement", Range(0, 1.0)) = 0.3
      _Color ("Color", color) = (1,1,1,0)
      _SpecColor ("Spec color", color) = (0.5,0.5,0.5,0.5)
   }
            

   SubShader {
      Tags { "RenderType"="Opaque" }
      LOD 300
            
      CGPROGRAM
      #pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessFixed nolightmap
      #pragma target 4.6
 

      // unity에서 제공하는 테셀레이션 유틸

      #include "Tessellation.cginc"


      struct appdata {
         float4 vertex : POSITION;
         float4 tangent : TANGENT;
         float3 normal : NORMAL;
         float2 texcoord : TEXCOORD0;
      };


      sampler2D _DispTex;
      float _Displacement;

      float _Tess;

      // 버텍스 쉐이더

      void disp (inout appdata v)
      {

         // Height map 을 기준으로 노멀에 변화량을 적용해 해당 방향으로 버텍스 이동

         // _DispTex 의 밉맵 0번의 uv 좌표상에 r(빨강)색 정보를 가져와 크기를 정한다.
         float d = tex2Dlod(_DispTex, float4(v.texcoord.xy, 0, 0)).r * _Displacement;
         

         // 버텍스 위치 변경, 원하는 형태에 맞춰 방향을 설정

         v.vertex.xyz += v.normal * d;

      }

   

      // 테셀레이션함수, 고정크기라 설정된 값 그대로 리턴

      float4 tessFixed()

      {

         return _Tess;

      }

 

      // 거리에 따른 테셀레이션을 조절할 경우 아래처럼 테셀레이션 함수를 정의할 수 있음

      // 버텍스 입력인자를 3개 지정하면 삼각형 꼭짓점에 대한 버텍스 데이터가 전달됨.

      float4 tessDistance( appdata v0, appdata v1, appdata v2 )

      {

         float minDist = 10.0;

         float maxDist = 25.0;

         return UnityDistanceBasedTess( v0.vertex, v1.vertex, v2.vertex, minDist, maxDist, _Tess);

      }

 

      // 서피스 쉐이더 작성

      sampler2D _MainTex; 
      sampler2D _NormalMap; 

      fixed4 _Color;

 

      struct Input {
         float2 uv_MainTex;
      };

 


      void surf (Input IN, inout SurfaceOutput o) {
         half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
         o.Albedo = c.rgb;
         o.Specular = 0.2;
         o.Gloss = 1.0;
         o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
       }
       ENDCG
     }
     FallBack "Diffuse"
  }

 


테셀레이션 응용

눈 효과

https://www.youtube.com/watch?v=Sr2KoaKN3mU

 

height map으로 사용할 splat 텍스처에 동적으로 깊이를 표시해서 테셀레이션이 동작하도록함.

 

 

바닥면에 적용될 쉐이더

PBR 쉐이더 생성

Shader "Custom/SnowTrack" { 
   Properties {

      _Tess ("Tessellation", Range(1,32)) = 4

      _SnowColor ("Snow Color", color) = (1,1,1,0)
      _SnowTex ("Snow (RGB)", 2D) = "white" {} 

      _GroundColor ("Ground Color", color) = (1,1,1,0)
      _GroundTex ("Ground (RGB)", 2D) = "white" {} 
      _Splat ("SplatMap", 2D) = "black" {} 
      _Displacement ("Displacement", Range(0, 1.0)) = 0.3 
      

      _Glossiness("Smoothness", Range(0,1)) = 0.5

      _Metallic("Metallick", Range( 0, 1)) = 0.0

   } 
            

   SubShader { 
      Tags { "RenderType"="Opaque" } 
      LOD 300 
             
      CGPROGRAM 
      #pragma surface surf standard fullforwardshadows vertex:disp tessellate:tessDist
      #pragma target 4.6 
 

      // unity에서 제공하는 테셀레이션 유틸

      #include "Tessellation.cginc"


      struct appdata { 
         float4 vertex : POSITION; 
         float4 tangent : TANGENT; 
         float3 normal : NORMAL; 
         float2 texcoord : TEXCOORD0; 
      };


      sampler2D _Splat; 
      float _Displacement;

      float _Tess;

      // 버텍스 쉐이더

      void disp (inout appdata v) 
      {

         float d = tex2Dlod( _Splat, float4(v.texcoord.xy, 0, 0)).r * _Displacement; 
         

         // height map 기준으로 들어가게 변경

         v.vertex.xyz -= v.normal * d;

 

         // 다른 오브젝트의 collider가  바닥면 아래로 들어갈 수 있도록 전체적으로 올림

         v.vertex.xyz += v.normal * _Displacement;

      }

  

      // 거리기반 테셀레이션

      float4 tessDist( appdata v0, appdata v1, appdata v2 )

      {

         float minDist = 10.0;

         float maxDist = 25.0;

         return UnityDistanceBasedTess( v0.vertex, v1.vertex, v2.vertex, minDist, maxDist, _Tess);

      }

 

      // 서피스 쉐이더 작성

      sampler2D _GroundTex; 

      fixed4 _GroundColor;

      sampler2D _SnowTex; 

      fixed4 _SnowColor;

 

      half _Glossiness;

      half _Metallic;

 

      struct Input { 
         float2 uv_GroundTex; 

         float2 uv_SnowTex; 

         float2 uv_Splat; 
      };

 

      // _Splat 텍스처의 붉은 부분에 따라 두 텍스처 색상 lerp
      void surf (Input IN, inout SurfaceOutputStandard o) {

         half amount = tex2Dlod( _Splat, float4( IN.uv_Splat, 0, 0)).r ;

         fixed4 c = lerp( tex2D (_SnowTex, IN.uv_SnowTex) * _SnowColor, 

                              tex2D (_GroundTex, IN.uv_GroundTex) * _GroundColor,

                              amount );
         o.Albedo = c.rgb; 
         o.Metallic= _Metallic; 
         o.Smoothness = _Glossiness; 
         o.Alpha = c.a
       } 
       ENDCG 
     } 
     FallBack "Diffuse" 
  }

 

 

Splat 맵용 쉐이더

테셀레이션 처리를 위한 Splat맵은 동적으로 변경이 이루어져야 한다.

 

Unlit 쉐이더 생성

Shader "Unlit/DrawTracks"

{

   Properties

   {

      _MainTex( "Texture", 2D) = "white" {}

      _Coordinate( "Coordinate", Vector) = (0,0,0,0)

      _Color("Draw Color", Color) = (1,0,0,0)

      _Size( "Size", Range(1, 500)) = 1

      _Strength("Strength", Range(0, 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;

         };

 

         sampler2D _MainText;

 

         // TRANSFORM_TEX 매크로 사용을 위해 _ST 선언

         float4 _MainText_ST;

 

         fixed4 _Coordinate, _Color;

         half _Size, _Strength;

         

         

         v2f vert( appdata v )

         {

            v2f o;

            o.vertex = UnityObjectToClipPos( v.vertex );

            o.uv = TRANSFORM_TEX( v.uv, _MainTex );

            return o;

         }

 

         fixed4 frag( v2f i ) : SV_Target

         {

            fixed4 col = tex2D( _MainTex, i.uv );

 

            // 스크립트를 통해 hit 영역이 _Coordinate 로 전달됨

            // _Coordinate 와의 거리에 따라 색상을 조절

            float draw = pow(1 - distance( i.uv, _Coordinate.xy)), 500 / _Size);

            fixed4 drawcol = _Color * (draw * _Strength);

 

            // 0~1로 클램프

            return saturate( col + drawcol );

         }

}

 

public class DrawWithMouse : MonoBehaviour {

   public Camera _camera;

   public Shader _drawShader;

 

   private RenderTexture _splatmap;

   private Material _snowMaterial;

   private Material _drawMaterial;

   private RaycastHit _hit;

 

   [Range(1,500)]

   public float _brushSize;

 

   [Range(0, 1)]

   public float _brushStrength;

 

   void Start() {

      // 트랙을 그리기 위한 쉐이더에서 매터리얼 생성

      _drawMaterial = new Material( _drawShader );

 

      // 드로우 매터리얼 설정

      _drawMaterial.SetVector( "_Color", Color.red);

      _drawMaterial.SetFloat("_Strength", _brushStrength);

      _drawMaterial.SetFloat("_Size", _brushSize);

 

      // 바닥 메쉬의 매터리얼 얻기

      _snowMaterial = GetComponent<MeshRenderer>().material;

 

      // 렌더 텍스처 생성

      _splatmap = new RenderTexture(1024, 1024, 0, RenderTextureFormat.ARGBFloat);

 

      // 바닥 매터리얼에 트랙이 그려질 텍스처 설정(height map)

      _snowMaterial.SetTexture(" _Splat", _splatmap);

   }

 

   void Update() {

      if( Input.GetKey( KeyCode.Mouse0 ))

      {

         if( Physics.Raycast( _camera.ScreenPointToRay( Input.mousePosition), out _hit ))

         {

             // hit 된 지점의 uv 위치를 그리기 매터리얼에 설정

            _drawMaterial.SetVector("_Coordinate", new Vector4( _hit.textureCoord.x, _hit.textureCoord.y, 0, 0));

 

            RenderTexture temp =

                   RenderTexture.GetTemporary(_splatmap.width, _splatmap.height, 0, RenderTexutreFormat.ARGBFloat );

            

            // 이미 적용되어 있는 splatmap 이미지정보를 temp로 렌더링

            Graphics.Blit( _splatmap, temp );

 

            // temp를 drawMaterial 적용해 splatmap으로 렌더링

            Graphics.Blit( temp, _splatmap, _drawMaterial);

 

            RenderTexture.ReleaseTemporary( temp );

         }

      }

   }

 

   private void OnGUI()

   {

      GUI.DrawTexture( new Rect(0, 0, 256, 256), _splatmap, ScaleMode.ScaleToFit, false, 1);

   }

}

'프로그래밍 > Unity' 카테고리의 다른 글

[Unity] 인스펙터 변수 이름 변경  (0) 2018.11.13
[Unity] SteamVR 액션 설정  (0) 2018.11.05
[Unity] Post Process Stack v2  (0) 2018.10.31
[Unity] AR Foundation Package  (0) 2018.09.20
[Unity] 유니티용 DLL  (0) 2018.09.18
[Unity] VR 환경 끄거나 켜기  (0) 2018.09.14
[Unity] AssetDatabase  (0) 2018.09.12
[Unity] AssetBundle  (0) 2018.08.24
[unity3d] 서피스 쉐이더 정리  (1) 2018.06.26
[Unity] Mesh 생성 및 변경  (0) 2011.06.24