Depth and Normal Textures (Part 3)

반응형

이전 글 : Depth and Normal Texture (Part 2)

원문 : Depth and Normal Texture (Part 3)


이 글은 세편의 시리즈로 이루어 져 있고 이 글이 마지막 편이다.

이전 두 글에서, 유니티에서 깊이 텍스처를 사용하는 법에 대해 이야기 했었다.

이제, DepthTextureMode.DepthNormals를 통해 기본적으로 depth와 뷰 공간 normal을

하나로 압축해서 depth + normal 텍스처를 사용하는 법을 알아볼 것이다.


아래 이미지가 우리가 만들 효과다.

아래 이미지로보는 장면이 뷰 공간 노멀을 색상으로 표현한 것이고,

그 다음 이미지가 깊이값을 표현한 것이다.




Depth + Normal Texture

만약  Part 1에서 다룬 내용이 기억 난다면, 유니티에서 깊이 텍스처를 만들기 위해

Camera.depthTextureMode를 사용하는 거에 대해 알 것이다.

유니티 문서에 따라, 이 모드를 사용하기 위해 두가지 모드 값이 있다.

1. DepthTextureMode.Depth : 깊이 텍스처 .

2. DepthTextureMode.DepthNormals : 깊이와 뷰 공간 노멀을 하나의 텍스처에 압축.


우리느 이미 DepthTextureMode.Depth는 익숙한데, DepthTextureMode.DepthNormals 을 통해

어떻게 depth와 normal을 얻어올 수 있을까?


DecodeDepthNormal을 사용하면 된다.

이 함수는 UnityCG.cginc include 파일에 포함되어 있다.

UnityCG.cginc



define된 내용은 아래와 같다.


inline void DecodeDepthNormal( float4 enc, out float depth, out float3 normal )
{
   depth = DecodeFloatRG (enc.zw);
   normal = DecodeViewNormalStereo (enc);
}

이  함수에서 어떤 일이 벌어지는가?

함수의 인자가 세개인 것을 알 수 있다. (float4 enc, out float depth, out float3 normal)

기본적으로, enc로부터 DecodeFloatRG 함수를 실행하여 depth 값을 얻고 DecodeViewNormalStereo 함수를

실행하여 normal값을 얻는 것을 알수 있다.


코드상에서 아래와 같이사용하면 된다.


DecodeDepthNormal(tex2D(_CameraDepthNormalsTexture, i.scrPos.xy), depthValue, normalValues);

depthValue는 화면의 깊이값을 가지고 있는 float 값이며 normalValue는 뷰 공간의 노말을 가지는 float3 값이다.

첫번째 인자값인 tex2D(_CameraDepthNormalsTexture, i.scrPos.xy), 이는 무슨 일을 하는 것인가?

글쎄..일반적으로, _CameraDepthNormalsTexture의 변수 타입은 Sampler2D이다.

DecodeDepthNormal은 어떤 float4 타입의 값을 요구한다.

그래서 우리가 해야 할 일은, 주어진 샘플러로 texture look up을 수행하는 함수에 tex2d를 제공하는 것이다.


tex2d의 첫번째 인자로 sampler을 넣어주면 되는데 위에서는 _CameraDepthNormalsTexture를 넣어주었고,

두번째 인자는 해당 픽셀 값을 찾을 좌표값을 넣어주면 되는데 위 경우에는 화면의 좌표값을 넣어주었다.

하지만 i.scrPos가 float4라서 형식에 맞추기 위해 xy만 넣어주었다.


The Shader

셰이더 코드는 아래와 같다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Shader "Custom/DepthNormals" {
Properties {
   _MainTex ("", 2D) = "white" {}
   _HighlightDirection ("Highlight Direction", Vector) = (1, 0,0)
}

SubShader {
Tags { "RenderType"="Opaque" }

Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

sampler2D _CameraDepthNormalsTexture;
float _StartingTime;
float _showNormalColors = 1; //when this is 1, show normal values as colors. when 0, show depth values as colors.

struct v2f {
   float4 pos : SV_POSITION;
   float4 scrPos: TEXCOORD1;
};

//Our Vertex Shader
v2f vert (appdata_base v){
   v2f o;
   o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
   o.scrPos=ComputeScreenPos(o.pos);
   o.scrPos.y = 1 - o.scrPos.y;
   return o;
}

sampler2D _MainTex; 
float4 _HighlightDirection;

//Our Fragment Shader
half4 frag (v2f i) : COLOR{

float3 normalValues;
float depthValue;
//extract depth value and normal values

DecodeDepthNormal(tex2D(_CameraDepthNormalsTexture, i.scrPos.xy), depthValue, normalValues);
if (_showNormalColors == 1){
   float4 normalColor = float4(normalValues, 1);
   return normalColor;
} else {
   float4 depth = float4(depthValue);
   return depth;
}
}
ENDCG
}
}
FallBack "Diffuse"
}

노멀값이 뷰 공간으로부터의 값이기 때문에 카메라를 움직이면 노멀값, 그에 따른 컬러값이 바뀐다는것을 명심해라.



카메라에 스크립트 붙이기

이 스크립트를 "DepthNormals.cs"라고 부르자. 

스크립트의 내용은 아래에 있고 "E"버튼을 누르면 셰이더의 값을 바꾸게해 뒀다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using UnityEngine;
using System.Collections;

public class DepthNormals : MonoBehaviour {

public Material mat;
bool showNormalColors = true;

void Start () {
   camera.depthTextureMode = DepthTextureMode.DepthNormals;
}

// Update is called once per frame
void Update () {
   if (Input.GetKeyDown (KeyCode.E)){
      showNormalColors = !showNormalColors;
   }

   if (showNormalColors){
      mat.SetFloat("_showNormalColors", 1.0f);
   } else {
      mat.SetFloat("_showNormalColors", 0.0f);
   }
}

// Called by the camera to apply the image effect
void OnRenderImage (RenderTexture source, RenderTexture destination){
   //mat is the material containing your shader
   Graphics.Blit(source,destination,mat);
}
}

결론

이제 Depth + normal texture로 부텅 depth 와 normal을 어떻게 얻어오는지 알게되었다.

이 내용은 셰이더를 어떻게 작업해야 하는지에 대한 완벽한 지침서가 아니라는 것을 알아줬으면 좋겠다.

내가 몇일동안 유니티에서 vertex/fragment shader와 깊이 텍스처를 통한 작업에 대한

경험을 간단하게 축약해 놓은 것이다.

네가 개발하고 있는 프로젝트에 유용한 정보가 되었길 바란다.


추가내용.

Script에서 사용하는 DepthTextureMode.DepthNormals는 

화면크기의 32비트(8비트/채널) 텍스처를, 뷰 공간 법선이 RG, 깊이가 BA 채널에 인코딩된 형태로 빌드한다.

법선은 Stereographic Projection(스테레오 투영)을 사용하여 인코딩 되며 깊이는 두 8비트 채널로 압축된 16비트 값이다.


// 0~1 사이의 값을 채널당 8비트 RG채널로 인코딩/디코딩.

inline float2 EncodeFloatRG( float v )

{

float2 kEncodeMul = float2(1.0, 255.0);

float kEncodeBit = 1.0/255.0;

float2 enc = kEncodeMul * v;   // float2(depth, depth*255)

enc = frac (enc);                 // enc의 소수점만 구한다.

enc.x -= enc.y * kEncodeBit;

return enc;

}

inline float DecodeFloatRG( float2 enc )

{

float2 kDecodeDot = float2(1.0, 1/255.0);

return dot( enc, kDecodeDot );

}


// http://docs.unity3d.com/kr/current/Manual/SL-BuiltinIncludes.html

// http://www.gamedev.net/topic/630239-encode-float-to-rg-or-rgb/


DepthNormals.cs

DepthNormals.shader



Reference Link

- cg wiki frac

-












'Unity > Unity Graphics' 카테고리의 다른 글

Shader LOD  (0) 2016.09.29
hdr texture를 위한 bc6h  (0) 2016.09.05
Depth and Normal Texture (Part 2)  (0) 2016.06.13
Unity에서 Depth Texture의 사용  (0) 2016.04.29
Depth and Normal Texture (Part 1)  (2) 2016.04.29
TAGS.

Comments