블로그 이미지
자신의 단점을 메꾸는 것을 단(鍛)이라 하고 자신의 강점을 갈고 닦는 것을 련(鍊)이라 하여, 두가지를 합친 것을 단련이라고 부른다. 붕대마음

카테고리

전체목록 (667)
참고사이트 (8)
Goal (4)
Travel (10)
My Life (105)
Game (35)
Game Review (7)
Game Plan (0)
Books (5)
English (1)
Optimizing (12)
Study (218)
유용한 것들_etc (44)
유용한 것들_func (20)
Unity (48)
Unreal (87)
작업장 (54)
RenderMonkey (6)
정리요망 (1)
따라잡기 시리즈 (0)
링크용 (0)
Total345,595
Today85
Yesterday123

shader 작업을 하다가 ngui의 특정 패널에 붙어있는 uisprite가 내가 지정해둔

custom shader로 돌아가지 않는 현상을 발견했다.

그래서 아래와 같은 상황을 점검해 봤다.



1. UISprite에서 사용하고 있는 Atlas의 Material의 shader가 내가 만든 customSprite로

   설정이 되어있는가?  Yes

2. 혹시 UIPanel내에서 shader를 직접연결하는 코드가 있는가? No

3. 정말 내가 설정해둔 custom shader로 렌더링이 안되는가? Yes

   Frame Debug로 최종 렌더링시 지정한 shader를 쓰지 않는것을 발견.

   실제 customSprite shader의 pixel shader에서 return fixed4(1,0,0,1)으로 결과값이 반영안됨.

4. 다른곳에 있으면(다른 패널) 제대로 customSprite shader를 쓰는가? Yes


그렇다면 UIPanel의 문제인가?

이 UIPanel이 다른 UIPanel과 다른건 무었인가?


살펴보니 UIPanel의 Cliping 방식이 다름

SoftClip를 사용하고 있음.

그래서 SoftClip으로 전체 검색해 보니 의심스러운 곳이 보인다.

UIDrawCall.cs에서 CreateMaterial()함수에 보면 mClipCount가 1이 되고

이 값으로 셰이더 이름을 바꾸고 있다.


그래서 SoftClip으로 설정하면 렌더링 할 때 shader 이름이 "customSprite"가 아니라

"customSprite 1" 이 된다.


실제로 NGUI의 셰이더 코드들을 보면 아래와 같이 되어있다.


같이 셰이더에 뒤에 넘버링만 되어있다.

이는 UIPanel에서 하위 오브젝트를 그릴때 Cliping 속성값에 따라 

셰이더 파일을 바꿔주는 부분이 있어서 그와 짝을 맞추기 위해 셰이더 파일을

위와같은 네이밍 방식으로 만들어 준듯하다.


그래서 내가 만약 특정 atlas를 위해 shader을 구현하였다면

UIPanel의 특성에 맞게 추가 셰이더 파일을 넘버링하여 만들어야 하는 상황이 

생긴다는 것이다.



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

NGUI의 UIPanel의 속성에 따른 Shader File Name  (0) 2016.10.19
Shader LOD  (0) 2016.09.29
hdr texture를 위한 bc6h  (0) 2016.09.05
Depth and Normal Textures (Part 3)  (0) 2016.07.24
Depth and Normal Texture (Part 2)  (0) 2016.06.13
Unity에서 Depth Texture의 사용  (0) 2016.04.29
Posted by 붕대마음

댓글을 달아 주세요

Shader LOD

Unity/Unity Graphics / 2016. 9. 29. 03:13

unity shader에서는 mobile device의 성능에 따라 하나의 셰이더 파일에서

원하는 셰이더 내용을 선택해서 수행할 수 있도록 subshader block 과 

shader lod 라는 개념을 지원한다.


빌트인된 셰이더들은 아래와 같이 LOD를 적용한다.

VertexLit 종류 = 100

Decal, Reflective Vertex Lit = 150

Diffuse = 200

Diffuse Detail, Reflective Bumped Unlit, Reflective Bumped VertexLit = 250

Bumped, Specular = 300

Bumped Specular = 400

Parallax = 500

Parallax Specular = 600


~.cs

Shader.globalMaximumLOD = 300;

~.shader

LOD 300


위와같이 300이라고 설정하면 LOD값 300부터 가능하다.


Reference Link

- Unity Doc (shader level of detail)

- Unity Doc (shaderlab: subshader)

-

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

NGUI의 UIPanel의 속성에 따른 Shader File Name  (0) 2016.10.19
Shader LOD  (0) 2016.09.29
hdr texture를 위한 bc6h  (0) 2016.09.05
Depth and Normal Textures (Part 3)  (0) 2016.07.24
Depth and Normal Texture (Part 2)  (0) 2016.06.13
Unity에서 Depth Texture의 사용  (0) 2016.04.29
Posted by 붕대마음

댓글을 달아 주세요

유니티 릴리스 정보를 보면 기본적으로 hdr을 쓰기 위해 ARGBHalf를 쓰고 있는데

메모리 효율을 위해 추가 텍스처 포맷을 지원한다고 나와있다.

관련 내용만 해석해 보면 아래와 같다.


그래픽스 : 이제 새로운 두가지 포맷으로 텍스처를 임포트할 수 있다.

- BC6H는 높은 퀄리티의 압축된 RGB HDR 텍스처이다.

- BC7은 높은 퀄리티의 압축된 RGB(A)텍스처로 현재 데스탑(DX11+, GLCore),

  PS4 그리고 XBox 플랫폼에서 사용가능하다.

- 이러한 플랫폼에서 HDR 텍스처는 이제 기본적으로 FP16의 비압축 텍스처로와

  BC6H의 압축된 텍스처이다.

- 반사 프로브는 똑같이 작동한다(비압축 -> FP16, 압축 -> BC6H)

- BC7은 지원되는 플랫폼에서 "High Quality Compressed"라는 이름으로

  설정하여 임포트 하면 자동으로 선택될 것이다.

- 노트 : DX9과 MacGL은 이러한 포맷을 지원하지 않는다.

  만약 텍스쳐를 BC6H로 설정하면 자동으로 비압축 FP16으로,

  BC은 비압축 RGBA8로 변경될 것이다.


참고로...

BC6H는 3개의 컬러채널(16, 16, 16)을 가진다.

즉, half floating point 이다.


BC7은 3개의 컬러채널(4~7비트)과 0~8비트의 알파채널을 가진다.


Reference Link

- unity doc - 5.5.0b1 release notes

- msdn - texture block compression in direct3d 11

-

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

NGUI의 UIPanel의 속성에 따른 Shader File Name  (0) 2016.10.19
Shader LOD  (0) 2016.09.29
hdr texture를 위한 bc6h  (0) 2016.09.05
Depth and Normal Textures (Part 3)  (0) 2016.07.24
Depth and Normal Texture (Part 2)  (0) 2016.06.13
Unity에서 Depth Texture의 사용  (0) 2016.04.29
Posted by 붕대마음
TAG bc6h, bc7, HDR

댓글을 달아 주세요

이전 글 : 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 Textures (Part 3)  (0) 2016.07.24
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
Posted by 붕대마음

댓글을 달아 주세요

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

원문 : Depth and Normal Texture (Part 2)


이 글은 이전 글에 연속되는 글이다.

이 글은 총 세개의 글로 이루어져 있고 depth 와 normal 텍스쳐들을 다룬다.


Depth Texture 작업

이제 우리는 어떻게 깊이텍스처를 얻는지 배웠고, 그 값을 흑백 이미지로 출력했으니,

이걸로 좀 더 재미있는걸 해 보자.

Quanturm Conundrum의 차원 이동 효과에서 환경을 거쳐가는 빛의 링을 

간단한 버전으로 만들어 볼 예정이다.

어디서든 내가 바라보는곳의 중간지점에서 시작하는 것 대신에 내가 볼 수 있는

가장 먼 곳에서 시작해서 연속적으로 증가해서 카메라를 지나가게 만들거다.

추가로, 링이 오브젝트들을 지나가면 살짝 컬러색을 넣는다.


아래 이미지가 결과 화면이다.




Post Processing 전의 렌더된 이미지 얻기

우리가 만들려는 이미지는 카메라에서 그려진 이미지 위에 덮여질 것이다.

그래서 어떤 특수효과가 적용되기 전에 카메라에서 렌더링 된 직후의 이미지를 

얻어와야 할 필요가 있다.

이를 위해, shader에서 Properties 구문을 사용할 거다.

카메라로부터 그려진 이미지는 _MainTex라는 이름으로 들어온다.

Properties {
   _MainTex ("", 2D) = "white" {}
}


또한 shader에서 pass에서 변수이름을 알기 위해 아래와 같이 써 준다.

sampler2D _MainTex;

Time

효과가 움직일려면 시간값이 필요하다.

다행히도 유니티에 빌트인된 float4 _Time : Time(t/20, t, t*2, t*3) 값을 사용할 수 있다.

4개의 float형을 가진 float4를 사용하는 _Time 이라는 이름의 프로퍼티이다.

x는 t/20을 가지며 이는 보이는 것 그대로 time값을 20으로 나눈 것이다.

그 다음은 차례대로 time값, time값을 2로 곱한값, time값을 3으로 곱한값이다.


지금의 경우에는 0에서 1로 진행되는 값이 필요하다.

아래의 코드에서, _RingPassTimeLength 는 화면을 가로지르는 시간이다.

_StartingTime 는 처음으로 링이 움직일 때의 시간이고 _Time.y는 현재 순간의 시간이다.

float _RingPassTimeLength = 2;
float t = 1 - ((_Time.y - _StartingTime)/_RingPassTimeLength );

링이 처음으로 움직일 때, 시작 시간이 현재시간이므로 

_Time.y-StartingTime = 0이 되서 최종 t=1 이 된다.

그리고, _Time.y 값이 증가하면서 t의 값은 감소한다.

그래서 t값을 우리가 보고 있는 깊이값으로 적용할 때 화면을 통과할 수 있다.


User-Specified Uniforms

코드를 고치는 것 대신에 사용자가 에디터를 통해 조절할 수 값을 조절할 수 있게 할수 있다.

이를 위해, 우리는 User-Specified Uniforms를 사용할 것이다.

우리의 경우, 링이 화면을 통과하는 시간(_RingPassTimeLength)와 

링 자체의 너비(_RingWidth)를 아래와 같이 에디터 상에 노출 시킬 수 있다.


이 속성들은 셰이더 코드에서 선언해 줘야 하며 사용하기 위해

같은 이름으로 정의해 줘야 한다.

그래서, DepthRingPass.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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
Shader "Custom/DepthRingPass" {

Properties {
   _MainTex ("", 2D) = "white" {} //this texture will have the rendered image before post-processing
   _RingWidth("ring width", Float) = 0.01
   _RingPassTimeLength("ring pass time", Float) = 2.0
}

SubShader {
Tags { "RenderType"="Opaque" }
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

sampler2D _CameraDepthTexture;
float _StartingTime;
uniform float _RingPassTimeLength; //the length of time it takes the ring to traverse all depth values
uniform float _RingWidth; //width of the ring
float _RunRingPass = 0; //use this as a boolean value, to trigger the ring pass. It is called from the script attached to the camera.

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; //Reference in Pass is necessary to let us use this variable in shaders

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

   //extract the value of depth for each screen position from _CameraDepthExture
   float depthValue = Linear01Depth (tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)).r);

   fixed4 orgColor = tex2Dproj(_MainTex, i.scrPos); //Get the orginal rendered color
   float4 newColor; //the color after the ring has passed
   half4 lightRing; //the ring of light that will pass through the dpeth

   float t = 1 - ((_Time.y - _StartingTime)/_RingPassTimeLength );

   //the script attached to the camera will set _RunRingPass to 1 and then will start the ring pass
   if (_RunRingPass == 1){
      //this part draws the light ring
      if (depthValue < t && depthValue > t - _RingWidth){
         lightRing.r = 1;
         lightRing.g = 0;
         lightRing.b = 0;
         lightRing.a = 1;
         return lightRing;
      } else {
          if (depthValue < t) {
             //this part the ring hasn't pass through yet
             return orgColor;
          } else {
             //this part the ring has passed through
             //basically taking the original colors and adding a slight red tint to it.
             newColor.r = (orgColor.r + 1)*0.5;
             newColor.g = orgColor.g*0.5;
             newColor.b = orgColor.b*0.5;
             newColor.a = 1;
             return newColor;
         }
      }
    } else {
        return orgColor;
    }
}
ENDCG
}
}
FallBack "Diffuse"
}


그리고 아래의 스크립트는 카메라에 붙인다.

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

DepthRingPass.cs 은 깊이 텍스처처와 카메라에서 렌더링된 화면을 이미지로 

셰이더로 전달 하는 거 뿐만 아니라,  링의 시작점이 된다.

사용자가 "E"키를 누를 경우 _StartingTime값이 현재시간으로 설정되고 _RunRingPass가

1로 설정된다.

여기서 SetFloat 함수로 1을 전달하는 이유는 SetBool 같은 함수가 없기때문이다.


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
using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
public class DepthRingPass : MonoBehaviour {

public Material mat;

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

void Update (){
   if (Input.GetKeyDown(KeyCode.E)){
      //set _StartingTime to current time
      mat.SetFloat("_StartingTime", Time.time);
      //set _RunRingPass to 1 to start the ring
      mat.SetFloat("_RunRingPass"1);
  }
}

// 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);
}
}


머티리얼을 만들어서 우리가 만든 shader를 설정하고 이를 다시

위에 우리가 작성한 DepthRingPass.cs에 추가해 줘야 한다는 것을 기억하자.


part3 글에서는 셰이더에서 깊이와 노멀을  DepthTextureMode, DepthNormal들이

어떻게 동작하지는 살펴볼 것이다.


part2.unitypackage

DepthGrayscale.shader

DepthRingPass.shader

DepthRingPass.cs

PostProcessDepthGrayscale.cs



Reference Link

- unity doc : built-in shader variables

- tex2DProj

-





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

hdr texture를 위한 bc6h  (0) 2016.09.05
Depth and Normal Textures (Part 3)  (0) 2016.07.24
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
#pragma multi_compile  (0) 2016.04.23
Posted by 붕대마음

댓글을 달아 주세요



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;



1번2번.zip



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;

}

_CameraDepthTextureShader Variables 대한 설명은 이곳을 참고하면 된다.

유니티에서는 깊이텍스쳐가 전역 변수 속성으로 셰이더에서 샘플링 할 수 있는데,

셰이더에서 sample로사용하기 위해 선언되어진 이름이 _CamDepthTexture이다.

_CameraDepthTexture는 항상 카메라의 첫번째 깊이 텍스처을 참조하지만,

그와 반대로 _LastDepthTexture는 어떤카메라에서든 렌더되어진

마지막 깊이 텍스처를 참조한다.

이는 절반해상도의 깊이 텍스처를 스크립트에서 만들어서 두번째 카메라에서

사용하여 후처리 셰이더가 가능하게 하도록 하는데 유용하다.


그리고 텍스처에서 읽어오 값(depth)를 0~1 값으로 보간하기 위해

Linear01Depth라는 함수를 사용한다.


3번.zip


위 코드는 이 링크에서 참고하였다.




4.


참고 링크 

Unity Doc, Depth Textures의 사용

- Unity Doc, 카메라의 깊이 텍스처

- 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
Unity에서 Depth Texture의 사용  (0) 2016.04.29
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
Posted by 붕대마음

댓글을 달아 주세요

이 글은 번역글 이다.

원본 : http://williamchyr.com/2013/11/unity-shaders-depth-and-normal-textures/


나는 지난 삼일을 유니티 셰이더를 배우는데 보냈다.

많은 양의 기본 문서들을 보는 것은 그다지 어려운 일이 아니었다.

하지만 깊이 버퍼(depth buffer)까지 왔을 때, 후처리 (post process)의

특별한 효과에 무척 효율적이지만, 정보가 절대적으로 부족하며, 

유니티의 문서가 크게 도움이 되지 않았다.

예를들자면, 만약 어떻게 깊이 텍스처와 노멀 텍스처가 사용되어지는지

알려고 해도 유니티 문서의 조언은 "대체 셰이더 예제 프로젝트의 외곽검출이나

SSAO 효과에 사용되어진다."라고만 되어 있다.

이는 셰이더를 잘 파악하고 있는 누군가에게는 충분한 내용이지만,

초보에게는 그다지 도움이 되지 않는 내용이다.


어쨋든, 많은 시간의 코딩으로 시행착오를 거치고, 여러 블로그를 찾아 다니고

포럼에서 토픽에 관해서 논의해서, 드디어 유니티에서 깊이 텍스처와 

노멀 텍스처가 어떻게 작동하는지 알게되었다.

학습과정은 실망스러웠지만, 나는 내가 기억하는 동안 내가 무었을 했는지

글을 쓰는것이 좋겠다고 생각했다.


- 몇달안에, 나는 내가 무었을 했는지 잊을 것이고 내가 작성한 코드를

  이해 못할지도 모른다.

- 누군가가 같은 문제에 직면했을 때, 이 정보가 도움이 되길 바란다.

  몇몇의 블로그 글들은 내가 깊이 텍스처를 알아내는데 엄청난

  도움을 주었고, 이 글을 써 내려가는 동안에도 그 블로거들에게 정말 

  감사하고 있다.


자 이제 시작해 보자.


영감

나는 육개월 전즘에 셰이더를 공부하기시작했다.

그래픽 파이프라인을 설명하는 많은 튜토리얼, 

다른 종류의 셰이더 들 등이 기억이 난다.

이 즈음해서 나는 그런 셰이더들이 이해가 되지 않았고

무척 어려워 보였다.

나는 기존의 셰이더로 시작하여 간신히 몇가지를 알아냈고 내가 원하는 것을

알아내기까지 그 주위의 것들을 파헤쳤다.


이번에, Quantum Conundrum 이라는 게임의 차원 이동 효과를 

다시 만들어 보기를 원했다.



Quantum Conundrum을 아직 해보지 않았다면, 위 효과가 어떻게 나오는지 설명할 것이다.

기본적으로,  케릭터는 차원을 넘나 다닐 수 있는 능력을 가지고 있다.

중력이 가벼운 차원, 중력이 무거운 차원, 움직임이 느린 차원, 그리고 역중력 차원이 있다.

각 차원에서, 환경과 오브젝트의 모양이 끊임없이 변하며 서로 다른 물리적 특성을 가진다.

예를들어, 가벼운 차원에서는 모든 것이 매우 가볍기 때문에 소파를 집어들거나 다른 무거운 것들을

쉽게 들어올릴 수 있고, 무거운 차원에서는 모든것이 실제로 무거워 져서 

일반적으로 버튼을 무겁게 누르지 않았을 종이상자가 무거운 차원에서는

무겁게 눌러야 할 만큼 무거워진다.


게다가 속성을 바꾸기 위해, 모이는 모든것들을 바꾼다.

가벼운 차원에서 모든것은 구름처럼 보이는 반면에 무거운 차원에

있는 동안은 모든것이 금속 텍스처를 가진것 처럼 보인다.

위의 gif 이미지 에서 보면 플레이어는 기본 차원에서 무거운 차원으로 이동하고 나서,

가벼운 차원으로 갔다가 무거운 차원으로 돌아온 후 다시 기본차원으로 돌아온다.


가벼운 차원


무거운 차원


기본 차원



변환하는 장면


이 효과에 대해 몇가지 알아차린 것이 있다.

1. 방을 통과하는 빛의 고리는 항상 내가 쳐다보고 있는 오브젝트에서 시작해서

   바깥으로 퍼져나간다.

   나의 추측으로는 창문뒤로 링의 일부가 보이는걸로 봐서 모든 방향으로 팽창하는 

   구가 있는것 같다.

2. 빛의 링은 환경 뿐 아니라 오브젝트들과도 겹쳐진다.

3. 링은 차원의 텍스처를 분할하며, 새 차원의 텍스처는 링이 지나가기 전까지

   실제로 넣지 않는다. 이는 특정 시점에서 오브젝트들이 실제로 두개의 텍스처를 

   가진다는 것을 의미한다. (그림을 자세히 보면 정 가운데에 있는 의자와 의자의 오른쪽

   바래 바닥은 무거운 차원의 텍스처로 바뀌었지만 다른 부분은 기본 차원으로 남아있다.)


첫번째 단계 - 깊이 텍스쳐 구축

이 효과를 만들기 위한 아이디가 없고 어디서부터 시작해야 할지도 모르겠다.

몇몇 포럼과 트위터에 질문을 달고나서, 이 효과가 공간적 인식(spatially aware) 느낌을 주기 위해

깊이 버퍼를 사용하는 후처리 효과를 사용하는 셰이더라는 것을 알았다.


이 시점에 나는 내가 배웠던 셰이더를 대부분 다 까먹었기에 기본부터 다시 하기로 했다.

이쪽을 너무 파고싶지는 않기에 surface shader와 vertex/fragment shader의 차이점,

그리고 내가 찾은 실제로 도움을 주는 참고 링크들에 대해서 설명하는것은 하지 않기로 한다.

이 내용들이 처음에는 혼란스럽고 어려워 보이지만 몇번 읽어보고 셰이더를 작성하는 것을

연습 해 보면 내가 약속하건데 모든 것을 이해할 수 있을 것이다.

아래 글을 읽기 전에 적어도 위 링크들을 한번 보기를 권하며, 특히 셰이더에 신입이라면

더욱 위 링크들을 한번 보는것을 추천한다.


유니티에서 깊이 버퍼를 얻기 위해 렌더 텍스처를 사용해야만 하는데

이는 실시간으로 생성되어 지고 갱신되는 특수한 텍스처이다.

게임의 한 지역에서 어떤 일이 벌어지도록 보이게 하는 tv화면같은 것을

만드는데 이 텍스처를 사용할 수 있다.

깊이버퍼 또는 깊이 텍스처는 사실 단순히 화면에서

물체가 카메라로부터 얼마나 멀리 떨어져 있는지를 나타내는 값을 

기록하고 있는 렌더 텍스처이다.


그래서 어떻게 깊이 텍스처를 얻을 것인가?

첫번째로 깊이 텍스처를 만들기 위해 카메라에 대해 이야기 해야 하는데

Camera.depthTextureMode를 알아야 한다.

그리고나서 셰이더에 전달하기 위해 OnRenderImage 함수를 사용해야 한다.


PostProcessDepthGrayscale.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using UnityEngine;
using System.Collections;

//so that we can see changes we make without having to run the game

[ExecuteInEditMode]
public class PostProcessDepthGrayscale : MonoBehaviour {

   public Material mat;

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

   void OnRenderImage (RenderTexture source, RenderTexture destination){
      Graphics.Blit(source,destination,mat);
      //mat is the material which contains the shader
      //we are passing the destination RenderTexture to
   }
}

카메라 오브젝트에 위 스크립트를 붙인다.


셰이더

이제 깊이텍스처를 처리하고 디스플레이 하기 위해 셰이더를 만들것이다.

그냥 단순한 vertex, fragment 셰이더가 될 것이다.

기본적으로 카메라로부터 깊이 텍스처를 읽어서 screen 좌표에 깊이값을 출력한다.

DepthGrayscale.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
Shader "Custom/DepthGrayscale" {
SubShader {
Tags { "RenderType"="Opaque" }

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

sampler2D _CameraDepthTexture;

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

//Vertex Shader
v2f vert (appdata_base v){
   v2f o;
   o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
   o.scrPos=ComputeScreenPos(o.pos);
   //for some reason, the y position of the depth texture comes out inverted
   o.scrPos.y = 1 - o.scrPos.y;
   return o;
}

//Fragment Shader
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;
}
ENDCG
}
}
FallBack "Diffuse"
}

위 코드의 vertex shader에서 한가지 언급할 부분이 있다.

24번째 줄을 보면 o.srcPos.y = 1 - o.srcPos.y; 라고 있다.

몇가지 이유로 나의 깊이 텍스처는 y축이 반전되어 나오고 나는 이 부분을 

겪는 다른 사람을 찾을 수 없어서 결국 y값을 반전시켰다.

만약 결과값이 y값 반전이 되어 나온다면 24번째 줄을 주석처리 하면 된다.


이제 새 머티리얼을 만들어 위에서 만든 셰이더를 적용하고 PostProcessDepthGrayscale.cs를

붙이 카메라 오브젝트에 머티리얼을 설정 해 준다.


결과물




위와 같은 결과물을 볼 수 있다.

같은 박스를 여러개 만들어 위치를 다르게 두었다.

만약 색생값이 다르게 나오지 않고 같게 나온다면 카메라의 far clipping값을 수정해야 한다.


추가사항

이 글은 단순한 번역글이다.

다만 추가하고 싶은 내용이 있어서 글을 적는다.

shader의 24번째 줄의 의미에 관한 것이다.

uv의 좌표값은 시스템마다 조금씩 다르다.

나에게 익숙한 directx는 왼쪽위가 0이었고 오른쪽으로 갈수록 u값이 커지며 아래쪽으로 갈수록

v의 값이 커지는데 즉, 왼쪾위가 0,0으로 시작한다.

하지만 unity는 다중 플랫폼을 지원하며 opengl은 왼쪽위가 0,1로 시작한다.

그러므로 오른쪽 아래가 1,0이 된다.

그래서 나는 셰이더를 짤 때 이런 방식을 사용한다.

#if UNITY_UV_STARTS_AT_TOP

if( o.scrPos.y < 0.0 )

{

o.scrPos.y = 1.0 - o.scrPos.y;

}

#endif


관련 파일

DepthGrayscale.shader

PostProcessDepthGrayscale.cs



참고 링크

- unity doc, render texture

- game dev forver - unity uv

- unity doc, camera depth texture

- unity doc, camera depth texture 의 사용

-

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

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
#pragma multi_compile  (0) 2016.04.23
Performance Tips when Writing Shaders  (0) 2016.04.15
TRANSFORM_TEX  (0) 2016.04.15
Posted by 붕대마음

댓글을 달아 주세요

  1. 2019.10.01 16:26  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

가끔 고정된 셰이더 코드를 만들고 거기에 약간 다른 "variant" 셰이더 를 만드는게 편하다.

보통 이는 "mega shaders" 또는 "uber shaders"라고 불리며, 각 상황에 따라

다른 처리기 지시문과 셰이더 코드를 여러번 컴파일 해서 만들어졌다.

유니티에서는 그냥 #pragma multi_compile 또는 #pragma shader_feature 지시문을 

shader snippet에 추가하여 만들수 있다.

이는 surface shader에서도 가능하다.


런타임에 적절한 셰이더 variant는 material 키워드

(Material.EnableKeyword / DisableKeyward) 또는 

global shader keyword(Shader.enableKeyword / DisableKeyword)를 통해

뽑아서 사용한다.


어떻게 사용하는가?

#pragma multi_compile Test_Off TestOn

위와 같이 하면 TestOff와 TestOn이라는 두개의 shader variants가 만들어진다.

런타임때 material 또는 global shader 키워드를 기반으로 둘중 하나가 사용된다.

만약 두 키워드가 다 활성화 되어 있다면 첫번째 값인 TestOff가 사용된다.

 

두개 이상의 variant도 가능하다

#pragma multi_compile Test_Off TestOn Test_None


그리고 이름이 만약 전부 밑줄이라면 셰이더 variant는 지시문 없는

매크로로 정의되어 만들어 진다.

이는 일반적으로 두개의 키워드를 사용하는 것을 피하기 위해 사용된다.

#pragma multi_compile __ Test_On



참조 링크

http://forum.unity3d.com/threads/pragma-multi_compile.187817/

- UNITY - 정점쉐이더와 Fragment 쉐이더의 프로그래밍

- UNITY - Making multiple shader program variants

-

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

Unity에서 Depth Texture의 사용  (0) 2016.04.29
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
TRANSFORM_TEX  (0) 2016.04.15
Unity Tags  (0) 2016.04.12
Posted by 붕대마음

댓글을 달아 주세요

float : 32비트, 높은 정밀도
half : 16비트, 중간 정밀도, -6000 ~ 6000
fixed : 11비트, -2.0 ~ 2.0, 그리고 1/256.

color 와 벡터 길이 fixed를 쓰셈
그리고 적당히 half를 쓰고 더 높은 정밀도가 필요하면 float를 쓰셈.


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

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
TRANSFORM_TEX  (0) 2016.04.15
Unity Tags  (0) 2016.04.12
Built in shader variables  (0) 2015.10.30
Posted by 붕대마음
TAG Fixed, float, Half

댓글을 달아 주세요

TRANSFORM_TEX

Unity/Unity Graphics / 2016. 4. 15. 00:10

UnityCG.cginc 폴더에 보면 이런게 있다


// Transforms 2D UV by scale/bias property

#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)


코드에서는 UnityCG.cginc를 include 해서 아래와 같이 사용한다.

o.uv = TRANSFORM_TEX(v.uv, _MainTex); 


그런데 이게 어떤 기능일까?

아래 참고 매뉴얼을 보면 이러한 설명이 되어 있다.


특별한 텍스처 프로퍼티들

shader/material 프로퍼티로 설정한 각 텍스처에서, 유니티는 추가정보를 벡터 프로퍼티로 덧붙인다.


Texture tiling & offset

머티리얼은 종종 기울기와 오프셋 필드를 머티리얼의 텍스처를 위해 가지는데

이 정보는 float4 {텍스처 이름}_ST 항목으로 셰이더로 전달되어 진다.

x는 X 기울기값

y는 Y 기울기값

z는 X 오프셋값

w는 Y 오프셋값

예를 들어, 만약 셰이더가 _MainTex라는 텍스처를 가진다면 기울기 정보는 

_MainTex_ST 벡터가 될 것이다.


o.uv = TRANSFORM_TEX(v.uv, _MainTex);  와 같이 코딩한 채로 위 값을 고쳐보면

텍스처가 이상하게 변하는 것을 볼 수 있다.

Tiling은 몇장의 텍스처를 사용할 것인가를 나타낸다.

예를 들어 x가 1로 되어 있을 경우 아래와 같이 한장의 텍스처를 사용한다.



x가 2로 되어 있을 경우 아래와 같이 같은 범위에서 두장의 텍스처를 사용한다.

즉 1by1 정 사각형에 가로 세로 몇장의 텍스처를 사용할 것인지를 나타낸다.


offset값은 uv 흘리기로 이해하면 쉽다.

uv가 0~1사이 이므로 offset을 1로 하면 0으로 한 것과 같이 다시 처음부터 시작이다.

정수값 ~ 정수값+1 사이의 값이 의미가 있다.


그러므로 만약 위와 같이 Tiling 1,1  그리고 offset 값 0,0 으로 쓴다는게 약속이 된다면

아래의 매크로를 해 줄 필요가 없다.

TRANSFORM_TEX( v.uv, _Tex );



이는 위 값들이 미리 규칙에 맞게 선언 해 둔 float4 _MainTex_ST; 값에 전달되어 지고

TRANSFORM_TEX의 매크로로 변환되어 결과값 계산이 아래와 같이 되기 때문이다.

o.uv = v.uv * _MainTex_ST.xy + _MainTex.zw; 


그리고 추가로 아래와 같이 코드를 고쳐 보자.

o.uv = v.uv;

위 이미지의 값을 아무리 고쳐도 아무런 변화가 없는 것을 볼 수 있다.


또 {텍스처 이름}_TexelSize 는 텍스처의 크기 정보를 가진다.

x : 1.0/width

y : 1.0/height

z : width

w : height

이러한 특별한 텍스처 프로퍼티값들은 필요시 요긴하게 사용할 수 있다.


참고 

- Cg/HLSL 쉐이더 프로퍼티 접근

-

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

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
TRANSFORM_TEX  (0) 2016.04.15
Unity Tags  (0) 2016.04.12
Built in shader variables  (0) 2015.10.30
Posted by 붕대마음

댓글을 달아 주세요

최근에 달린 댓글

최근에 받은 트랙백

글 보관함