사실 조금 써놓고 추석이라는 핑계로 귀찮아서 데굴대고 있었는데
후후후 감사합니다. 손님
이거 책상에 앉지 않을 수 없군요
자 오늘은 지난 시간 UV 의 HLSL 버전의 시간입니다.
UV라... 여기서는 이론 설명이 조금 들어가게 되는데, 역시나 이론 설명 자세한건 책을 참고하시고요 (꾸준글)
책을 보신 분들은 UV 가 사실 이거라는거 다 알고 계시죠? 거럼요 을매나 좋은 책인데 당연히 알겠지 ( ...)
쉿 조용 나의 귀여운 아기고양이
아참참, R,G,B 랑 X,Y,Z 는 사실 같은거다라는거 말씀드렸죠? 여기에 하나 더 U,V,W 도 같은 말이랍니다. 보통 UV만 쓰지만... 같은건데 어디에 쓰냐 따라서 이름이 다르고 막 이래
그리고 유니티에서 사용하는 방식은 왼쪽 아래가 float2(0,0) 오른쪽 위가 float2(1,1) 이라고도 책에서 얘기했었죠.
즉 아래같은 상황일때
버텍스에는 사실 이런 숫자가 들어가 있는 것이란 말이다... 뭐 그런 거였습니다. 책에 있어요 책에. 비엘북스 출판사 사장님 보고 계십니까.... 제가 이렇게 열심히 홍보하고 있습니다. . 사장님 솔직히 말해서요.. 애초에 출판사 이름이 좀 신경쓰였 ... 아니 아무것도 아닙니다 ...
정말입니다
자 오늘은 HLSL이므로, 텍스쳐 한 장 받는 것 부터 시작합니다.
Shader "Example/URPUnlitShaderBasic"
{
Properties
{
_BaseMap("BaseMap",2D) = "white"{}
}
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
};
struct Varyings
{
float4 positionHCS : SV_POSITION;
float2 uv : TEXCOORD0;
};
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
CBUFFER_END
Varyings vert(Attributes IN)
{
Varyings OUT;
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
return OUT;
}
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
return color;
}
ENDHLSL
}
}
}
오늘도 역시나 다운로드도 있어염 ♥ 이걸로 시작하자고요
![](https://t1.daumcdn.net/keditor/emoticon/friends1/large/005.gif)
아 도네 받았으면 이 정도 서비스 할 의욕 당근 생기지요
UV는 어떻게 생겼나
그래요 뭐 UV는 숫자인가 봐요. 버텍스에 들어가 있고요. 그런가 보다 하는건데...
근데 뭐... UV 를 눈으로 봐야 .. 사람이 또 믿음이 가고 뭐 그러는거 아니겠어요? 우리가 또 그렇잖아요?
인간답지 않게 숫자로만 보면... 거 뭐 믿음이 가겠어요?
그래서 정말 UV가 조래조래 되어 있나 확인해 봅시다.
일단 이 그림을 보면 UV는 XY와 완전 동일한 건데 말입니다... (정말 동일)
여기서 U , 즉 X 만 생각해 보세요. 자 어서.
그럼 왼쪽은 0이고 오른쪽은 1이지요?
숫자는 색이니까, 이걸 색으로 나타내면 다음과 같겠군요 이걸 이번에는 눈으로 확인해 볼께요
UV의 X 만 출력해 봅시다
픽셀셰이더에서
IN.uv.x
를 출력하면 되겠군요
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
//UV의 U만 출력한다
return IN.uv.x;
}
오홍. 쿼드 하나 만들고 테스트 해보시면 좀 더 명확하게 알 수 있는데요. 귀찮아서 그냥 있던 공하고 박스 썼습니다.
보시면 0 ~ 1의 그라데이션 모양으로 U가 표현 가능한 것을 볼 수 있습니다.
그라데이션 색이 왜이래? 너무 흰 쪽으로 치우친 것 같은데? 하시는 분은 ... 이게 정상이예요 .. 아직 모르시겠는 분은 감마가 어디감마 를 참고하시면 되겠습니다.
이제 UV 중에 V , 즉 XY 중에 Y 를 출력해 보지요
흠 이번엔 아래가 0이고 위가 1이네요?
즉 이번엔 이걸 출력하려면
픽셀셰이더에서
IN.uv.y
를 출력하면 되겠군요
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
//UV의 V만 출력한다
return IN.uv.y;
}
오올 좋아요. 이번엔 세로로 가네..
자 그럼 X와 Y가 이렇게 생겼다는거니깐...
X Y
이걸 각각 R , G 채널에 넣고, B는 0으로 넣는다면
return float4(IN.uv.x , IN.uv.y, 0 , 1);
이렇게 겠지요. 리턴은 float4 로 해야 하니까 마지막 알파 자리에 1을 가뿐하게 넣어주고.
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
//UV를 이미지로 출력해 본다
return float4(IN.uv.x , IN.uv.y, 0 , 1);
}
자 그럼
후후후 어디서 많이 보던 그림이 나왔지요
즉 저 칼라의 의미가 UV인 거예요. 왼쪽 아래가 0,0 오른쪽 위가 1,1
색은 숫자고 숫자는 색인거지요 이게 그 얘기 중 하나예요 둘은 같은거
UV 의 타일링(Tiling) 과 이동(Offset)
UV 의 개념을 알게 되었으니 타일링과 옵셋(이동) 에 대해 알아보도록 하죠
일단 다시 처음으로 돌아가고 봅시다
자 저기에 보면 Tiling과 Offset이 있지요?
저게 사실 어떤 원리로 구현되어 있는지 확인하는 시간입니다.
이거 뭐 .. 여기서부턴 좀 더 간단해집니다.
일단 텍스쳐 한 장일때로 돌아가서 생각해 보죠
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
return color;
}
일단 타일링부터, 타일링은 곱셈입니다.
간단하게 UV에 숫자를 곱해주면 타일링이 동작하지요
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv * 2); //UV에 2를 곱해주면
return color;
}
UV는 float2로 되어 있는 변수이므로, xy에 각각 다른 숫자를 곱해주면 uv에 다른 타일링을 줄 수도 있습니다.
예를 들어 아래같이 해주면
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv * float2(3,1)); //U에 3를 v에 1을 곱해주면
return color;
}
U 만 3배 타일링이 된 텍스쳐를 만들 수 있습니다.
이제 대충 짐작하시겠지만 덧셈을 하면 Offset(이동) 이예요. 여기서부터는 쉽죠?
그냥 위 식을 좀 바꿔서
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv + float2(3,0)); //U에 3를 v에 0을 더해주면
return color;
}
요렇게 3, 0을 더해주면 (곱해주는건 1이 기본이지만 더해주는건 0이 기본이니까요)
아무 일도 일어나지 않습니다.
자 이건 뭐 간단한거예요. 정수로 이동해서 그래요.
이동은 1,2,3 처럼 정수로 이동하면 그냥 한 사이클이 돌아버려서 제자리처럼 보이는 것 뿐이예요. 사실은 완벽하게 3 텍스쳐만큼 움직인 것임.
그러므로 제대로 효과를 보려면 0.5 와 같이 소숫점을 넣어야 합니다.
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv + float2(3.4,0)); //U에 3.4를 v에 1을 더해주면
return color;
}
흐음.. .좋아요 곱셈은 타일링이고 덧셈은 이동(옵셋) 이라고 했으니까 둘다 동시에 넣어보는것도 재밌겠군요.
단 이번엔 프로퍼티까지 만들어서요.
셰이더 그래프는 프리뷰가 있어서 그런거 안만들어도 보기 좋지만, HLSL 코드로 짜려면 역시 프로퍼티로 조정해 보는게 좋지요
자 이건 과제! 만들어 보세요
목표로 할 건 이거예요.
정답 풀코드는 다음과 같습니다.
Shader "Example/URPUnlitShaderBasic"
{
Properties
{
_BaseMap("BaseMap",2D) = "white"{}
//프로퍼티 추가. 타일은 1이 기본이고 옵셋은 0이 기본인걸 잊지 마세요
//곱셈과 덧셈의 차이지
_TileX ("타일U", float) = 1
_TileY ("타일V", float) = 1
_OffsetX ("옵셋U", float) = 0
_OffsetY ("옵셋V", float) = 0
}
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
};
struct Varyings
{
float4 positionHCS : SV_POSITION;
float2 uv : TEXCOORD0;
};
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
//C버퍼에 넣어줘야겠죠
float _TileX;
float _TileY;
float _OffsetX;
float _OffsetY;
CBUFFER_END
Varyings vert(Attributes IN)
{
Varyings OUT;
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
return OUT;
}
half4 frag(Varyings IN) : SV_Target
{
// 곱하기와 더하기는 서로 뭐 충돌날게 없으니 그냥 주욱 나열해주면 곱하기부터 연산될거예요
// 아래 UV가 너무 길다고 생각된다면,
// float2 TileOffsetUV = IN.uv * float2(_TileX,_TileY) + float2(_OffsetX, _OffsetY);
// 처럼 변수를 하나 생성시켜 줘서 따로 계산을 하고
// 아래처럼 그 계산한 변수를 넣어주는 방법도 있겠죠
// half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, TileOffsetUV);
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv * float2(_TileX,_TileY) + float2(_OffsetX, _OffsetY));
return color;
}
ENDHLSL
}
}
}
Time을 이용한 흘러가기
타일링이 곱셈, 옵셋(이동) 이 덧셈이란걸 알고 있다는게 중요합니다.
이런걸로 뭘 할 수 있냐고요?
이런걸 할 수 있지요 . 바로 내장변수인 _Time을 더해주는 건데요
UV에 따라 흘러가게 할 수 있어서 유용하게 사용할 수 있답니다.
_Time은 시간인데요, 유니티가 켜졌을 때 부터 지금까지의 시간이라서 생각보단 큰 수가 들어가 있습니다. 지금 이순간에도 계속 커지고 있는 숫자인 거예요.
그리고 _Time 변수는 이렇게 써주면 됩니다.
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv + _Time.y);
return color;
}
_Time.y 라고 되어 있는게 보이실텐데요 이게 뭐냐면
https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html
Unity - Manual: Built-in shader variables
Built-in shader helper functions Shader data types and precision Built-in shader variables Unity’s built-in include files contain global variables for your shadersA program that runs on the GPU. More infoSee in Glossary: things like current object’s tr
docs.unity3d.com
요렇게 메뉴얼에 나와 있습니다.
즉 _Time은 float4의 변수이고, xyzw (rgba) 에는 각각 (1/20 초 , 초, 초*2, 초*3) 이 들어가 있다는 거예요.
궁금하신 분은 _Time.x 나 _Time.z 같은걸 넣어 보시면 알 수 있습니다.
그럼 여기서 잠깐 퀴즈로 응용 한 번 해 볼까요
1. 흘러가는 속도를 조절해 보세요
2. 대각선 말고 한 쪽 방향으로 흘러가게 해 보세요
어렵지 않으니 일단 고민해 보세요 . 답은 아래에!
1. 흘러가는 속도를 조절하려면 Time에 숫자를 곱해주면 되겠죠
프로퍼티로 빼주면 더더욱 좋고
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv + _Time.y *0.5);
return color;
}
2. 한 쪽 방향만 흘러가게 하려면 xy 두 쪽이 아니라 한 쪽에다가만 연산해 주면 되겠죠
덧셈이니까 0이 기본.
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv + float2(_Time.y ,0));
return color;
}
float4 _BaseMap_ST 와의 관계
여기쯤 되어서 코드로 하시는 분은 의문증이 생길 수 있는데요,
기껏 타일링과 옵셋을 만들었는데
여기 이미 만들어져 있는거랑 똑같잖아요?!?!?
네 맞습니다. 완전히 똑같은걸 구현한 거거든요
요게 어디서 구현되어 있냐면..
그냥 평범한 텍스쳐 출력하는 코드를 보시면,
float4 _BaseMap_ST;
를 변수선언하는 부분이 보이실 겁니다. 이 부분이
이 부분의 값이 들어가는 float4예요
그리고 UV에다가
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
이 연산하는 부분이 보이실 겁니다. TRANSFORM_TEX 가 사용되네요.
이 함수는 Macros.hlsl에 디파인으로 정의되어 include되어 있는데요,
#define TRANSFORM_TEX(tex, name) ((tex.xy) * name##_ST.xy + name##_ST.zw)
요렇게 디파인 되어 있습니다. 텍스쳐에 이름 받아서 _ST 붙여서 xy는 곱하고 zw는 더하는 거 보이시죠?
이 구현을 우리는 수동으로 해준 것 뿐이랍니다.
Shader "Example/URPUnlitShaderBasic"
{
Properties
{
_BaseMap("BaseMap",2D) = "white"{}
}
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
};
struct Varyings
{
float4 positionHCS : SV_POSITION;
float2 uv : TEXCOORD0;
};
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
CBUFFER_START(UnityPerMaterial)
//요기에서 값을 받아서
float4 _BaseMap_ST;
CBUFFER_END
Varyings vert(Attributes IN)
{
Varyings OUT;
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
//여기에서 그 연산을 합니다.
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
return OUT;
}
half4 frag(Varyings IN) : SV_Target
{
half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
return color;
}
ENDHLSL
}
}
}
유후 오늘은 UV의 조작에 대해 알아봤어요. 다음 시간에는 예제를 해 볼까나!!!
여러분 모두 즐추석되세요 !!
![](https://t1.daumcdn.net/keditor/emoticon/friends1/large/003.gif)
블로그 주인장에게 커피값을 후원할 수 있습니다!
댓글