Combining Textures [텍스쳐 결합]
여러 텍스처를 샘플링한다.
디테일 텍스쳐를 적용한다.
선형 공간에서 색상을 다룬다.
스플릿 맵을 사용한다.
[ 1. Detail Textures :세부 질감 ]
Texture로 표현하는 것은 한계가 있음
멀리 있는 것은 그런대로 괜찮으나, 가까이에 있는 것은 블러현상 처럼 뿌옇게 보일 수 있음
조밀하고 큰 Texture를 사용하는 것이 하나의 해결책일 수 있으나 한정적인 메모리를 생각하면 좋은 해결책은 아님
Mipmap도 이런 대안 중의 하나이지만, 여기서는 상대적으로 텍셀 밀도를 높일 수 있는 방법으로 타일링을 활용
타일화된 텍스처와 타일화 되지 않은 텍스처를 섞어서 사용하는 것도 좋은 방법임
Plane생성
Menu : GameObject >> Plane
Shader생성
Menu : Asset >> Create >> Shader >> Unlit Shader
Name : Textured With Detail
My First Shader.shader
Shader "Custom/Textured With Detail" { Properties { _Tint ("Tint", Color) = (1, 1, 1, 1) _MainTex ("Texture", 2D) = "white" {} } SubShader { … } }
Shader를 위한 Material 생성
Menu : Asset >> Create >> Material
Textured With Detail 드래그 >> (to) Material
Material 드래그 >> (to) Plane
멀리서 보면 괜찮아 보임. 그러나 너무 가까워지면 흐릿해짐.
[[ My First Shader.shader ]]
float4 MyFragmentProgram (Interpolators i) : SV_TARGET {
float4 color = tex2D(_MainTex, i.uv) * _Tint;
return color;
}
원래 샘플의 10 배를 타일링 : 타일 텍스처를 도입하여 텍셀 밀도를 높인다.
[[ My First Shader.shader ]]
float4 color = tex2D(_MainTex, i.uv) * _Tint; color = tex2D(_MainTex, i.uv * 10); return color;
이렇게하면 그리드가 훨씬 작아짐.
컴파일 된 코드에 텍스처 샘플이 하나만 있음. -> 컴파일러가 불필요한 코드를 제거.
(기본적으로 최종 결과에서부터 돌아가며 사용되지 않는 모든 것을 삭제함)
샘플을 대체하는 것이 아닌 결합이 목적이기에 서로 곱한다. 정확한 UV 좌표로 텍스처를 두 번 샘플링합니다.
[[ My First Shader.shader ]]
float4 color = tex2D(_MainTex, i.uv) * _Tint; color *= tex2D(_MainTex, i.uv); return color;
또, 하나의 텍스처 샘플로 끝남. -> 컴파일러가 중복 코드를 감지하고 최적화함. ->텍스처는 한 번만 샘플링됨.
( 결과는 레지스터에 저장되고 재사용됩니다. 컴파일러는 중개 변수 등을 사용할 때도 이러한 코드 중복을 감지 할만큼 똑똑합니다. 모든 것을 원래 입력으로 되돌립니다. 그런 다음 모든 것을 최대한 효율적으로 재구성합니다. )
이제 두 번째 샘플에 대해 UV 좌표 10을 팩합니다. 마침내 크고 작은 그리드가 결합 된 것을 볼 수 있습니다.
[[ My First Shader.shader ]]
color *= tex2D(_MainTex, i.uv * 10);
텍스처 샘플은 더 이상 동일하지 않으므로 컴파일러는 두 개를 사용해야 됨.
두 개의 텍스처를 함께 곱하면 결과가 더 어두워 진다.
적어도 하나의 텍스처가 흰색이 아닌 한, 텍셀의 각 색상 채널은 0과 1 사이의 값을 가지기 때문이다.
원래 텍스처를 밝게하려면 1보다 큰 값이 필요. 원래 색상의 두 배
color *= tex2D(_MainTex, i.uv * 10) * 2;
이런 방법은 세부 사항에 사용 된 텍스처를 재해석해야됨. 1을 곱해도 아무 것도 바뀌지 않는다.
그러나 세부 샘플을 두 배로 늘리면 1/2에 해당합니다. 즉, 흰색이 아닌 단색의 회색으로 하면 변경되지가 않는다.
그래서 우리는 회색을 중심으로하는 특수 디테일 텍스처가 필요하다.
분리 된 디테일 텍스처를 사용하려면 셰이더에 두 번째 텍스처 속성을 추가해야 됨.
회색은 기본 텍스처의 모양을 변경하지 않는다. -> 회색을 기본값으로 사용.
[[ My First Shader.shader ]]
Properties { _Tint ("Tint", Color) = (1, 1, 1, 1) _MainTex ("Texture", 2D) = "white" {} _DetailTex ("Detail Texture", 2D) = "gray" {} }
세부 텍스처를 재질에 할당하고 타일링을 10으로 설정.
물론 디테일 텍스처와 타일링 및 오프셋 데이터에 액세스하기 위해 변수를 추가해야 됨.
[[ My First Shader.shader ]]
sampler2D _MainTex, _DetailTex; float4 _MainTex_ST, _DetailTex_ST;
10으로 하드 코딩 된 곱셈을 사용하는 대신 디테일 텍스처의 타일링 및 오프셋 데이터를 사용해야 됨.
버텍스 프로그램에서 주 UV와 같은 최종 세부 UV를 계산할 수 있다. -> 추가 UV 쌍을 보간해야 한다.
[[ My First Shader.shader ]]
struct Interpolators { float4 position : SV_POSITION; float2 uv : TEXCOORD0; float2 uvDetail : TEXCOORD1; };
새로운 디테일 UV는 디테일 텍스처의 타일링과 오프셋을 사용하여 원래의 UV를 변환함으로써 생성됩니다.
[[ My First Shader.shader ]]
Interpolators MyVertexProgram (VertexData v) { Interpolators i; i.position = mul(UNITY_MATRIX_MVP, v.position); i.uv = TRANSFORM_TEX(v.uv, _MainTex); i.uvDetail = TRANSFORM_TEX(v.uv, _DetailTex); return i; }
이제 조각 프로그램에서 여분의 UV 쌍을 사용가능.
[[ My First Shader.shader ]]
float4 MyFragmentProgram (Interpolators i) : SV_TARGET { float4 color = tex2D(_MainTex, i.uv) * _Tint; color *= tex2D(_DetailTex, i.uvDetail) * 2; return color; }
디테일 텍스처에 따라 주 텍스처가 더 밝고 어둡게 된다.
디테일 텍스처의 가져 오기 설정에서 Fadeout Mip Maps를 활성화.
필터 모드를 자동으로 삼선 형으로 전환하여 회색으로 변하는 점진적으로 흐리게 표시.
그리드는 세부 사항에서 자세히는 명확하지 않은 상태로 전환되지만 일반적으로는 알 수 없습니다. 예를 들어 대리석 재질의 주 텍스처와 디테일 텍스처는 다음과 같습니다.
잡아서 그리드 텍스처에 사용한 텍스처 가져 오기 설정과 동일한 설정을 사용하십시오.
소재가 이러한 텍스처를 사용하면 디테일 텍스처의 페이딩이 더 이상 눈에 띄지 않습니다.
그러나 디테일 질감 덕분에 대리석은 훨씬 더 잘 보인다.
쉐이더는 감마 색상 공간에서 장면을 렌더링하는 동안 잘 작동하지만 선형 색상 공간으로 전환하면 잘못 될 수 있습니다.
사용하는 색상 공간은 프로젝트 전체의 설정입니다.
File >> Build Setting >> Player Settings >> Other Settings >> Rendering
GPU는 텍스처 샘플을 선형 공간으로 변환합니다. 또한 Unity는 재료 색상 속성을 선형 공간으로 변환합니다.
그런 다음 셰이더는 이러한 선형 색상으로 작동합니다. 그 후 프래그먼트 프로그램의 출력은 다시 감마 공간으로 변환됩니다.
선형 색상을 사용하는 장점 중 하나는보다 사실적인 조명 계산을 가능하다는 것이다.
가벼운 상호 작용은 지수 적이 아닌 실제 생활에서 선형이기 때문이다.
그러나 선형 공간으로 전환 한 후에는 훨씬 더 어두워진다. 왜?
Gamma vs. linear space.
디테일 텍스처 샘플을 두 배로하기 때문에 ½ 값을 지정하면 주 텍스처가 변경되지 않는다. 그러나 선형 공간으로의 변환은 이것을 ½ 2.2 ≈ 0.22 근처의 값으로 변경됨.
두 배가되는 것은 대략 0.44이며, 1보다 훨씬 적습니다.
세부 텍스처의 가져 오기 설정에서 sRGB Sampling을 사용하지 않도록 설정하여이 오류를 해결할 수 있습니다. 이렇게하면 감마에서 선형 공간으로 변환되지 않으므로 쉐이더는 항상 원시 이미지 데이터에 액세스합니다. 그러나 디테일 텍스처는 sRGB 이미지이므로 결과가 여전히 잘못됩니다.
가장 좋은 해결책은 디테일 색상을 재 배열하여 다시 약 1의 중심에 배치하는 것이다. 선형 공간에서 렌더링 할 때만 해야 됨.
다행스럽게도 UnityCG는 곱하기 올바른 숫자를 포함 할 유니폼 변수를 정의합니다. 적절한 경우, rgb 구성 요소에 2 또는 대략 4.59가 들어있는 float4입니다. 감마 보정은 알파 채널에 적용되지 않으므로 항상 2입니다.
[[ My First Shader.shader ]]
color *= tex2D(_DetailTex, i.uvDetail) * unity_ColorSpaceDouble;
이 변경으로 인해 우리가 렌더링하는 색상 공간에 상관없이 디테일 소재가 동일하게 보인다.
Texture Splatting
상세한 큰 지형을 원할 때 쓰인다.
텍스처는 스플랫 맵으로 알려져 있습니다. 여러 지형 기능을 캔버스에 튄 것처럼 보인다. 보간 때문에 이 지도에는 고해상도가 필요하지 않다.
프로젝트에 추가 한 후 가져 오기 유형을 고급으로 전환 >> Bypass sRGB Sampling >> In Linear Space 활성화
맵을 바둑판 식으로 배열하지 않으므로 Wrap Mode >> Clamp로 설정
My First Shader를 복제 >> 이름 변경
Shader "Custom/Texture Splatting" { Properties { // _Tint ("Tint", Color) = (1, 1, 1, 1) _MainTex ("Splat Map", 2D) = "white" {} } SubShader { Pass { CGPROGRAM #pragma vertex MyVertexProgram #pragma fragment MyFragmentProgram #include "UnityCG.cginc" // float4 _Tint; sampler2D _MainTex; float4 _MainTex_ST; struct VertexData { float4 position : POSITION; float2 uv : TEXCOORD0; }; struct Interpolators { float4 position : SV_POSITION; float2 uv : TEXCOORD0; }; Interpolators MyVertexProgram (VertexData v) { Interpolators i; i.position = mul(UNITY_MATRIX_MVP, v.position); i.uv = TRANSFORM_TEX(v.uv, _MainTex); return i; } float4 MyFragmentProgram (Interpolators i) : SV_TARGET { return tex2D(_MainTex, i.uv); // * _Tint; } ENDCG } } }
이 셰이더를 사용하는 새로운 Matarial을 만들고 스플랫 맵을 기본 텍스처로 지정하십시오. 셰이더를 아직 변경하지 않았으므로 지도가 표시됩니다.
두 텍스처 중 하나를 선택할 수 있으려면 속성으로 셰이더에 추가해야합니다. Texture1과 Texture2로 이름을 정합니다.
[[ Texture Splatting.shader ]]
Properties { _MainTex ("Splat Map", 2D) = "white" {} _Texture1 ("Texture 1", 2D) = "white" {} _Texture2 ("Texture 2", 2D) = "white" {} }
원하는 텍스처를 사용할 수 있습니다.
그리드와 대리석 텍스처를 선택
C # 코드에서와 마찬가지로 셰이더 속성에 특성을 추가 할 수 있습니다. NoScaleOffset 특성은 이를 수행합니다.
타일링과 오프셋을 스케일 및 오프셋으로 참조합니다. 일관되지 못함.
여분의 텍스처에 이 속성을 추가하고 기본 텍스처의 타일링 및 오프셋 입력을 유지합시다.
[[ Texture Splatting.shader ]] (5번코드~)
Properties { _MainTex ("Splat Map", 2D) = "white" {} [NoScaleOffset] _Texture1 ("Texture 1", 2D) = "white" {} [NoScaleOffset] _Texture2 ("Texture 2", 2D) = "white" {} }
아이디어는 타일링 및 오프셋 컨트롤이 셰이더 관리자의 맨 위에 표시된다는 것.
그것들은 스플랫 맵 옆에 있지만 실제로 다른 텍스처에도 적용 할 것이다. 4와 같이 타일을 바릅니다.
이제 셰이더 코드에 샘플러 변수를 추가해야합니다. 그러나 해당 _ST 변수를 추가 할 필요는 없습니다.
[[ Texture Splatting.shader ]]
sampler2D _MainTex; float4 _MainTex_ST; sampler2D _Texture1, _Texture2;
이 방법으로 두 텍스처를 실제로 샘플링 할 수 있는지 확인하려면 조각 쉐이더를 변경하여 함께 추가
[[ Texture Splatting.shader ]]
float4 MyFragmentProgram (Interpolators i) : SV_TARGET { return tex2D(_Texture1, i.uv) + tex2D(_Texture2, i.uv); }
스플랫 맵을 샘플링하려면, 수정되지 않은 UV를 버텍스 프로그램에서 프래그먼트 프로그램으로 전달
[[ Texture Splatting.shader ]]
struct Interpolators { float4 position : SV_POSITION; float2 uv : TEXCOORD0; float2 uvSplat : TEXCOORD1; }; Interpolators MyVertexProgram (VertexData v) { Interpolators i; i.position = mul(UNITY_MATRIX_MVP, v.position); i.uv = TRANSFORM_TEX(v.uv, _MainTex); i.uvSplat = v.uv; return i; }
그런 다음 다른 텍스처를 샘플링하기 전에 스플랫 맵을 샘플링 가능
[[ Texture Splatting.shader ]]
float4 MyFragmentProgram (Interpolators i) : SV_TARGET { float4 splat = tex2D(_MainTex, i.uvSplat); return tex2D(_Texture1, i.uv) + tex2D(_Texture2, i.uv); }
값 1은 첫 번째 텍스처를 나타 내기로 결정했습니다. 우리의 스플랫 맵은 흑백이므로 RGB 채널 중 하나를 사용하여 이 값을 검색 할 수 있습니다.
R 채널을 사용하여 텍스처에 곱해 봅시다.
[[ Texture Splatting.shader ]]
return tex2D(_Texture1, i.uv) * splat.r + tex2D(_Texture2, i.uv);
우리는 기능적인 표시 재질을 가지고 있지만, 단지 두 개의 텍스처 만 지원합니다.
우리는 R 채널 만 사용하고 있으므로 R 채널과 G 채널을 추가하면 어떨까요?
그런 다음 (1,0,0)은 첫 번째 텍스처를 나타내고, (0,1,0)은 두 번째 텍스처를 나타내며, (0,0,1)은 세 번째 텍스처를 나타냅니다.
이 세 가지 사이에서 올바른 보간법을 얻으려면 RGB 채널이 항상 1을 더하는 지 확인해야합니다.
그러나 하나의 채널을 사용할 때 두 가지 텍스처를 지원할 수 있습니다.
이는 두 번째 텍스처의 가중치가 1 - R을 통해 파생 되었기 때문입니다.이 트릭은 여러 채널에서 작동합니다.
따라서 1-R-G-B를 통해 또 다른 텍스처를 지원할 수 있습니다.
이것은 세 가지 색상과 검정색을 가진 스 플랫지도로 연결됩니다.
함께 합산 된 세 개의 채널이 1을 초과하지 않는 한 유효한지도입니다. 여기에 그러한 지도가 있습니다. 이전과 같은 가져 오기 설정을 사용하십시오.
RGB 스플랫 맵을 지원하려면 셰이더에 두 개의 추가 텍스처를 추가해야합니다.
대리석 디테일과 테스트 텍스처를 할당
[[ Texture Splatting.shader ]]
Properties { _MainTex ("Splat Map", 2D) = "white" {} [NoScaleOffset] _Texture1 ("Texture 1", 2D) = "white" {} [NoScaleOffset] _Texture2 ("Texture 2", 2D) = "white" {} [NoScaleOffset] _Texture3 ("Texture 3", 2D) = "white" {} [NoScaleOffset] _Texture4 ("Texture 4", 2D) = "white" {} }
필요한 변수를 셰이더에 추가하십시오. 다시 한번, 여분의 _ST 변수가 필요 없습니다.
sampler2D _Texture1, _Texture2, _Texture3, _Texture4;
프래그먼트 프로그램 내에서 여분의 텍스처 샘플을 추가하십시오. 두 번째 샘플은 이제 G 채널을 사용하고 세 번째 샘플은 B 채널을 사용합니다. 최종 샘플은 (1 - R - G - B)로 변조됩니다.
return tex2D(_Texture1, i.uv) * splat.r + tex2D(_Texture2, i.uv) * splat.g + tex2D(_Texture3, i.uv) * splat.b + tex2D(_Texture4, i.uv) * (1 - splat.r - splat.g - splat.b);