Tessellation
https://docs.unity3d.com/kr/2018.1/Manual/SL-SurfaceShaderTessellation.html
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 |