Unity에서 Depth Texture의 사용
1. 깊이값 : 뷰 공간에서 원점(카메라 위치)와 정점간의 거리
v2f vert (appdata_base v)
{
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
// x is 1.0 (or –1.0 if currently rendering with a flipped projection matrix),
// y is the camera’s near plane, z is the camera’s far plane and w is 1/FarPlane.
o.depth = length(mul(UNITY_MATRIX_MV, v.vertex))* _ProjectionParams.w;
return o;
}
half4 frag(v2f i) : SV_Target
{
return fixed4(i.depth, i.depth, i.depth, 1);
}
2. 깊이값 : 투영공간에서 z 또는 w 값
o.depth = mul(UNITY_MATRIX_MVP, v.vertex).z * _ProjectionParams.w;
3. 유니티 에서는 깊이텍스처를 바로 접근하여 사용할 수 있다.
아래 링크 중 unity doc를 보면 사용방법을 알 수 있다.
ShaderTest.cs
void Start()
{
this.GetComponent<Camera>().depthTextureMode = DepthTextureMode.Depth;
}
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
Graphics.Blit(source, destination, mat);
}
ShaderTesht.shader
sampler2D _CameraDepthTexture;
v2f vert (appdata_base v)
{
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.scrPos=ComputeScreenPos(o.pos);
return o;
}
위에서 CumputeScreenPos이라는 빌트인 함수가 있다.
inline float4 ComputeScreenPos (float4 pos) {
float4 o = pos * 0.5f;
#if defined(UNITY_HALF_TEXEL_OFFSET)
o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w * _ScreenParams.zw;
#else
o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;
#endif
o.zw = pos.zw;
return o;
}
여기서 _ProjectionParams의 x값은 부호에 따라 1 또는 -1이 된다.
그리고 _ScreenParams.zw는 각각 1+1/width 와 1+1/height가 된다.
이 값들에 대한 설명은 이곳을 보면 된다.
w는 1로 생각하고 오프셋을 설정하여 식을 간략화 하면
float4 o = pos * 0.5f;
o.xy = float2(o.x, o.y) + _ScreenParams.zw;
위 두 식을 하나로 합치면 아래와 같이 된다.
o.scrPos.xy = o.pos.xy * 0.5 + _ScreenParams.zw*0.5;
여기서 _ScreenParams에 값을 대입하자
o.scrPos.xy = o.pos.xy * 0.5 + float2(1+1/width, 1+1/height)*0.5;
내가 만들어서 쓰던 함수는 아래와 같다.
inline float4 CustomScreenPos (float4 pos)
{
float4 o = pos;
// x, y, z 값을 w로 나누어 w는 1이되게 하고 xyz는 -1~1의 범위를 갖게 한다. NDC
// _MainTex_TexelSize.xy = 1/width, 1/height 이다
o.xy = (o.xy+_MainTex_TexelSize.xy) / o.w;
o.xy = float2(o.x+1, o.y+1) * 0.5; // -1~1 범위를 0~1로 변경
return o;
}
위 식도 간략화 해 보면 위와 같다.
결론적으로 보자면 w로 나누어서 perspective division을 거쳐서 -1~1의 범위로 만들고
다시 텍스처 좌표를 위해 0~1의 범위로 만든다.
다만 중간에 픽셀과 텍셀의 오프셋을 좀 추가해 준 것이다.
half4 frag (v2f i) : COLOR
{
float depthValue = Linear01Depth (tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)).r);
half4 depth;
depth.r = depthValue;
depth.g = depthValue;
depth.b = depthValue;
depth.a = 1;
return depth;
}
_CameraDepthTexture와 Shader Variables 대한 설명은 이곳을 참고하면 된다.
유니티에서는 깊이텍스쳐가 전역 변수 속성으로 셰이더에서 샘플링 할 수 있는데,
셰이더에서 sample로사용하기 위해 선언되어진 이름이 _CamDepthTexture이다.
_CameraDepthTexture는 항상 카메라의 첫번째 깊이 텍스처을 참조하지만,
그와 반대로 _LastDepthTexture는 어떤카메라에서든 렌더되어진
마지막 깊이 텍스처를 참조한다.
이는 절반해상도의 깊이 텍스처를 스크립트에서 만들어서 두번째 카메라에서
사용하여 후처리 셰이더가 가능하게 하도록 하는데 유용하다.
그리고 텍스처에서 읽어오 값(depth)를 0~1 값으로 보간하기 위해
Linear01Depth라는 함수를 사용한다.
위 코드는 이 링크에서 참고하였다.
4.
참고 링크
- Unity Doc, Depth Textures의 사용
- Fun with Shaders and the Depth Buffer
- Unit Doc, built in shader variables
- http://mgun.tistory.com/1663
- z버퍼란?
'Unity > Unity Graphics' 카테고리의 다른 글
Depth and Normal Textures (Part 3) (0) | 2016.07.24 |
---|---|
Depth and Normal Texture (Part 2) (0) | 2016.06.13 |
Depth and Normal Texture (Part 1) (2) | 2016.04.29 |
#pragma multi_compile (0) | 2016.04.23 |
Performance Tips when Writing Shaders (0) | 2016.04.15 |