가장 큰 특징으로는
- UV와 Tangents좌표계의 의존성을 없애기
- Generic Surface 접근을 돕는다
- 평면 투사(Projection)를 사용한다
- 3가지의 mapping을 Blend한다
Texturing Without UV Coordinates
Texture mapping을 수행하기 위해 일반적으로 사용되는 방법은 Mesh에 각 Vertex가 저장된 UV-좌표계를 사용하곤 한다
그러나, 이렇게 하는 것이 유일한 방법은 아니다
때때로, 사용가능한 UV 좌표계가 없을 수도 있기 때문이다
예를 들어, 임의의 Shpae의 절차적 Geometry 과정일 때
런타임에서 Terrain이나 Cave를 생성할 때, 적절한 텍스쳐 UnWrap를 위한 UV 좌표계를 생성할 수 없다
이러한 경우, 우리는 대안적인 방법으로 매핑해야 한다
그 방법으로는 Triplanar Mapping 방법이 있다
여기서 잠깐!!
Texture Mapping이란?
컴퓨터 그래픽스 분야에서 가상의 3차원 물체의 표면에 세부적인 질감의 묘사를 하거나 색을 칠하는 기법이다
일반적으로는 2차원의 그림을 3차원 물체의 표면에 여러 가지 방법을 통하여 적용하고 이에 따라 컴퓨터 그래픽 화면을 만들어 나갈 때 마치 실제의 물체처럼 느껴지게 끔 세부 묘사를 하는 것이다? 말이 드럽게 어렵네
내 생각엔 햄버거를 떠올리면 될 것 같은데
포장지는 2차원의 그림이였다고 하고, 햄버거는 3차원의 물체였다고 하면 포장지가 햄버거 감싸면 하나의 물체처럼 느껴지니깐? 아니면 말고 난 이렇게 이해하련다
이렇게 하면 3D 장면을 구성하는데 필요한 다각형 및 조명 계산의 수가 대폭 줄어든다고 한다
예를 들면, 2차원의 지도를 제작할 때 입체인 지구표면을 평면으로 바꾸는 방법을 쓰지만,
텍스쳐 매핑의 경우에는 그 반대로 2차원 지도를 3차원 구에 적용하면, 지구본을 그린 영상을 얻을 수 있다
텍스쳐 매핑의 기법에는 높이 매핑, 범프 매핑, 노멀 배핑 등이 있다
텍스처 맵은 2차원의 컴퓨터 그래픽 그림으로 도형 또는 면의 단위인 폴리곤의 겉에 적용된다
다시 본론으로 돌아와서, 여기까지, 우리는 항상 UV좌표계를 사용할 수 있다고 생각해왔다
우리의 Lighting Input이나 Lighting Shader는 UV좌표계를 의존하는 파일들을 포함한다
Vertex UV에 의존하지 않는 대안적인 것을 만들어 낼 수 있지만, 현재 파일이 UV와 함께 또는 없이 모두 작동 할 수 있다면 더욱 편리해질 것이다
이 작업을 하기 위해선 몇 가지 변경이 필요하다
기본 UV 없이 수행된다
Mesh Data가 UV를 포함하지 않을 때, Vertex부터 Fragment Program으로 전달할 UV를 가지고 있지 않는다
표면 속성을 수집한다
UV가 없으면 조명에 사용되는 표면 속성을 결정하는 다른 방법이 있어야 한다
맞춤형 표면
표면 데이터를 얻는 방법을 변경할 수 있도록 다시 사용자 정의 함수의 정의를 허용해야 한다
이 기능을 사용하기 위해서는 입력이 필요한데 이 입력은 기본적으로 UV좌표가 될 것이며, 위치 및 법선 벡터일 수 있다
접선 공간이 없다
Unity의 표면 셰이더 접근 방식과 달리 접선 공간이 아닌 World 공간에서의 법선(Normal) 벡터로 작업 하고 있다
삼면 셰이더
음...일단 HLSL코드를 해석하기 어려우므로 코드 분석은 나중으로 미루겠다..ㅠ
Texturing With Three Planes
Fragment의 World Position은 3D Vector이다, 그러나 Regular Texture Mapping은 2D로 끝난다
따라서 UV좌표계로써 사용하기 위한 2차원을 선택해야 한다
3차원 공간속 Plane에다가 Texture를 매핑하는 것을 뜻한다
가장 명확한 선택은 XY좌표계를 선택하는 것이다
surface.albedo = tex2D(_MainTex, parameters.position.xy);
위와 같이 하면 Z축을 따라 투영된 질감을 볼 수 있다
XZ좌표를 사용하면 Y축을 따라 투영할 수 있다
지형 텍스처링에 종종 사용되는 평면 텍스처 매핑에 해당한다
Single-Planar Mapping은 투영 축과 정렬될 때 작동한다, 그러나 이건 보기 안좋다
하나의 축가지곤 좋지 않을 결과물이 나오므로, 다른축과 함께 해야 더 좋아질 것이다
그래서, 3가지 매핑을 사용하는 것이 유용하며 , 3개의 다른 UV 좌표 쌍을 제공해야 한다
struct TriplanarUV {
float2 x, y, z;
};
TriplanarUV GetTriplanarUV (SurfaceParameters parameters) {
TriplanarUV triUV;
float3 p = parameters.position;
triUV.x = p.zy;
triUV.y = p.xz;
triUV.z = p.xy;
return triUV;
}
x,y,z축을 구조체로 받아오고
GetTriplanarUV함수에서 파라미터의 포지션을 받아오고
xy, xz, yz을 x,y,z에 집어넣는다
void MyTriPlanarSurfaceFunction (
inout SurfaceData surface, SurfaceParameters parameters
) {
TriplanarUV triUV = GetTriplanarUV(parameters);
float3 albedoX = tex2D(_MainTex, triUV.x).rgb;
float3 albedoY = tex2D(_MainTex, triUV.y).rgb;
float3 albedoZ = tex2D(_MainTex, triUV.z).rgb;
surface.albedo = (albedoX + albedoY + albedoZ) / 3;
…
}
GetTriplanarUV함수는 여기서 호출한다
위에서 x,y,z에 값을 집어넣었으니 집어 넣은 값을 다시 tex2D로 호출해서
albedo에 집어넣는다
그치만 블렌딩이 조화스러워 보이지 않는다...블렌딩 방법까지 패스...추후에 ㅎㅎ;;;
Albedo 외에도 Map에 저장할 수 있는 많은 표면 속성이 있다
예를 들어 금속성, 부드러움, 노멀 맵 등이 있다
음..
Shader "Standard Triplanar"
{
Properties
{
_Color("", Color) = (1, 1, 1, 1)
_MainTex("", 2D) = "white" {}
_Glossiness("", Range(0, 1)) = 0.5
[Gamma] _Metallic("", Range(0, 1)) = 0
_BumpScale("", Float) = 1
_BumpMap("", 2D) = "bump" {}
_OcclusionStrength("", Range(0, 1)) = 1
_OcclusionMap("", 2D) = "white" {}
_MapScale("", Float) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
CGPROGRAM
#pragma surface surf Standard vertex:vert fullforwardshadows addshadow
#pragma shader_feature _NORMALMAP
#pragma shader_feature _OCCLUSIONMAP
#pragma target 3.0
half4 _Color;
sampler2D _MainTex;
half _Glossiness;
half _Metallic;
half _BumpScale;
sampler2D _BumpMap;
half _OcclusionStrength;
sampler2D _OcclusionMap;
half _MapScale;
struct Input
{
float3 localCoord;
float3 localNormal;
};
void vert(inout appdata_full v, out Input data)
{
UNITY_INITIALIZE_OUTPUT(Input, data);
data.localCoord = v.vertex.xyz;
data.localNormal = v.normal.xyz;
}
void surf(Input IN, inout SurfaceOutputStandard o)
{
// Blending factor of triplanar mapping
float3 bf = normalize(abs(IN.localNormal));
bf /= dot(bf, (float3)1);
// Triplanar mapping
float2 tx = IN.localCoord.yz * _MapScale;
float2 ty = IN.localCoord.zx * _MapScale;
float2 tz = IN.localCoord.xy * _MapScale;
// Base color
half4 cx = tex2D(_MainTex, tx) * bf.x;
half4 cy = tex2D(_MainTex, ty) * bf.y;
half4 cz = tex2D(_MainTex, tz) * bf.z;
half4 color = (cx + cy + cz) * _Color;
o.Albedo = color.rgb;
o.Alpha = color.a;
#ifdef _NORMALMAP
// Normal map
half4 nx = tex2D(_BumpMap, tx) * bf.x;
half4 ny = tex2D(_BumpMap, ty) * bf.y;
half4 nz = tex2D(_BumpMap, tz) * bf.z;
o.Normal = UnpackScaleNormal(nx + ny + nz, _BumpScale);
#endif
#ifdef _OCCLUSIONMAP
// Occlusion map
half ox = tex2D(_OcclusionMap, tx).g * bf.x;
half oy = tex2D(_OcclusionMap, ty).g * bf.y;
half oz = tex2D(_OcclusionMap, tz).g * bf.z;
o.Occlusion = lerp((half4)1, ox + oy + oz, _OcclusionStrength);
#endif
// Misc parameters
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
}
ENDCG
}
FallBack "Diffuse"
CustomEditor "StandardTriplanarInspector"
}
깃 출처 - https://github.com/keijiro/StandardTriplanar
참고자료
https://catlikecoding.com/unity/tutorials/advanced-rendering/triplanar-mapping/