Performance Tips when Writing Shaders

Use Common sense ;)

사용자가 필요한 것들만 계산합니다; 실제로 필요하지 않은 것들은 제거될 수 있습니다. 예를 들어 재질당 색상을 지원하는 것은 쉐이더를 좀 더 융통성있게 만들기에 좋으나 만약 사용자가 항상 그 색을 흰 색으로 세팅한다면 그것은 스크린에 그려지는 꼭지점 또는 픽셀을 위해서 수행되어지는 쓸모없는 계산이 됩니다.

또 다른 기억해야하는 한 가지는 계산의 빈도수 입니다. 보통 꼭지점들이 있는 것(정점 쉐이더 실행)보다 더 많은 픽셀이 그려집니다 (그러므로 그들의 픽셀 쉐이더가 실행됩니다); 그리고 그려지는 물체보다 더 많은 꼭지점들이. 그래서 일반적으로 사용자가 할 수 있다면 픽셀 쉐이더를 정점 쉐이더로 계산을 움직이고 또는 쉐이더들로 완전히 움직이고 스크립트로부터 값을 한 번에 세팅합니다.

Less Generic Surface Shaders

Surface Shaders는 조명과 상호작용하는 쉐이더를 쓰기 위해 훌륭합니다. 그러나 그들의 기본적인 옵션은 일반적인 케이스를 위한 것입니다. 많은 경우에 사용자는 쉐이더들을 더욱 빠르게 또는 적어도 더 적게 실행하기 위해서 그들을 조절할 수 있습니다:

  • 뷰 디렉션을 사용하는 쉐이더를 위한 approxview 디렉티브는 뷰 디렉션을 픽셀당 하는 것 대신에 일반화된 정점당 하는 것으로 만들 것입니다. 이것은 대략적이나 자주 충분히 좋습니다.
  • Specular 쉐이더 타입을 위한 halfasview은 훨씬 빠릅니다. Half-vector(조명 방향과 뷰 벡터 사이의 중간)는 정점당 계산될 것이고 일반화될 것입니다. 그리고lighting function은 뷰 벡터 대신에 하나의 파라미터로서half-vector를 이미 받을 것입니다.
  • noforwardadd은 Forward 렌더링에서 하나의 쉐이더를 오직 하나의 방향성 있는 조명만을 지원하도록 만들 것입니다. 나머지 조명은 여전히 정점당 조명 또는 주변의 하모니로서 영향을 가질 수 있습니다. 이것은 심지어 다수의 조명이 있음에도 쉐이더를 더 적게 만들고 그것인 항상 한 번의 패스에서 렌더된다는 것을 확인하기에 좋습니다.
  • noambient은 주변의 조명과 쉐이더 위의 조화를 이루는 조명을 비활성화 할 것입니다. 이것은 약간 더 빠를 수 있습니다.

Precision of computations

Cg/HLSL에서 쉐이더를 쓸 때, 3 가지의 기본 숫자 타입이 있습니다: float, half 그리고 fixed (vector & 그들의 matrix 변형 뿐만 아니라, 예. half3와float4x4):

  • float: 고도로 정밀한 부동 소수점. 일반적으로32 비트, 보통의 프로그래밍 언어에서 float 타입처럼.
  • half: 중간 정도로 정밀한 부동 소수점. 일반적으로16 비트, -60000 과 +60000 사이이고 3.3 정확도의 자릿수.
  • fixed: 낮은 정도로 정밀한 부동 소수점. 일반적으로11 비트, -2.0과 +2.0사이이고1/256정확도.

가능한 가장 낮은 정확도를 사용하세요; 이것은iOS 그리고Android같은 모바일 플랫폼에서는 특별히 중요합니다. 중요한 규칙입니다:

  • 색상과 유닛 벡터를 위해서 fixed를 사용하세요
  • 다른 것들을 위해서 만약 범위와 정확도가 좋다면 half를 사용하시고 그렇지 않다면 float를 사용하세요.

모바일 플랫폼에서 키는 프래그먼트 쉐이더에서 낮은 정확도에서 가능한한 오래 머무르도록 확실시 하는 것입니다. 대부분의GPUs에서 스위즐을 낮은 정확도(fixed/lowp)타입으로 적용하는 것은 비용이 듭니다; fixed/lowp와 더 높은 정확도 타입으로 바꾸는 것도 역시 꽤 비용이 많이 듭니다.

Alpha Testing

고정된 함수 AlphaTest 또는 프로그래밍 할 수 있는 동일한clip()은 다른 플랫폼에서 다른 성능적인 특징들을 가집니다:

  • 일반적으로 그것을 대부분의 플랫폼에서 완전히 투명한 픽셀을 잘라내기 위해서 사용하는 것은 작은 장점입니다.
  • 그러나iOS 에서 발견된 PowerVR GPUs와 다른 Android 디바이스들에서 알파 테스팅은 비용이 많이 듭니다. 그것을 성능 최적화로서 사용하지 마시기 바랍니다. 그것은 더욱 느리게 할 것입니다.

Color Mask

플랫폼들에서 (주로iOS와Android 디바이스에서 발견되는 모바일GPUs) 어떤 채널(e.g. ColorMask RGB)들을 남겨두기 위해 ColorMask를 사용하는 것은 비용이 많이 들 수 있으므로 오직 그것을 정말로 필요할 때만 사용하시기 바랍니다.

'TA > Unity Shader 레퍼런스' 카테고리의 다른 글

Pass Tags  (0) 2012.07.17
Color, Material, Lighting (fixed funtion)  (0) 2012.07.12
Writing vertex and fragment shaders  (0) 2012.07.12
Blending  (0) 2012.06.25
Fallback  (0) 2012.05.21
Posted by 프리랜서 디자이너

Writing vertex and fragment shaders

ShaderLab 쉐이더는 단순히 "hardware shaders"보다 더 많이 둘러싸고 있습니다. 그들은 많은 것들을 합니다. 그들은 제재 인스펙터에서 보여지는 속성들을 설명하고 다른 그래픽 하드웨어를 위해 다수의 쉐이더 구현을 포함합니다. 그리고 정해진 함수 하드웨어 상태를 구성합니다. 꼭지점 그리고 프래그먼트 프로그램처럼 실제 프로그램이 가능한 쉐이더는 전체ShaderLab의 쉐이더 컨셉의 단지 한 부분일 뿐입니다. 기본적인 지시사항을 위해서 shader tutorial를 살펴보시기 바랍니다. 우리는 낮은 레벨의 하드웨어 shader programs를 부를 것입니다.

사용자가 조명과 상호작용하는 쉐이더를 쓰기를 원한다면 Surface Shaders 서류를 살펴보시기 바랍니다. 이 페이지의 나머지 부분은Unity조명과 상호작용하지 않는 쉐이더를 가정할 것입니다 (예. 특수 효과, Image Effects 등.)

쉐이더 프로그램은 쉐이더 텍스트에 미리보기를 끼워넣음으로서 Pass 명령안에 어딘가에 Cg / HLSL 언어로 쓰여집니다. 그들은 이것처럼 보입니다:

  Pass {
      // ... the usual pass state setup ...

      CGPROGRAM
      // compilation directives for this snippet, e.g.:
      #pragma vertex vert
      #pragma fragment frag

      // the Cg code itself

      ENDCG
      // ... the rest of pass setup ...
  }

Cg snippets

Cg 프로그램 미리보기는 CGPROGRAMENDCG사이에서 쓰여집니다.

미리보기의 시작에서 컴파일 지시어는 #pragma 문장으로 주어질 수 있습니다. Unity에 의해 인식되어지는 지시어는 다음과 같습니다:

  • #pragma vertex name - 함수 name가 꼭지점 프로그램임을 나타냅니다.
  • #pragma fragment name - 함수 name가 프래그멘트 프로그램임을 나타냅니다.
  • #pragma fragmentoption option - 컴파일된OpenGL 프래그먼트 프로그램에 option을 추가합니다. 사용가능한 옵션의 리스트를 위해서 ARB fragment program 상세 내용을 살펴보시기 바랍니다. 이 지시어는 꼭지점 프로그램이나OpenGL이 아닌 타겟으로 컴파일된 프로그램에서는 영향을 가지지 않습니다.
  • #pragma target name - 어떤 쉐이더 타겟이 컴파일이 될지를 나타냅니다. 자세한 내용을 위해서 shader targets를 살펴보시기 바랍니다.
  • #pragma only_renderers space separated names - 주어진 렌더러만을 위한 쉐이더를 컴파일합니다. 기본적으로 쉐이더는 모든 렌더러를 위해서 컴파일 됩니다. 자세한 내용을 위해서 renderers를 살펴보시기 바랍니다.
  • #pragma exclude_renderers space separated names - 주어진 렌더러들을 위해서 쉐이더를 컴파일하지 않습니다. 기본적으로 쉐이더는 모든 렌더러를 위해서 컴파일 됩니다. 자세한 내용을 위해서 renderers를 살펴보시기 바랍니다.

각각 미리보기는 꼭지점 프로그램이나 프래그먼트 프로그램 또는 둘 다를 포함해야만 합니다. 그러므로 #pragma vertex 또는 #pragma fragment 지시어가 또는 둘 다 필요되어 집니다.

Shader targets

기본적으로 Unity는 동등한 쉐이더 모델 1.1로 꼭지점 프로그램을 컴파일하고 쉐이더 모델 2.0으로 프래그먼트 프로그램을 컴파일 합니다. #pragma target을 사용하는 것은 쉐이더가 다른 레벨로 컴파일 되는 것을 허락합니다. 현재 이러한 타겟들이 지원됩니다:

  • #pragma target default - 기본 타겟으로 컴파일:
    • Direct3D 9에서 Vertex shader 1.1 그리고 pixel shader 2.0.
    • 128 인스트럭션 제한을 가진 ARB_vertex_program 그리고 96 인스트럭션 제한을 가진 ARB_fragment_program (32 텍스쳐+ 64 산수), 16 일시적인 레지스터와 4 텍스쳐 우회.
  • #pragma target 3.0 - 쉐이더 모델 3.0으로 컴파일:
    • Direct3D 9에서 Vertex shader 3.0 그리고 pixel shader 3.0.
    • 인스트럭션 제한을 가지지 않는 ARB_vertex_program 그리고1024인스트럭션 제한을 가진ARB_fragment_program (512텍스쳐+ 512산수), 32일시적인 레지스터와4 텍스쳐 우회. #pragma profileoption directive 지시어를 사용해서 이러한 제한을 덮어쓰는 것은 가능합니다. 예 #pragma profileoption MaxTexIndirections=256은 텍스쳐 우회 제한을 256으로 올립니다. derivative 지시사항과 같은 몇몇 셰이더 모델 3.0 기능들은 ARB_vertex_program/ARB_fragment_program 에 의해 지원되지 않습니다. 당신은 제한이 덜한 GLSL로 대신 번역하기 위해 #pragma glsl를 사용할 수 있습니다. 3.0 타겟으로 컴파일 할 때 꼭지점과 프래그먼트 프로그램 둘 모두 존재해야 합니다.

Renderers

Unity는 몇몇의 렌더링APIs를 지원하고 (예. Direct3D 9 와 OpenGL), 기본적으로 모든 쉐이더 프로그램은 지원되는 렌더러를 위해서 컴파일 되어 집니다. 사용자는 어떤 렌더러가 컴파일되는지 #pragma only_renderers 또는 #pragma exclude_renderers 을 사용해서 지정할 수 있습니다. 이것은 사용자가 오직Mac OS X(Direct3D은 없습니다)만을 타겟으로 할 것이라는 것을 안다면 또는 오직Windows만을(Unity가 D3D을 기본적으로 정하는 곳에서) 또는 어떤 특별한 쉐이더만이 오직 가능하다면 매우 유용합니다. 현재 지원되는 렌더러의 이름은 다음과 같습니다:

  • d3d9 - Direct3D 9.
  • opengl - OpenGL.
  • gles - OpenGL ES 2.0.
  • xbox360 - Xbox 360.
  • ps3 - PlayStation 3.
  • flash - Flash.

예를 들어 이 라인은 쉐이더를D3D9 모드로 오직 컴파일 할 것입니다:

  #pragma only_renderers d3d9

'TA > Unity Shader 레퍼런스' 카테고리의 다른 글

Color, Material, Lighting (fixed funtion)  (0) 2012.07.12
Performance Tips when Writing Shaders  (0) 2012.07.12
Blending  (0) 2012.06.25
Fallback  (0) 2012.05.21
RenderTech-VertexLit  (0) 2012.05.17
Posted by 프리랜서 디자이너
TA/Unity2012. 7. 10. 09:29

http://chulin28ho.egloos.com/5106486

 

[펌]HLSL 함수 by 김윤정

명령어 모음

원문 : http://blog.stnzone.com/darkman/entry/HLSL-함수

abs value abs(value a) 절대치 (성분마다).
acos acos(x) x 의 각 성분의 역코사인을 돌려준다. 각 성분은,[-1, 1] 의 범위로 한다.
all all(x) x 의 모든 성분이 0 이외의 값인지 아닌지를 테스트한다.
any any(x) x 의 몇개의 성분이 0 이외의 값인지 아닌지를 테스트한다.
asin asin(x) x 의 각 성분의 역정현을 돌려준다. 각 성분은,[-pi/2, pi/2] 의 범위로 한다.
atan atan(x) x 의 각 성분의 역탄젠트를 돌려준다. 반환값은,[-pi/2, pi/2] 의 범위이다.
atan2 atan2(y, x) y/x 의 역탄젠트를 돌려준다. y 와 x 의 부호를 사용해 [-pi, pi] 의 범위에 있는 반환값의 상한을 판단한다. atan2 는, x 가 0 으로 동일하고, y 가 0 으로 동일하지 않은 경우에서도, 원점 이외의 각 점에 대해서 충분히 정의되고 있다.
ceil ceil(x) x 이상의 최소의 정수를 돌려준다.
clamp clamp(x, min, max) x 를 [min, max] 의 범위에 제한한다.
clip clip(x) x 의 몇개의 성분이 0 보다 작은 경우, 현재의 픽셀을 파기한다. x 의 각 성분이 면으로부터의 거리를 나타내는 경우, 이 함수를 사용해, 클립면을 시뮬레이션 한다.
cos cos(x) x 의 코사인을 돌려준다.
cosh cosh(x) x 의 쌍곡코사인을 돌려준다.
cross cross(a, b) 2 개의 3D 벡터 a 와 b 의 외적을 돌려준다.
D3DCOLORtoUBYTE4 D3DCOLORtoUBYTE4(x) 4D 벡터 x 의 성분을 교체 및 스케일링 해, 일부 하드웨어에 있는 UBYTE4 지원의 부족을 보정한다.
ddx ddx(x) 스크린 공간의 x 좌표에 대해, x 의 편미분을 돌려준다.
ddy ddy(x) 스크린 공간의 y 좌표에 대해, x 의 편미분을 돌려준다.
degrees degrees(x) x 를 라디안 단위로부터 도수로 변환한다.
determinant determinant(m) 서방 행렬 m 의 행렬식을 돌려준다.
distance distance(a, b) 2 개의 점 a 와 b 간의 거리를 돌려준다.
dot dot(a, b) 2 개의 벡터 a 와 b 의 내적을 돌려준다.
exp exp(x) e 를 바닥으로 하는 지수 ex 를 돌려준다.
exp2 value exp2(value a) 2 를 바닥으로 하는 지수 (성분마다).
faceforward faceforward(n, i, ng) -n * sign(dot(i, ng))를 돌려준다.
floor floor(x) x 이하의 최대의 정수를 돌려준다.
fmod fmod(a, b) a = i * b + f 가 되는 것 같은, a / b 의 부동 소수점수(실수)의 잉여 f 를 돌려준다. 여기서, i 는 정수, f 는 x 와 부호가 같아, 그 절대치는 b 의 절대치보다 작다.
frac frac(x) f 가 0 보다 크고, 1 보다 작은 값이 되는 것 같은, x 의 소수부 f 를 돌려준다.
frc value frc(value a) 소수부 (성분마다).
frexp frexp(x, out exp) x 의 가수와 지수를 돌려준다. frexp 는 가수를 돌려주어, 지수는 출력 인수 exp 에 저장 된다. x 가 0 의 경우, 함수는 가수와 지수의 양쪽 모두에 0 을 돌려준다.
fwidth fwidth(x) abs(ddx(x)) +abs(ddy(x))를 돌려준다.
isfinite isfinite(x) x 가 유한의 경우는 TRUE 를 돌려준다. 그 이외의 경우는 FALSE 를 돌려준다.
isinf isinf(x) x 가 +INF 나 -INF 의 경우는 TRUE 를 돌려준다. 그 이외의 경우는 FALSE 를 돌려준다.
isnan isnan(x) x 가 NAN 나 QNAN 의 경우는 TRUE 를 돌려준다. 그 이외의 경우는 FALSE 를 돌려준다.
ldexp ldexp(x, exp) x * 2exp 를 돌려준다.
len float len(value a) 벡터의 길이.
length length(v) 벡터 v 의 길이를 돌려준다.
lerp lerp(a, b, s) a + s(b - a)를 돌려준다. 이 함수는, s 가 0 의 경우는 a 를 돌려주어, 1 의 경우는 b 를 돌려주도록, a 와 b 의 사이를 선형 보간 한다.
lit lit(ndotl, ndoth, m) 조명의 벡터 (앰비언트, 디퓨즈, 스펙큐러, 1)를 돌려준다. 앰비언트 = 1; 디퓨즈 = (ndotl < 0) ? 0 : ndotl; 스펙큐러 = (ndotl < 0) || (ndoth < 0) ? 0 : (ndoth * m);
log log(x) x 의, 바닥이 e 의 자연대수를 돌려준다. x 가 부의 경우, 이 함수는 무한을 돌려준다. x 가 0 의 경우, +INF 를 돌려준다.
log10 log10(x) x 의, 바닥이 10 의 자연대수를 돌려준다. x 가 부의 경우, 이 함수는 무한을 돌려준다. x 가 0 의 경우, +INF 를 돌려준다.
log2 log2(x) x 의, 바닥이 2 의 자연대수를 돌려준다. x 가 부의 경우, 이 함수는 무한을 돌려준다. x 가 0 의 경우, +INF 를 돌려준다.
max max(a, b) a 와 b 의 큰 (분)편을 선택한다.
min min(a, b) a 와 b 가 작은 (분)편을 선택한다.
modf modf(x, out ip) 값 x 를, 각각이 x 와 같은 부호를 가진 소수부와 정수부로 나눈다. x 의 부호 첨부 소수부가 반환된다. 정수부는 출력 인수 ip 에 저장 된다.
mul mul(a, b) a 와 b 의 사이의 행렬 곱셈을 실행한다. a 가 벡터의 경우, 행 벡터로서 처리한다. b 가 벡터의 경우, 열로서 처리한다. 내부 넓이의 a 열과 b 행은 동일해야 한다. a 행 x b 열의 넓이를 얻을 수 있다.
noise noise(x) 처리되지 않다.
normalize normalize(v) 정규화된 벡터 v / length(v)를 돌려준다. v 의 길이가 0 의 경우, 결과는 무한이 된다.
pow pow(x, y) xy 를 돌려준다.
radians radians(x) x 를 도수로부터 라디안 단위로 변환한다.
reflect reflect(i, n) 입사 방향 i, 표면 법선 n 로 했을 경우의, v = i - 2 * dot(i, n) * n 에 의해 구할 수 있는, 반사 벡터 v 를 돌려준다.
refract refract(i, n, eta) 입사 방향 i, 표면 법선 n, 굴절 eta 의 상대 인덱스가 주어졌을 경우의, 굴절 벡터 v 를 돌려준다. i 와 n 의 사이의 입사각이 지정된 eta 보다 너무 크면 (0,0,0)를 돌려준다.
round round(x) x 를 가장 가까운 정수에 말다.
rsqrt rsqrt(x) 1 / sqrt(x)를 돌려준다.
saturate saturate(x) x 를 [0, 1] 의 범위에 제한한다.
sign sign(x) x 의 부호를 요구한다. x 가 0 보다 작은 경우는 -1, 0 으로 동일한 경우는 0, 0 보다 큰 경우는 1 을 돌려준다.
sin sin(x) x 의 정현을 돌려준다.
sincos sincos(x, out s, out c) x 의 정현과 코사인을 돌려준다. sin(x)는 출력 인수 s 에 저장 되어 cos(x)는 출력 인수 c 에 저장 된다.
sinh sinh(x) x 의 쌍곡정현을 돌려준다.
smoothstep smoothstep(min, max, x) x < min 의 경우는 0 을 돌려준다. x > max 의 경우는 1 을 돌려준다. x 가 [min, max] 의 범위내이면, 0 으로 1 의 사이의 매끄러운 에르미트 보간을 돌려준다.
sqrt value sqrt(value a) 제곱근 (성분마다).
step step(a, x) (x >= a) ? 1 : 0 을 돌려준다.
tan tan(x) x 의 탄젠트를 돌려준다.
tanh tanh(x) x 의 쌍곡탄젠트를 돌려준다.
tex1D tex1D(s, t) 1D 의 텍스처 참조. s 는 샘플러 또는 sampler1D 개체. t 는 스칼라-.
tex1D tex1D(s, t, ddx, ddy) 미분을 지정한, 1D 의 텍스처 참조. s 는 샘플러 또는 sampler1D 개체. t, ddx, ddy 는 스칼라-.

tex1Dproj tex1Dproj(s, t) 1D 의 투영 텍스처 참조. s 는 샘플러 또는 sampler1D 개체. t 는 4D 벡터. t 는, 참조가 실행되기 직전의 성분으로 나눗셈 된다.
tex1Dbias tex1Dbias(s, t) 1D 의 바이어스 텍스처 참조. s 는 샘플러 또는 sampler1D 개체. t 는 4D 벡터. 참조를 실행하기 전에, 밉레벨에 t.w 의 바이어스를 걸칠 수 있다.
tex2D tex2D(s, t) 2D 의 텍스처 참조. s 는 샘플러 또는 sampler2D 개체. t 는 2D 텍스처 좌표.
tex2D tex2D(s, t, ddx, ddy) 미분을 지정한, 2D 의 텍스처 참조. s 는 샘플러 또는 sampler2D 개체. t, ddx, ddy 는 2D 벡터.
tex2Dproj tex2Dproj(s, t) 2D 의 투영 텍스처 참조. s 는 샘플러 또는 sampler2D 개체. t 는 4D 벡터. t 는, 참조가 실행되기 직전의 성분으로 나눗셈 된다.
tex2Dbias tex2Dbias(s, t) 2D 의 바이어스 텍스처 참조. s 는 샘플러 또는 sampler2D 개체. t 는 4D 벡터. 참조를 실행하기 전에, 밉레벨에 t.w 의 바이어스를 걸칠 수 있다.
tex3D tex3D(s, t) 3D 의 볼륨 텍스처 참조. s 는 샘플러 또는 sampler3D 개체. t 는 3D 텍스처 좌표.
tex3D tex3D(s, t, ddx, ddy) 미분을 지정한, 3D 의 볼륨 텍스처 참조. s 는 샘플러 또는 sampler3D 개체. t, ddx, ddy 는 3D 벡터.
tex3Dproj tex3Dproj(s, t) 3D 의 투영 볼륨 텍스처 참조. s 는 샘플러 또는 sampler3D 개체. t 는 4D 벡터. t 는, 참조가 실행되기 직전의 성분으로 나눗셈 된다.
tex3Dbias tex3Dbias(s, t) 3D 의 바이어스 텍스처 참조. s 는 샘플러 또는 sampler3D 개체. t 는 4D 벡터. 참조를 실행하기 전에, 밉레벨에 t.w 의 바이어스를 걸칠 수 있다.
texCUBE texCUBE(s, t) 3D 의 큐브 텍스처 참조. s 는 샘플러 또는 samplerCUBE 개체. t 는 3D 텍스처 좌표.
texCUBE texCUBE(s, t, ddx, ddy) 미분을 지정한, 3D 의 큐브 텍스처 참조. s 는 샘플러 또는 samplerCUBE 개체. t, ddx, ddy 는 3D 벡터.
texCUBEproj texCUBEproj(s, t) 3D 투영의 큐브 텍스처 참조. s 는 샘플러 또는 samplerCUBE 개체. t 는 4D 벡터. t 는, 참조가 실행되기 직전의 성분으로 나눗셈 된다.
texCUBEbias texCUBEbias(s, t) 3D 의 바이어스 큐브 텍스처 참조. s 는 샘플러 또는 samplerCUBE 개체. t 는 4D 벡터. 참조를 실행하기 전에, 밉레벨에 t.w 의 바이어스를 걸칠 수 있다.
transpose transpose(m) 행렬 m 의 전치행렬을 돌려준다. 입력의 넓이가 m 행 x m 열의 경우, 결과는 넓이 m 열 x m 행이 된다.

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

유니티 C# 공부 모음  (0) 2013.05.07
Texture Matrix in Fixed Function  (0) 2012.07.18
Blending modes..참고 자료  (0) 2012.06.08
기초적인 조명쉐이더  (0) 2012.06.08
정 반사광  (0) 2012.06.08
Posted by 프리랜서 디자이너

ShaderLab syntax: Blending

Blending은 투명한 물체를 만들기 위해 사용됩니다.

그래픽들이 표현될 때 모든 쉐이더가 실행되고 모든 텍스쳐가 적용되어진 후에 픽셀은 스크린에 쓰여집니다. 그들이 이미 있는 것과 어떻게 결합되는지는Blend 명령에 의해 조절됩니다.

Syntax

Blend Off
블렌딩 기능 취소
Blend SrcFactor DstFactor
블렌딩을 구성 & 활성화. 생성된 색상은 SrcFactor를 곱합니다. 스크린에 이미 있던 색상은 DstFactor 에 의해 곱해지고 두 개는 함께 더해집니다.

Properties

모든 다음의 속성들은SrcFactor와DstFactor 둘 다를 위해서 유효합니다. Source 는 계산되어지는 색상을 의미하고 Destination 는 이미 스크린에 있는 색상을 의미합니다.

One

1의 값 – 소스 또는 데스티네이션 색상이 온전히 오게 합니다.

 

Zero

0의 값 – 소스 또는데스티네이션 값을 삭제하게 합니다.

 

SrcColor

이 스테이지의 값은 소스 색상 값에 의해 곱해집니다.

 

SrcAlpha

이 스테이지의 값은 소스 알파 값에 의해 곱해집니다.

 

DstColor

이 스테이지의 값은 프레임 버퍼 소스 색상 값에 의해 곱해집니다.

 

DstAlpha

이 스테이지의 값은 프레임 버퍼 소스 알파 값에 의해 곱해집니다.

 

OneMinusSrcColor

이 스테이지의 값은 (1 – 소스 색상)에 의해 곱해집니다.

 

OneMinusSrcAlpha

이 스테이지의 값은 (1 – 소스 알파)에 의해 곱해집니다.

 

OneMinusDstColor

이 스테이지의 값은 (1 – 데스티네이션 색상)에 의해 곱해집니다.

 

OneMinusDstAlpha

이 스테이지의 값은 (1 – 데스티네이션 알파)에 의해 곱해집니다.

 

Details

아래는 가장 흔한 블렌드 타입입니다:

Blend SrcAlpha OneMinusSrcAlpha     // Alpha blending
Blend One One                       // Additive
Blend One OneMinusDstColor          // Soft Additive
Blend DstColor Zero                 // Multiplicative
Blend DstColor SrcColor             // 2x Multiplicative

Example

이것은 스크린에 이미 무엇이 있던 하나의 텍스쳐를 추가하는 쉐이더의 작은 예입니다:

Shader "Simple Additive" {
    Properties {
        _MainTex ("Texture to blend", 2D) = "black" {}
    }
    SubShader {
        Tags { "Queue" = "Transparent" }
        Pass {
            Blend One One
            SetTexture [_MainTex] { combine texture }
        }
    }
}

'TA > Unity Shader 레퍼런스' 카테고리의 다른 글

Performance Tips when Writing Shaders  (0) 2012.07.12
Writing vertex and fragment shaders  (0) 2012.07.12
Fallback  (0) 2012.05.21
RenderTech-VertexLit  (0) 2012.05.17
RenderTech-ForwardRendering  (0) 2012.05.17
Posted by 프리랜서 디자이너
TA/Unity2012. 6. 8. 14:12

http://mouaif.wordpress.com/2009/01/05/photoshop-math-with-glsl-shaders/

 

 

PhotoshopMathFP.glsl

Photoshop math with GLSL shaders

January 5, 2009

I usualy play with Photoshop to try post-processing effects on photos or game screenshots, it’s a lot faster than coding directly anything in shaders, but at the end I wanted to see my effects running in real-time. So I adapted a big part of the C-like code from this famous Photoshop blending mode math page + missing blending modes to GLSL (and now HLSL!) code and I added a few other useful things from Photoshop, such as Hue/Saturation/Luminance conversion, desaturation, contrast.

For example, I tried combining a few things in my Editor:

photoshopmath_tn

photoshopmath_editor_tn

Translating Photoshop operations on layers gives this kind of code:

uniform sampler2D Tex;
uniform sampler1D GradientMap;
uniform sampler1D GradientGround;

varying vec2 uv;

void main()
{

vec3 color = texture2D(Tex, uv).xyz;

// Split-tone
vec4 colorDesat = Desaturate(color, 1.0);
vec3 splitColor = texture1D(GradientMap, colorDesat.r).rgb;
vec3 pass1 = BlendColor(color, splitColor);

// Vertical gradient
vec4 verticalGradientColor = texture1D(GradientGround, uv.y);
vec3 pass2 = mix(pass1, BlendColor(pass1, verticalGradientColor.rgb), verticalGradientColor.a);

// Luminosity
vec3 pass3 = mix(pass2, BlendLuminosity(pass2, color + vec3(0.08)), 0.5);

// Linear light at 40%
vec3 pass4 = mix(pass3, BlendLinearLight(pass3, color), 0.4);

// Final
gl_FragColor = vec4(pass4, 1.0);

}

Here is the list of blending modes and functions I got:

Blending modes:

  • Normal
  • Lighten
  • Darken
  • Multiply
  • Average
  • Add
  • Substract
  • Difference
  • Negation
  • Exclusion
  • Screen
  • Overlay
  • SoftLight
  • HardLight
  • ColorDodge
  • ColorBurn
  • LinearDodge
  • LinearBurn
  • LinearLight
  • VividLight
  • PinLight
  • HardMix
  • Reflect
  • Glow
  • Phoenix
  • Hue
  • Saturation
  • Color
  • Luminosity

Functions:

  • Desaturation
  • RGBToHSL (RGB to Hue/Saturation/Luminance)
  • HSLToRGB (Hue/Saturation/Luminance to RGB)
  • Contrast

 

 

 

 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

/*
** Photoshop & misc math
** Blending modes, RGB/HSL/Contrast/Desaturate, levels control
**
** Romain Dura | Romz
** Blog: http://blog.mouaif.org
** Post: http://blog.mouaif.org/?p=94
*/


/*
** Desaturation
*/

vec4 Desaturate(vec3 color, float Desaturation)
{
 vec3 grayXfer = vec3(0.3, 0.59, 0.11);
 vec3 gray = vec3(dot(grayXfer, color));
 return vec4(mix(color, gray, Desaturation), 1.0);
}


/*
** Hue, saturation, luminance
*/

vec3 RGBToHSL(vec3 color)
{
 vec3 hsl; // init to 0 to avoid warnings ? (and reverse if + remove first part)
 
 float fmin = min(min(color.r, color.g), color.b);    //Min. value of RGB
 float fmax = max(max(color.r, color.g), color.b);    //Max. value of RGB
 float delta = fmax - fmin;             //Delta RGB value

 hsl.z = (fmax + fmin) / 2.0; // Luminance

 if (delta == 0.0)  //This is a gray, no chroma...
 {
  hsl.x = 0.0; // Hue
  hsl.y = 0.0; // Saturation
 }
 else                                    //Chromatic data...
 {
  if (hsl.z < 0.5)
   hsl.y = delta / (fmax + fmin); // Saturation
  else
   hsl.y = delta / (2.0 - fmax - fmin); // Saturation
  
  float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta;
  float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta;
  float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta;

  if (color.r == fmax )
   hsl.x = deltaB - deltaG; // Hue
  else if (color.g == fmax)
   hsl.x = (1.0 / 3.0) + deltaR - deltaB; // Hue
  else if (color.b == fmax)
   hsl.x = (2.0 / 3.0) + deltaG - deltaR; // Hue

  if (hsl.x < 0.0)
   hsl.x += 1.0; // Hue
  else if (hsl.x > 1.0)
   hsl.x -= 1.0; // Hue
 }

 return hsl;
}

float HueToRGB(float f1, float f2, float hue)
{
 if (hue < 0.0)
  hue += 1.0;
 else if (hue > 1.0)
  hue -= 1.0;
 float res;
 if ((6.0 * hue) < 1.0)
  res = f1 + (f2 - f1) * 6.0 * hue;
 else if ((2.0 * hue) < 1.0)
  res = f2;
 else if ((3.0 * hue) < 2.0)
  res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0;
 else
  res = f1;
 return res;
}

vec3 HSLToRGB(vec3 hsl)
{
 vec3 rgb;
 
 if (hsl.y == 0.0)
  rgb = vec3(hsl.z); // Luminance
 else
 {
  float f2;
  
  if (hsl.z < 0.5)
   f2 = hsl.z * (1.0 + hsl.y);
  else
   f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z);
   
  float f1 = 2.0 * hsl.z - f2;
  
  rgb.r = HueToRGB(f1, f2, hsl.x + (1.0/3.0));
  rgb.g = HueToRGB(f1, f2, hsl.x);
  rgb.b= HueToRGB(f1, f2, hsl.x - (1.0/3.0));
 }
 
 return rgb;
}


/*
** Contrast, saturation, brightness
** Code of this function is from TGM's shader pack
** http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=21057
*/

// For all settings: 1.0 = 100% 0.5=50% 1.5 = 150%
vec3 ContrastSaturationBrightness(vec3 color, float brt, float sat, float con)
{
 // Increase or decrease theese values to adjust r, g and b color channels seperately
 const float AvgLumR = 0.5;
 const float AvgLumG = 0.5;
 const float AvgLumB = 0.5;
 
 const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
 
 vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
 vec3 brtColor = color * brt;
 vec3 intensity = vec3(dot(brtColor, LumCoeff));
 vec3 satColor = mix(intensity, brtColor, sat);
 vec3 conColor = mix(AvgLumin, satColor, con);
 return conColor;
}


/*
** Float blending modes
** Adapted from here: http://www.nathanm.com/photoshop-blending-math/
** But I modified the HardMix (wrong condition), Overlay, SoftLight, ColorDodge, ColorBurn, VividLight, PinLight (inverted layers) ones to have correct results
*/

#define BlendLinearDodgef    BlendAddf
#define BlendLinearBurnf    BlendSubstractf
#define BlendAddf(base, blend)   min(base + blend, 1.0)
#define BlendSubstractf(base, blend)  max(base + blend - 1.0, 0.0)
#define BlendLightenf(base, blend)   max(blend, base)
#define BlendDarkenf(base, blend)   min(blend, base)
#define BlendLinearLightf(base, blend)  (blend < 0.5 ? BlendLinearBurnf(base, (2.0 * blend)) : BlendLinearDodgef(base, (2.0 * (blend - 0.5))))
#define BlendScreenf(base, blend)   (1.0 - ((1.0 - base) * (1.0 - blend)))
#define BlendOverlayf(base, blend)  (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend)))
#define BlendSoftLightf(base, blend)  ((blend < 0.5) ? (2.0 * base * blend + base * base * (1.0 - 2.0 * blend)) : (sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend)))
#define BlendColorDodgef(base, blend)  ((blend == 1.0) ? blend : min(base / (1.0 - blend), 1.0))
#define BlendColorBurnf(base, blend)  ((blend == 0.0) ? blend : max((1.0 - ((1.0 - base) / blend)), 0.0))
#define BlendVividLightf(base, blend)  ((blend < 0.5) ? BlendColorBurnf(base, (2.0 * blend)) : BlendColorDodgef(base, (2.0 * (blend - 0.5))))
#define BlendPinLightf(base, blend)  ((blend < 0.5) ? BlendDarkenf(base, (2.0 * blend)) : BlendLightenf(base, (2.0 *(blend - 0.5))))
#define BlendHardMixf(base, blend)  ((BlendVividLightf(base, blend) < 0.5) ? 0.0 : 1.0)
#define BlendReflectf(base, blend)   ((blend == 1.0) ? blend : min(base * base / (1.0 - blend), 1.0))


/*
** Vector3 blending modes
*/

// Component wise blending
#define Blend(base, blend, funcf)   vec3(funcf(base.r, blend.r), funcf(base.g, blend.g), funcf(base.b, blend.b))

#define BlendNormal(base, blend)   (blend)
#define BlendLighten    BlendLightenf
#define BlendDarken    BlendDarkenf
#define BlendMultiply(base, blend)   (base * blend)
#define BlendAverage(base, blend)   ((base + blend) / 2.0)
#define BlendAdd(base, blend)   min(base + blend, vec3(1.0))
#define BlendSubstract(base, blend)  max(base + blend - vec3(1.0), vec3(0.0))
#define BlendDifference(base, blend)  abs(base - blend)
#define BlendNegation(base, blend)  (vec3(1.0) - abs(vec3(1.0) - base - blend))
#define BlendExclusion(base, blend)  (base + blend - 2.0 * base * blend)
#define BlendScreen(base, blend)   Blend(base, blend, BlendScreenf)
#define BlendOverlay(base, blend)   Blend(base, blend, BlendOverlayf)
#define BlendSoftLight(base, blend)  Blend(base, blend, BlendSoftLightf)
#define BlendHardLight(base, blend)  BlendOverlay(blend, base)
#define BlendColorDodge(base, blend)  Blend(base, blend, BlendColorDodgef)
#define BlendColorBurn(base, blend)  Blend(base, blend, BlendColorBurnf)
#define BlendLinearDodge   BlendAdd
#define BlendLinearBurn   BlendSubstract
// Linear Light is another contrast-increasing mode
// If the blend color is darker than midgray, Linear Light darkens the image by decreasing the brightness. If the blend color is lighter than midgray, the result is a brighter image due to increased brightness.
#define BlendLinearLight(base, blend)  Blend(base, blend, BlendLinearLightf)
#define BlendVividLight(base, blend)  Blend(base, blend, BlendVividLightf)
#define BlendPinLight(base, blend)   Blend(base, blend, BlendPinLightf)
#define BlendHardMix(base, blend)   Blend(base, blend, BlendHardMixf)
#define BlendReflect(base, blend)   Blend(base, blend, BlendReflectf)
#define BlendGlow(base, blend)   BlendReflect(blend, base)
#define BlendPhoenix(base, blend)   (min(base, blend) - max(base, blend) + vec3(1.0))
#define BlendOpacity(base, blend, F, O)  (F(base, blend) * O + blend * (1.0 - O))


// Hue Blend mode creates the result color by combining the luminance and saturation of the base color with the hue of the blend color.
vec3 BlendHue(vec3 base, vec3 blend)
{
 vec3 baseHSL = RGBToHSL(base);
 return HSLToRGB(vec3(RGBToHSL(blend).r, baseHSL.g, baseHSL.b));
}

// Saturation Blend mode creates the result color by combining the luminance and hue of the base color with the saturation of the blend color.
vec3 BlendSaturation(vec3 base, vec3 blend)
{
 vec3 baseHSL = RGBToHSL(base);
 return HSLToRGB(vec3(baseHSL.r, RGBToHSL(blend).g, baseHSL.b));
}

// Color Mode keeps the brightness of the base color and applies both the hue and saturation of the blend color.
vec3 BlendColor(vec3 base, vec3 blend)
{
 vec3 blendHSL = RGBToHSL(blend);
 return HSLToRGB(vec3(blendHSL.r, blendHSL.g, RGBToHSL(base).b));
}

// Luminosity Blend mode creates the result color by combining the hue and saturation of the base color with the luminance of the blend color.
vec3 BlendLuminosity(vec3 base, vec3 blend)
{
 vec3 baseHSL = RGBToHSL(base);
 return HSLToRGB(vec3(baseHSL.r, baseHSL.g, RGBToHSL(blend).b));
}


/*
** Gamma correction
** Details: http://blog.mouaif.org/2009/01/22/photoshop-gamma-correction-shader/
*/

#define GammaCorrection(color, gamma)        pow(color, 1.0 / gamma)

/*
** Levels control (input (+gamma), output)
** Details: http://blog.mouaif.org/2009/01/28/levels-control-shader/
*/

#define LevelsControlInputRange(color, minInput, maxInput)    min(max(color - vec3(minInput), vec3(0.0)) / (vec3(maxInput) - vec3(minInput)), vec3(1.0))
#define LevelsControlInput(color, minInput, gamma, maxInput)    GammaCorrection(LevelsControlInputRange(color, minInput, maxInput), gamma)
#define LevelsControlOutputRange(color, minOutput, maxOutput)    mix(vec3(minOutput), vec3(maxOutput), color)
#define LevelsControl(color, minInput, gamma, maxInput, minOutput, maxOutput)  LevelsControlOutputRange(LevelsControlInput(color, minInput, gamma, maxInput), minOutput, maxOutput)

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

Texture Matrix in Fixed Function  (0) 2012.07.18
HLSL 함수  (0) 2012.07.10
기초적인 조명쉐이더  (0) 2012.06.08
정 반사광  (0) 2012.06.08
unity shader 3가지 방식 정리  (0) 2012.06.01
Posted by 프리랜서 디자이너
TA/Unity2012. 6. 8. 13:46

 

출처 http://kblog.popekim.com/2011/12/04-part-1.html

[포프의 쉐이더 입문강좌] 04. 기초적인 조명쉐이더 Part 1

이전편 보기



샘플파일받기

제4장 기초적인 조명쉐이더

이 장에서 새로 배우는 HLSL

  • NORMAL: 정점의 법선정보를 불러올 때 사용하는 시맨틱
  • normalize(): 벡터 정규화 함수
  • dot(): 내적 함수
  • saturate(): 0 ~ 1을 넘어서는 값의 범위를 짤라 냄.
  • reflect(): 벡터반사 함수
  • pow(): 거듭제곱 함수


이 장에서 새로 사용하는 수학
  • 내적: 코사인 함수를 재빨리 계산할 때 사용할 수 있음
  • 정규화: 벡터를 단위벡터(길이가 1인 벡터)로 만듬.


빛이 존재하지 않는다면 물체를 볼 수 없습니다. 매우 당연한 이치인데도 이걸 까먹고 지내는 분들이 많은 것 같습니다. (저도 종종 까먹습니다.) 예를 들어, 창문이 하나도 없는 방에 들어가서 문을 닫아버리면 아무것도 볼 수가 없지요? 어디서 새어 들어오는 빛이 있지 않는 한 아무리 어둠 속에서 오래 있어도 아무것도 보이지 않습니다. 이 당연한 사실을 자꾸 까먹는 이유는 실생활에서 완전히 칠흑 같은 어둠을 찾기가 쉽지 않기 때문입니다. 왜일까요? 바로 끝없이 반사하는 빛의 성질 때문입니다. 딱히 눈에 뜨이는 광원이 없더라도 대기중의 미세입자에 반사되어 들어오는 빛까지 있으니까요. 이렇게 다른 물체에 반사돼서 들어오는 빛을 간접광이라 합니다. 반대로 직접광은 광원으로부터 직접 받는 빛입니다. 그림 4.1에 예를 들어보겠습니다.

그림 4.1 직접광과 간접광의 예

직접광과 간접광 중에서 어떤 것을 더 쉽게 계산할 수 있을까요? 위 그림만 봐도 딱 답이 나오죠? 직접광입니다. 간접광은 수없이 반사의 반사를 거치므로 당연히 직접광보다 계산하기 어렵습니다. 간접광을 계산하는 방법 중 하나로 광선추적(ray-tracing)이라는 기법이 있습니다. 아마 3D 그래픽에 관심이 많으신 분들이라면 최근 들어 광선추적에 대해 논하는 많은 자료를 보셨을 겁니다. 하지만, 아직도 실시간 광선추적기법이 게임에서 널리 사용되지 않는 이유는 하드웨어 사양이 따라주지 않기 때문이라죠. (특히 콘솔 하드웨어의 하드웨어 사양이 더 큰 문제입니다.) 그렇기 때문에 아직도 컴퓨터 게임 등을 비롯한 실시간 3D 프로그램에서는 주로 직접광만을 제대로 계산하고 간접광은 흉내내기 정도로 그치는 게 보통입니다. 따라서 이 장에서도 직접광만을 다루도록 하겠습니다. (간접광까지도 다루는 조명모델을 전역조명모델(global illumination model)이라고 합니다. 반대로 직접광만을 다루는 조명모델을 지역조명모델(local illumination model)이라 합니다.) 참고로 이 장에서 배우는 조명 쉐이더는 아직까지도 대부분의 게임에서 표준으로 사용하는 기법이므로 잘 숙지해 두세요.

빛을 구성하는 요소는 크게 난 반사광(diffuse light)과 정 반사광(specular light)이 있습니다. 이 둘을 따로 살펴보도록 하겠습니다.

난 반사광
배경
대부분의 물체는 스스로 빛을 발하지 않습니다. 그럼에도 저희가 이 물체들을 지각할 수 있는 이유는 다른 물체(예, 태양)가 발산하는 빛이 이 물체의 표면에서 반사되기 때문입니다. 이 때, 여러 방향으로 고르게 반사되는 빛이 있는데 이것을 난 반사광(diffuse light)이라고 합니다. (diffuse 광은 아직도 용어정립이 안되고 있습니다. 따라서 용어를 사용할 때마다 종종 영문 표기를 같이 하도록 하겠습니다. 다른 용어로는 산란광, 확산광 등이 있는데 반사광이 가장 적합한 같습니다.) 어느 방향에서 바라봐도 물체의 명암이나 색조가 크게 변하지 않는 이유를 아시나요? 여러 방향으로 고르게 퍼지는 난 반사광 덕분입니다. 만약 빛이 한 방향으로만 반사된다면(이것이 뒤에서 살펴볼 정 반사광입니다.) 그 방향에서만 물체를 지각할 수 있겠지요.

참고로 물체의 표면이 거칠수록 난반사가 심해지는 것이 보통입니다. (표면이 완전히 매끈하더라도 난반사가 완전히 사라지는 경우는 극히 드뭅니다. 표면을 뚫고 들어간 뒤, 물체 내부에서 반사되는 빛도 있기 때문입니다.)

일단 난 반사광을 그림으로 그려 보겠습니다.

그림 4.2. 난 반사광

그림 4.2에서 아직 보여 드리지 않은 것이 조금 후에 배워 볼 정 반사광입니다. 정 반사광이 무엇인지는 나중에 알려 드릴 테니 일단은 입사광 중의 일부는 난 반사광이 되고 다른 일부는 정 반사광이 된다고만 기억해 두세요.

자, 그렇다면 수학적으로 난 반사광을 어떻게 계산할까요? 당연히 수학자마다 다른 주장을 하지만 그 중에서 게임에서 주로 사용하는 람베르트(lambert) 모델을 살펴봅시다. 요한 람베르트라는 수학자가 창시한 람베르트 모델은 표면법선(법선(normal)이란 표면의 방위(orientation)를 나타내는 벡터입니다. 따라서 그림 4.2에서처럼 좌우로 평평한 평면의 법선은 위쪽으로 수직인 선이 됩니다.)과 입사광이 이루는 각의 코사인 값을 구하면 그게 바로 난 반사광의 양이라고 합니다. 그렇다면 일단 코사인 함수의 그래프를 볼까요?


그림 4.3. y = cos(x) 그래프

위 그래프를 보시면 입사광과 표면 법선의 각도가 0일 때, 결과(y축의 값)가 1인 거 보이시죠? 그리고 각도가 늘어날수록 결과가 점점 작아지다가 90도가 되니 0이 돼버립니다. 여기서 더 나아가면 그 후로는 아예 음수 값이 돼버리네요? 그러면 실제 세계에서 빛의 각도에 따라 결과가 어떻게 바뀌는지 살펴 볼까요?

그림 4.4. 입사광과 법선이 이루는 다양한 각도

위의 그림에서 평면이 가장 밝게 빛나는 때가 언제일까요? 당연히 해가 중천에 떠있을 때겠죠? (그림 a) 그리고 해가 저물어감에 따라 점점 표면도 어두워지겠네요. (그림 b) 이제 해가 지평선을 넘어가는 순간, 표면도 깜깜해집니다. (그림 c) 그렇다면 해가 지고 난 뒤엔 어떻게 되죠? 여전히 표면이 깜깜하겠죠? 표면에 전혀 빛이 닿지 않으니까요. 자, 그럼 이 현상을 그래프로 그려보면 어떻게 될까요? 법선과 해가 이루는 각도를 X축으로 두고 표면의 밝기를 Y축으로 하겠습니다. 여기서 Y축이 가지는 값의 범위는 0~1인데0은 표면이 아주 깜깜한 때를(0%), 1은 표면이 최고로 밝은 때(100%)를 나타냅니다.

그림 4.5. 관찰결과를 그려본 그래프

위 그래프에서 -90 ~ 90도사이의 그래프에 물음표를 달아둔 이유는 각도가 줄어듦에 따라 얼마나 빠르게 표면이 어두워지는지를 모르기 때문입니다. 이제 이 그림을 그림 4.3과 비교해 볼까요? 그림 4.3에서 결과가 0 이하인 부분들을 0으로 만들면 지금 만든 그래프와 꽤 비슷하네요? 차이점이라고는 -90 ~ 90도 사이에서 그래프가 떨어지는 속도가 조금 다르다 뿐이군요. 그렇다면 람베르트 아저씨가 표면이 어두워지는 속도를 아주 꼼꼼히 잘 관찰한 뒤에, 위 코사인 공식을 만들었다고 믿어도 될까요? 전 그렇게 믿고 있습니다. -_-

자, 그럼 람베르트 모델을 적용하면 코사인 함수 한 번으로 난 반사광을 쉽게 구할 수 있겠군요! 하지만 코사인 함수는 그다지 값싼 함수가 아니어서 쉐이더에서 매번 호출하는 것이 영 꺼림직합니다. 다른 대안이 없을까요? 수학책을 뒤적여 보니까 내적(dot product)이라는 연산이 코사인을 대신할 수 있다고 나오는 걸요?

θ = A와 B가 이루는 각도
| A | = 방향벡터 A의 길이
| B | = 방향벡터 B의 길이




A ∙ B = cosθ | A || B |

즉,

cosθ = (A ∙ B) ÷ (| A |ⅹ| B |);

위의 내적 공식에 따르면 두 벡터가 이루는 각의 코사인 값은 그 둘의 내적을 구한 뒤 두 벡터의 길이를 곱한 결과로 나눈 것과 같습니다. 여기서 두 벡터의 길이를 1로 만들면 공식을 더 간단히 만들 수 있습니다.

cosθ = (A' ∙ B')

두 벡터가 이루는 각의 코사인 값은 두 벡터의 내적과 같다는 군요. 근데 이렇게 저희 맘대로 벡터의 길이를 바꿔도 되는 걸까요? 이 질문을 다르게 표현하면, '난 반사광을 계산할 때 법선의 길이나 입사광 벡터의 길이가 중요한가요?'입니다. 전혀 그렇지 않지요? 두 벡터가 이루는 각이 중요할 뿐 벡터의 길이는 결과에 아무런 영향을 미치지 않습니다. 따라서 이 두 벡터의 길이를 각각 1로 만들어서 공식을 간단하게 만드는 게 훨씬 나아 보이는군요. (이렇게 길이가 1인 벡터를 단위벡터(unit vector)라고 하며, 단위벡터를 만드는 과정을 정규화(normalize)라고 합니다.)

그럼 내적이 코사인 함수보다 값싼 연산인 이유를 살펴볼까요? 벡터 A의 성분을 (a, b, c)로 두고 벡터 B의 성분을 (d, e, f)로 두면 두 벡터의 내적을 이렇게 간단히 구할 수 있습니다.

A ∙ B = (a ⅹ d) + (b ⅹ e) + (c ⅹ f)

코사인 함수보다 훨씬 간단해 보이는 게 맞죠? 당장 코사인 함수를 구하라고 하면 머리부터 긁적이실 걸요? ^^

자, 그럼 이 정도면 난 반사광에 대해 충분히 설명을 드린 것 같으니 지금 배운 내용들을 까먹기 전에 곧바로 쉐이더를 작성해 보겠습니다.

 

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

HLSL 함수  (0) 2012.07.10
Blending modes..참고 자료  (0) 2012.06.08
정 반사광  (0) 2012.06.08
unity shader 3가지 방식 정리  (0) 2012.06.01
Mobile/BackgroundQueueAdditive(SL_Shader)  (0) 2012.05.21
Posted by 프리랜서 디자이너
TA/Unity2012. 6. 8. 13:21

출처 http://kblog.popekim.com/2012/01/04-part-3.html

[포프의 쉐이더 입문강좌] 04. 기초적인 조명쉐이더 Part 3

이전편 보기

정 반사광

배경
정 반사광(specular light)(이것을 반사광이라 부르기도 합니다.)은 난 반사광과는 달리 한 방향으로만 반사되는 빛으로 입사각이 출사각과 같은 것이 특징입니다. 따라서 정 반사광의 효과를 보려면 빛이 반사되는 방향에서 물체를 바라봐야만 합니다. 모니터에 빛이 반사 되서 화면을 보기 힘들었던 기억 있으시죠? 그 때 모니터를 조금 돌리면 조금 살만했던 거도요? 그게 바로 정 반사광입니다.

앞에서 보여 드렸던 난 반사광을 그림에 정 반사광도 추가해 보지요.

그림 4.8 난 반사광과 정 반사광

난 반사광과 마찬가지로 정 반사광을 수학적으로 재현해내는 수학공식이 여럿 있습니다. 여기서는 게임업계에서 널리 사용하는 기법인 퐁(phong) 모델을 사용하겠습니다. 퐁 모델은 반사광과 카메라벡터(카메라에서 현재 위치까지 선을 그은 벡터)가 이루는 각도의 코사인 값을 구하고, 그 결과를 여러번 거듭제곱하면 정 반사광을 구할 수 있다고 합니다. 아래의 그림을 보시죠.

그림 4.9. 정반사광의 예

반사광(R)과 카메라벡터(V)가 이루는 각도의 코사인 값을 구하는 것은 난 반사광에서 했던 것과 별반 차이가 없겠군요. 법선벡터와 입사광 벡터 대신에 반사광 벡터와 카메라벡터를 쓰는 것만 빼면요. 근데 왜 이 결과에 다시 거듭제곱을 할까요? 역시 코사인 그래프를 보면 답이 보입니다.

그림 4.10. 거듭제곱수가 늘어남에 따라 빠르게 줄어드는 코사인 그래프

위 그래프에서 보면 거듭제곱수가 늘어남에 따라 코사인 값이 빠르게 줄어드는 거 보이시죠? 실생활에서 정 반사광을 관찰해봅시다. 정반사광의 폭이 얼마나 되나요? 난 반사광에 비해 상당히 타이트하지 않나요? 바로 이런 타이트한 정 반사광을 재현하기 위해 코사인 값에 거듭제곱을 하는 겁니다.

그러면 거듭제곱은 몇 번이나 해야 할까요? 이건 사실 표면의 재질에 따라 다릅니다. 거친 표면일수록 정 반사광이 덜 타이트할 테니까 거듭제곱 수를 줄여줘야겠죠. 보통 한 20번 정도 거듭제곱을 해주면 대충 괜찮은 결과를 얻으실 수 있습니다.

그럼 이제 쉐이더를 작성해 봅시다.

기초설정
바로 조금 전에 작성했었던 난 반사광 쉐이더에 정 반사광 조명 코드를 추가하도록 하죠. 어차피 이 두 광이 합쳐져야 제대로 된 조명효과니까요.

그림 4.9에서 새로 추가된 것이 뭐가 있었죠? 반사광 벡터하고 카메라 벡터죠? 반사광 벡터야 입사광 벡터를 법선에 대해 반사시킨 것이니(입사각과 출사각이 같습니다) 이미 가지고 있는 정보에서 구할 수 있겠네요. 카메라 벡터는요? 입사광의 벡터를 구했던 것과 마찬가지 방법으로 카메라 위치에서 현재 위치까지 선을 쭈욱~ 그으면 되겠죠? 그러려면 카메라 위치를 전역변수로 만들어야 겠네요. 렌더몽키의 Lighting 쉐이더 위에 마우스 오른쪽 버턴을 눌러 새로운 float4 변수를 추가합시다. 이름은 gWorldCameraPosition이 적당하겠네요. 이제 이 변수 위에 마우스 오른쪽 버튼을 눌러 ViewPosition이라는 변수 시맨틱을 대입합니다.

이 외에 별다른 설정은 없는 것 같군요. 이제 정점쉐이더를 살펴봅시다.

정점쉐이더
마찬가지로 정점쉐이더의 전체 소스코드부터 보여드리겠습니다.

float4x4 gWorldMatrix;
float4x4 gViewMatrix;
float4x4 gProjectionMatrix;


float4 gWorldLightPosition;
float4 gWorldCameraPosition;


struct VS_INPUT
{
float4 mPosition : POSITION;
float3 mNormal: NORMAL;
};


struct VS_OUTPUT
{
float4 mPosition : POSITION;
float3 mDiffuse : TEXCOORD1;
float3 mViewDir: TEXCOORD2;
float3 mReflection: TEXCOORD3;
};


VS_OUTPUT vs_main( VS_INPUT Input )
{
VS_OUTPUT Output;


Output.mPosition = mul( Input.mPosition, gWorldMatrix );


float3 lightDir = Output.mPosition.xyz - gWorldLightPosition.xyz;
lightDir = normalize(lightDir);

float3 viewDir = normalize(Output.mPosition.xyz - gWorldCameraPosition.xyz);
Output.mViewDir = viewDir;

Output.mPosition = mul( Output.mPosition, gViewMatrix );
Output.mPosition = mul( Output.mPosition, gProjectionMatrix );

float3 worldNormal = mul( Input.mNormal, (float3x3)gWorldMatrix );
worldNormal = normalize(worldNormal);


Output.mDiffuse = dot(-lightDir, worldNormal);
Output.mReflection = reflect(lightDir, worldNormal);


return Output;
}


정점쉐이더 입력데이터 및 전역변수
일단 정점쉐이더 입력데이터를 보죠. 새로 필요한 정점정보가 있나요? 아무리 생각해도 별 다른 게 안 떠오르는 거 보니 없는 거 같네요. 난 반사광에 사용했던 입력구조체를 그냥 사용해도 될 거 같습니다.

그렇다면 전역변수는 어떻죠? 방금 전에 추가했던 gWorldCameraPosition을 선언해야겠죠? 다음의 코드를 추가합니다.

float4 gWorldCameraPosition;

정점쉐이더 출력데이터
이제 정점쉐이더 출력데이터를 살펴보도록 하죠. 난 반사광에서 그랬던 것처럼 정점쉐이더에서 정 반사광을 계산한 뒤에 픽셀쉐이더에 전달해 주면 될까요? 불행히도 그렇진 않습니다. 정 반사광을 구하려면 코사인 값에 거듭제곱을 해야 하는데 거듭제곱을 한 뒤 보간(interpolate)을 한 결과와 보간을 한 뒤에 거듭제곱을 한 결과의 차이는 엄청납니다. 따라서 정 반사광 계산은 픽셀 쉐이더에서 해야 하니 이 계산에 필요한 두 방향벡터인 R과 V를 구한 뒤에 픽셀쉐이더에 전달해 주도록 하겠습니다. 다음의 코드를 VS_OUTPUT에 추가합시다.

float3 mViewDir: TEXCOORD2;
float3 mReflection: TEXCOORD3;

정점쉐이더 함수
이제 정 반사광을 계산하는데 필요한 두 방향벡터를 구해보죠. 카메라 벡터는 어떻게 구한다고 했었죠? 그냥 카메라 위치로부터 현재위치까지 선을 그으면 된다고 했죠? 입사광의 방향벡터를 구하는 것과 별 다를 바가 없겠네요. 입사광의 방향벡터를 구하는 코드 바로 아래에 다음의 코드를 추가합니다.

float3 viewDir = normalize(Output.mPosition.xyz - gWorldCameraPosition.xyz);
Output.mViewDir = viewDir;

이제 정 반사광의 방향벡터를 구할 차례입니다. 이 때, 빛의 입사각과 출사각이 같다고 말씀드렸었죠? 그럼 반사벡터를 구하는 수학 공식이 필요하겠군요. 근데 이런 공식은 굳이 기억하지 않으셔도 됩니다. (저도 수학책 다시 열어봐야 압니다. -_-) 여태까지 그랬던 것처럼 당연히 이런 것을 척척 처리해주는 HLSL 함수가 있겠죠? reflect()라는 함수입니다. reflect는 첫 번째 인자로 입사광의 방향벡터를 두 번째 인자로 반사 면의 법선을 받습니다. Output을 반환하기 바로 전에 다음의 코드를 입력합니다.

Output.mReflection = reflect(lightDir, worldNormal);

자, 이제 두 벡터를 다 구해봤으니 정점쉐이더에서 할 일은 끝났습니다.

픽셀쉐이더
마찬가지로 픽셀쉐이더의 전체 코드부터 보여드립니다.

struct PS_INPUT
{
float3 mDiffuse : TEXCOORD1;
float3 mViewDir: TEXCOORD2;
float3 mReflection: TEXCOORD3;
};


float4 ps_main(PS_INPUT Input) : COLOR
{
float3 diffuse = saturate(Input.mDiffuse);

float3 reflection = normalize(Input.mReflection);
float3 viewDir = normalize(Input.mViewDir);
float3 specular = 0;
if ( diffuse.x > 0 )
{
specular = saturate(dot(reflection, -viewDir ));
specular = pow(specular, 20.0f);
}


float3 ambient = float3(0.1f, 0.1f, 0.1f);

return float4(ambient + diffuse + specular, 1);
}


우선 정점쉐이더 출력데이터에서 가져올 두 벡터를 PS_INPUT 구조체에 추가합니다.

float3 mViewDir: TEXCOORD2;
float3 mReflection: TEXCOORD3;

이전에 diffuse를 구했던 코드 바로 밑에 새로운 코드들을 추가하겠습니다. 우선 mReflection과 mViewDir을 다시 한번 정규화시켜 줍니다. 정점쉐이더에서 이미 단위벡터로 만들었던 이 벡터들을 다시 정규화해 주는 이유는 보간기를 거치는 동안 그 값이 흐트러질 수 있기 때문입니다. (보간기가 선형적(linear)으로 보간을 해서 그렇습니다.)

float3 reflection = normalize(Input.mReflection);
float3 viewDir = normalize(Input.mViewDir);

이제 이 두 벡터의 내적을 구한 뒤, 거듭제곱을 합니다.

float3 specular = 0;
if ( diffuse.x > 0 )
{
specular = saturate(dot(reflection, -viewDir ));
specular = pow(specular, 20.0f);
}

위에서 난반사광의 양이 0% 이상일 때에만 정 반사광을 계산하는 거 보이시죠? 난 반사광이 존재하지 않는 표면에는 이미 빛이 닿지 않으므로 정 반사광이 존재할 수가 없기 때문입니다. 내적을 구할 때 -viewDir을 사용한 것도 보이시죠? 난 반사광을 구할 때와 마찬가지로 두 벡터의 밑동이 만나야 올바른 내적의 결과를 구할 수 있기 때문입니다.

또한 거듭제곱을 할 때 pow() 함수를 이용한 것도 눈 여겨 봐주시기 바랍니다. 여기서는 20번 거듭제곱을 했는데 각 물체마다 이 값을 다르게 하는 것이 보통입니다. (거듭제곱의 수가 높을 수록 정반사광의 범위가 타이트해집니다. 숫자를 바꿔보면서 실험해보세요.) 따라서 이 값을 float형의 전역변수로 선언해주는 게 보다 나은 방법이 되겠습니다. 이 정도는 독자 분들의 몫으로 남겨두도록 하지요.

이제 결과를 반환할 차례입니다. 일단 정 반사광의 효과만을 보기 위해 specular만을 반환해볼까요? 이전에 있던 return문을 다음과 같이 바꿉니다.

return float4(specular, 1);

이제 쉐이더를 컴파일한 뒤 실행해보면 다음의 결과를 보실 수 있을 것입니다.

그림 4.11. 난 반사광에 비해 매우 강렬하고 타이트한 하이라이트를 보여주는 정 반사광

이제 정 반사광이 어떤 건지 확실히 보이시죠? 여기에 난 반사광을 더하면 보다 완벽한 조명효과가 되겠네요. return 코드를 다음과 같이 바꿔봅시다.

return float4(diffuse + specular, 1);

위 코드에서 난 반사광과 정 반사광을 더하면 그 결과가 1이 넘는 경우가 있는데 크게 걱정하지 않으셔도 됩니다. 그런 경우엔 알아서 1이 됩니다. (현재 하드웨어 백버퍼의 포맷이 8비트 이미지이기 때문입니다. 부동소수점 텍스처를 사용하면 1 이상의 값을 저장할 수도 있습니다.)

이제 정점쉐이더와 픽셀쉐이더를 각각 컴파일 하신 뒤 미리 보기 창을 보면 다음과 같은 결과가 보이죠?

그림 4.12. 난 반사광 + 정 반사광

자, 이 정도면 훌륭한 조명효과입니다. 하지만 공의 왼쪽 밑부분이 칠흑같이 어두운 게 좀 망에 안 드는군요. 앞서 말씀 드렸다시피 실제세계에서는 간접광이 저 어두운 부분을 비춰줄 텐데 말이지요. 그럼 아주 간단하게 주변광을 정의해줘서 저 부분을 조금이나마 밝혀볼까요? 주변광을 10%로 선언해서 ambient 변수에 대입해주도록 합시다.

float3 ambient = float3(0.1f, 0.1f, 0.1f);

그리고 최종 반환 값에 ambient를 추가합니다.

return float4(ambient + diffuse + specular, 1);


이제 결과가 아래와 같이 바뀔 겁니다.

그림 4.13. 주변광 + 난 반사광 + 정 반사광

선택사항: DirectX 프레임워크
이제 C++로 작성한 DirectX 프레임워크에서 쉐이더를 사용하시고자 하는 분들을 위한 선택적인 절입니다.

우선 '제3장: 텍스처매핑'에서 사용했던 프레임워크의 사본을 만들어 새로운 폴더에 저장합니다. 그 다음, 렌더몽키에서 사용했던 쉐이더와 3D 모델을 DirectX 프레임워크에서 사용할 수 있도록 파일로 저장합니다. Sphere.x와 Lighting.fx라는 파일이름을 사용하도록 하겠습니다.

이제 비주얼 C++에서 솔루션 파일을 엽니다.

자, 그럼 전역변수를 먼저 살펴보겠습니다. 일단 이 장에서는 텍스처를 사용하지 않으니 저번 장에서 선언했던 텍스처 변수, gpEarthDM를 지우겠습니다. 그 다음, 쉐이더 변수의 이름을 gpTextureMappingShader에서 gpLightingShader로 바꿉니다.

이제 새로운 변수들을 선언할 차례입니다. 광원의 위치와 카메라의 위치가 필요했었죠? 이 둘은 모두 월드공간 안에 있었네요. 렌더몽키에서 사용했던 빛의 위치를 다시 사용하겠습니다.

// 빛의 위치
D3DXVECTOR4 gWorldLightPosition(500.0f, 500.0f, -500.0f, 1.0f);

카메라 위치는 예전에 RenderScene() 함수 안에서 사용했던 값을 그대로 가져왔습니다.

// 카메라 위치
D3DXVECTOR4 gWorldCameraPosition( 0.0f, 0.0f, -200.0f, 1.0f );

이제 CleanUp() 함수로 가봅시다. 더 이상 gpEarthDM 텍스처를 사용하지 않으니 이를 해제하는 코드를 지웁니다.

다음은 LoadAssets() 함수 입니다. 우선 gpEarthDM 텍스처를 로딩하는 코드를 삭제합니다. 그리고 쉐이더의 파일명을 Lighting.fx로 바꿉니다. gpTextureMappingShader라는 변수명을gpLightingShader로 바꾸는 것도 잊지 마세요.

// 텍스처 로딩


// 쉐이더 로딩
gpLightingShader = LoadShader("Lighting.fx");
if ( !gpLightingShader )
{
return false;
}

마지막으로 RenderScene() 함수를 보겠습니다. 일단gpTextureMappingShader 라는 변수명을 모두 찾아gpLightingShader로 바꿉니다. 이제 뷰행렬을 만드는 코드를 보죠. 뷰행렬을 만들 때 사용했던 vEyePt라는 변수가 있었죠? 이 변수의 값이 앞서 정의했던 gWorldCameraPosition의 값과 동일하니 gWolrldCameraPosition의 값을 사용하도록 하지요.

예전에 아래처럼 되어 있던 코드를

D3DXVECTOR3 vEyePt( 0.0f, 0.0f, -200.0f );

다음과 같이 바꿉니다.

D3DXVECTOR3 vEyePt( gWorldCameraPosition.x, gWorldCameraPosition.y,
gWorldCameraPosition.z );

이제gpLightingShader->SetTexture() 코드를 지웁니다. 이 장에서 만든 쉐이더에는 텍스처를 사용하지 않으니 이 코드가 필요 없습니다. 그럼 마지막으로 광원의 위치와 카메라의 위치를 쉐이더에 전달해 줍니다. 이들의 데이터형은 D3DXVECTOR4이므로 쉐이더에서 SetVector()를 호출합니다.

gpLightingShader->SetVector("gWorldLightPosition", &gWorldLightPosition);
gpLightingShader->SetVector("gWorldCameraPosition", &gWorldCameraPosition);

이제 코드를 컴파일 한 뒤 실행해보시죠. 아까 렌더몽키에서 보셨던 것과 동일한 결과를 볼 수 있죠?

기타 조명기법
여전히 대부분의 게임이 사용하는 조명기법은 람베르트 + 퐁이지만 최근 들어 다른 조명기법들을 사용하는 게임들이 늘어나고 있습니다. 조명기법을 좀 더 심층적으로 연구하고 싶으신 독자 분들을 위해 몇 가지 기법을 언급하겠습니다.


  • 블린-퐁(Blinn-Phong): 퐁과 거의 비슷한 기법. 현재도 많이 사용함
  • 오렌-네이어(Oren-Nayar): 표면의 거친 정도를 고려한 난 반사광 조명기법
  • 쿡-토런스(Cook-Torrance): 표면의 거친 정도를 고려한 정 반사광 조명기법
  • 구면조화 조명기법(spherical harmonics lighting): 오프라인에서 간접광을 사전 처리한 뒤, 실시간에서 이를 주변광으로 적용할 때 사용할 수 있음


정리
다음은 이 장에서 배운 내용을 짧게 요약해 놓은 것입니다.

  • 람베르트 모델은 난 반사광을 계산하는 기법으로 코사인 함수를 사용한다.
  • 퐁 모델은 정 반사광을 계산하는 기법으로 코사인 값을 거듭제곱 한다.
  • 벡터의 길이를 1로 바꾸면 내적을 구하는 것만으로도 코사인 함수를 대신할 수 있다.
  • 동일한 계산을 어느 쪽에서도 할 수 있다면 픽셀쉐이더 보다는 정점쉐이더에서 한다.
  • 이 장에서 배운 조명보다 훨씬 사실적이고 복잡한 기법들이 존재한다. 그 중 일부는 이미 몇몇 게임에서 쓰이고 있다.


이제 조명기법까지 마쳤으니 쉐이더의 기초는 다 배운 거나 다름없습니다. 다음 장부터는 여태까지 배웠던 지식들을 잘 혼합하여 보다 실용적인 기법들을 구현해 보겠습니다. 제1~4장 중에 잘 이해가 안 되는 내용이 있었다면 다시 한 번 복습을 하신 뒤에 제5장으로 넘어오시기 바랍니다.

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

Blending modes..참고 자료  (0) 2012.06.08
기초적인 조명쉐이더  (0) 2012.06.08
unity shader 3가지 방식 정리  (0) 2012.06.01
Mobile/BackgroundQueueAdditive(SL_Shader)  (0) 2012.05.21
Unity Mobile Shader  (0) 2012.05.21
Posted by 프리랜서 디자이너
TA/Unity2012. 6. 1. 09:35

아래 내용은 부트캠트에서 강연 하신 이득우님의 자료를 일부 요약 한것입니당.

 

unity의 3가지 방식의 셰이더

 

Fixed Function Program
• 키워드로 구성
• 정점기반 라이팅 ( 가볍고 빠르다 )
• 오래된 하드웨어와 호환

 

Fixed Function Program 예시


Shader "Fixed Function Shader" {
Properties {
_Color ("Main Color", Color) = (1,1,1,0)
_SpecColor ("Spec Color", Color) = (1,1,1,1)
_Emission ("Emmisive Color", Color) = (0,0,0,0)
_Shininess ("Shininess", Range (0.01, 1)) = 0.7
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlendTex ("Alpha Blended (RGBA) ", 2D) = "white" {}
}
SubShader {
Pass {
Material {
Diffuse [_Color]
Ambient [_Color]
Shininess [_Shininess]
Specular [_SpecColor]
Emission [_Emission]
}
Lighting On
SeparateSpecular On
SetTexture [_MainTex] {
combine previous * texture
}
SetTexture [_BlendTex] {
combine previous lerp (texture) texture
}
}
}
}

 

-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------

 

Vertex / Fragment Program
• 셰이더 모델 : 2.0/3.0
• 셰이더 언어 : Cg, GLSL
• 셰이더 : 버텍스 , 픽셀 셰이더
• CGIncludes 디렉터리
• 셰이더 관련 다양한 매크로 및 함수 제공
• Cg To GLSL 자동 변환

 

Vertex/Fragment Shader 예시

 

Shader "Cg shader" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 inVert : POSITION)
: SV_POSITION
{
float4 outVert =
mul (UNITY_MATRIX_MVP, inVert);
return outVert;
}
half4 frag(float4 i) : COLOR
{
return half4(1.0, 0.0, 0.0, 1.0);
}
ENDCG
}
}
}

 

------------------------------------------------------------------

------------------------------------------------------------------

 

 

surface shader

 

 

 

CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG

 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

 

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

결론

 

Fixed Function Program 은 버텍스라이트를 기반으로 하는 가벼운 모바일게임에서 가볍게 돌리 있다는 얘기니까...

너를 앞으론모바일용으로 규정하겠다 ㅎㅎㅎ

 

 

Vertex / Fragment Program 은 Forward Randering 에 대응 하는 녀석이고, 내가 공부하고있는 부분이며 공부하기에 딱 좋다.

너무 어려워 아오 머리야...

 

 

surface shader 는 Foward와 Deferred둘다 대응이되는 shader라는군...이걸로 짜는게 사실편하다..몇줄 안넣어도 대단한 원리 이런거 잘 몰라도도 짤수 있으니까....하지만 공부가 안되 이건.....

 

 

 

 

 

 

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

Blending modes..참고 자료  (0) 2012.06.08
기초적인 조명쉐이더  (0) 2012.06.08
정 반사광  (0) 2012.06.08
Mobile/BackgroundQueueAdditive(SL_Shader)  (0) 2012.05.21
Unity Mobile Shader  (0) 2012.05.21
Posted by 프리랜서 디자이너
TA/Unity2012. 5. 21. 14:14
  1. hader "Mobile/BackgroundQueueAdditive"
  2. {
  3. Properties
  4. {
  5. _Color ("Main Color", Color) = (.5,.5,.5,0)
  6. _EnvMap ("EnvMap", 2D) = "black" { TexGen SphereMap }
  7. }
  8. Category
  9. {
  10. Tags { "Queue"="Background" "IgnoreProjector" = "True" "RenderType"="Background"}
  11. //SeparateSpecular On
  12. Lighting Off
  13. Zwrite off
  14. Fog { Mode Off }
  15. //Ztest Always
  16. //Cull off //Will Show back thorugh front
  17. Blend One One // additive
  18. //Blend One OneMinusSrcColor // soft additive
  19. //Blend SrcAlpha OneMinusSrcAlpha // real alpha blending
  20. SubShader
  21. {
  22. Pass
  23. {
  24. SetTexture [_EnvMap]
  25. {
  26. combine texture
  27. }
  28. SetTexture [_EnvMap]
  29. {
  30. constantColor [_Color]
  31. Combine previous * constant DOUBLE, previous * constant
  32. }
  33. }
  34. }
  35. }
  36. }

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

Blending modes..참고 자료  (0) 2012.06.08
기초적인 조명쉐이더  (0) 2012.06.08
정 반사광  (0) 2012.06.08
unity shader 3가지 방식 정리  (0) 2012.06.01
Unity Mobile Shader  (0) 2012.05.21
Posted by 프리랜서 디자이너
TA/Unity2012. 5. 21. 01:18

원문 http://chulin28ho.egloos.com/5526644 대충 살아가는 개발자.




Unity Mobile Shader

유니티의 모바일 쉐이더가 새로 생겼지만,  애석하게도 도움말이 없습니다. (혹시 제가 못 찾은 겁니까?)
그래서 아티스트 분들을 위해 제가 간단하게 정리해 보았습니다. 

Built-in mobile shader




Mobile / Background 쉐이더 
라이팅 영향도 받지 않고 단지 텍스쳐만 출력하는 초 간단한 쉐이더입니다. 
재미있게도 Zwrite도 하지 않습니다. 즉 이것은 말그대로 원거리 배경용 쉐이더. 스카이박스는 아니고 멀리 보이는 원경 한 장짜리 이미지 같은 경우에 쓸 수 있는 쉐이더입니다. 0.5g

Mobile / Bumped Specular 
보이는 그대로, 모바일용으로 만든 스페큘러가 포함된 노말맵 사용 쉐이더입니다. 
좀 더 간략화 된 특징을 가지고 있는데, 
- 메인 칼라나 스페큘러 칼라를 지정할 수 없습니다. 
- 스페큘러에서 사용하는 라이트 방향은 버텍스에서 대충 받아서 옵니다.  
- 노말맵의 타일링이나 옵셋은 베이스 텍스쳐의 설정값을 따릅니다. 
- 디퍼드 라이팅을 지원하지 않습니다. 라이트맵도 지원하지 않습니다. 
- 디렉셔널 라이트 1개만 지원되고, 나머지 라이트들은 버텍스 라이트나 구면조화 라이트로 작동합니다. 
일반적인 범프/스페큘러보다는 좀 가볍게 되어 있긴 합니다만, 그래도 노말맵 연산이 되므로 모바일에서 함부로 사용해서는 곤란합니다.  3g

Mobile / Bumped Specular (1 Directional Light) 
위의 범프 스페큘러 쉐이더와 동일하지만, 단 한가지만 다릅니다. 
- 디렉셔널 라이트 1개만 지원되고, 나머지 라이트는 무시됩니다. 
즉 위의 쉐이더보다 아주 약간 가볍다고 할 수 있습니다.  2.7g 

Mobile / Particles / Additive
이전에 사용하던 일반적인 Add 쉐이더와 거의 같으며, 다른점은 다음과 같습니다. 
- Tint Color가 없습니다. 
- 스무스 파티클 옵션이 없습니다. 
- 알파테스트가 없습니다 (전에는 있었나?)
- 칼라마스크가 없습니다. 
사실상 그 외는 동일하게 제작되어 있습니다. 좀 더 가벼워진 버전이라고 할 수 있습니다. 1.5g 

Mobile / Particles / Alpha Blended
이전에 사용하던 알파 블렌딩 쉐이더와 동일하며, 다른점은 위의  Add 쉐이더와 같습니다.

Mobile / Particles / Multiply
이전에 사용하던 멀티 쉐이더와 동일하며, 다른점은 아래와 같습니다.
- 스무스 파티클 옵션이 없습니다. 
- 알파테스트가 없습니다 
- 칼라마스크가 없습니다.
역시 이전에 사용하던 쉐이더와 동일하되 약간씩 가볍게 제작된 것 외에 차이가 없습니다 . 

Mobile / Particles / VertexLit Blended
이전에 사용하던 버텍스라이트 블렌디드 쉐이더와 동일하며, 다른점은 아래와 같습니다. 
- 알파테스트가 없습니다. 
- 칼라마스크가 없습니다. 
이하 상동

Mobile / Skybox
이전에 사용하던 스카이박스 쉐이더처럼 6면의 텍스쳐를 집어 넣습니다. 그렇지만 단지 Tint Color 만이 제거되어 있습니다. 
사실상 거의 같지만 아주 살짝 가벼운 정도입니다. 대부분, 기능들을 꼭 제거할만큼 효과가 커 보이지 않습니다. 

Mobile / VertexLit
기본적인 버텍스 칼라 쉐이더에서 일부 기능이 삭제된 쉐이더입니다. 
- per material 칼라가 삭제되어 있습니다. 
- 스페큘러가 없습니다. 
- emission이 없습니다. 
그 외는 동일합니다. 


사실상 모바일 쉐이더라고 해서 특별하게 만들어 진 것은 없으며, 단지 기능이 몇 개씩 삭제된 정도입니다. 즉 일반 게임용 쉐이더도 '가벼운 쉐이더 라면' 모바일에서도 대부분 똑같이 돌아가며, 사용에 제약이 없습니다. 
이펙트 쉐이더들도 모바일용으로 컨버팅해 놓도록 해야겠네요... 

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

Blending modes..참고 자료  (0) 2012.06.08
기초적인 조명쉐이더  (0) 2012.06.08
정 반사광  (0) 2012.06.08
unity shader 3가지 방식 정리  (0) 2012.06.01
Mobile/BackgroundQueueAdditive(SL_Shader)  (0) 2012.05.21
Posted by 프리랜서 디자이너

http://www.unitykoreawiki.com/index.php?n=KrMain.SL-Fallback


ShaderLab syntax: Fallback

모든Subshaders후에 Fallback이 정의될 수 있습니다. 그것은 기본적으로 어떠한 서브 쉐이더를 하드웨어 위에서 실행될 수 없으면 또다른 쉐이더로부터의 그것들을 사용하는 것을 시도해보라고 말합니다.

Syntax

Fallback "name"
주어진 이름으로 쉐이더에 대체.
Fallback Off
심지어 어떠한 서브 쉐이더가 하드웨어에서 실행될 수 없음에도 명확하게 어떠한 대체도 없고 어떠한 주의 메세지도 프린트되어 지지말아야 한다고 나타냅니다.

Details

대체를 나타내는 서술은 마치 그 다른 쉐이더로부터 모든 서브 쉐이더가 그것의 위치에 삽입되어지는 것처럼 같은 효과를 가집니다.

Example

Shader "example" {
    // properties and subshaders here...
    Fallback "otherexample"
} 

결론은 하드웨어 문제로 작성된 셰이더를 돌릴수 없을때 이것을 사용하라~~ 이런것이군먼

'TA > Unity Shader 레퍼런스' 카테고리의 다른 글

Writing vertex and fragment shaders  (0) 2012.07.12
Blending  (0) 2012.06.25
RenderTech-VertexLit  (0) 2012.05.17
RenderTech-ForwardRendering  (0) 2012.05.17
RenderTech-DeferredLighting  (0) 2012.05.17
Posted by 프리랜서 디자이너

http://unitykoreawiki.com/index.php?n=KrMain.RenderTech-VertexLit

 

 

Vertex Lit Rendering Path Details

이 페이지는 Vertex Lit rendering path의 자세한 내용을 다룹니다.

Vertex Lit 경로는 일반적으로 오브젝트 꼭지점에서 계산된 모든 빛으로부터의 조명과 함께 하나의 패스에서 각각의 오브젝트들을 그립니다.

패스를 그리는 가장 빠른 방법이며 가장 폭넓은 하드웨어 지원을 가집니다 (그러나 그것은 콘솔에서 작동하지 않음을 명심하십시오).

모든 조명이 꼭지점 레벨에서 계산되기 때문에 이 렌더링 경로는 픽셀당 효과의 대부분을 지원하지 않습니다: 그림자, 일반 매핑, light cookies, 고도로 구체화된 반사 하이라트는 지원되지 않습니다.

 

 

여기서 일반 맵핑은 노멀맵을 말하는거임 ㅡㅡ;;

'TA > Unity Shader 레퍼런스' 카테고리의 다른 글

Writing vertex and fragment shaders  (0) 2012.07.12
Blending  (0) 2012.06.25
Fallback  (0) 2012.05.21
RenderTech-ForwardRendering  (0) 2012.05.17
RenderTech-DeferredLighting  (0) 2012.05.17
Posted by 프리랜서 디자이너

http://unitykoreawiki.com/index.php?n=KrMain.RenderTech-ForwardRendering

 

포워드 렌더링 패스 상세사항

이 페이지는 Forward rendering path에 대한 자세한 서술합니다.

하나 혹은 여러 개의 패스의 각 포워드 렌더링 패스는 오브젝트에 영향을 주는 라이트에 달려 있습니다. 라이트들은 또한 포워드 렌더링에 의해 그들의 설정과 강도에 따라 다르게 다루어지고, 좌우되지요.

구현 상세사항

포워드 렌더링에서, 각 오브젝트에 적용되는 가장 밝은 라이트들은 퍼 픽셀 라이트 모드로 온전하게 렌더링 됩니다. 그리고, 4개까지의 라이트들은 버텍스 당 라이트로 계산됩니다. 다른 라이트들은 Spherical Harmonics (SH) 로 계산됩니다. 이것은 많이 빠르지만 근사값에 불과합니다. 라이트가 픽셀당 라이트인지 아닌지는 다음 사항들에 의합니다:

  • Render Mode가 Not Important로 설정된 라이트는 항상 버텍스당(per-vertex)이거나 SH 입니다.
  • 가장 밝은 방향성 라이트는 언제나 픽셀 당 라이트 입니다.
  • Render Mode가 Important로 설정된 라이트는 언제나 픽셀 당 (per-pixel) 입니다.
  • 만일 위 결과가 현재 Pixel Light Count Quality Setting보다 덜한 라이트라면, 밝기를 감소하기 위해서 더 많은 라이트가 픽셀 당으로 랜더링 됩니다.

각 오브젝트의 랜더링은 다음과 같이 발생합니다:

  • 기본패스는 한 개의 픽셀 당 방향성 라이트와 모든 버텍스당/SH 라이트에 적용합니다.
  • 다른 픽셀 당 라이트는 추가적인 패스에, 각 라이트 당 한 패스로 랜더링 됩니다.

예를 들어, 만약 어떤 오브젝트가 여러 개의 라이트에 영향을 받는다면 (아래 그림의 공이 A에서 H 까지의 라이트 영향을 받으면):

A 부터 H 까지의 각 라이트는 같은 색과 강도를 가지고 있다고 합시다. 이들 모두는 자동 렌더링 모드라고 한다면, 그래서 이들은 이 오브젝트를 위해 정확하게 이 순서로 정렬 될 것입니다. 가장 밝은 라이트들은 픽셀당 라이트 모드로 렌더링 됩니다. (A에서 D) 그리고 최대 4개까지 버텍스당 라이트 모드로 (D에서 G) , 그리고 최종적으로 나머지 라이트는 SH가 됩니다. (G에서 H 까지):

라이트 그룹은 겹쳐집니다; 예를 들어 마지막 픽셀 당 라이트는 버텍스 당 라이트 모드와 섞이고 그래서 오브젝트와 라이트가 움직임에 따라 더 적은 "light popping"이 생깁니다.

기본 패스

기본 패스는 하나의 픽셀 당 방향성 라이트와 모든 SH 라이트와 함께 오브젝트를 랜더링 합니다. 이 패스는 또한 쉐이더에서 모든 라이트 맵, 은은하고 방사적 라이팅을 추가합니다. 이 패스에서 랜더링 된 방향성 라이팅은 Shadows을 가질 수 있습니다.

추가 패스들

추가 패스들은 해당 오브젝트에 추가되는 각 픽셀당 라이트를 위해 랜더링 됩니다. 이러한 패스들의 라이트들은 쉐도우를 가질 수 없습니다 (그래서 결과적으로, Forward Rendering은 쉐도우를 가진 하나의 방향성 라이트를 지원합니다).

성능 고려사항

SH (Spherical Harmonics, 구면 조화 함수)라이트는 매우 빨리 랜더링 될 수 있습니다. 그들은 CPU에 아주 작은 비용만 부과하며 GPU에 적용은 actually free입니다(그것은, 기본 패스는 항상 SH 라이팅을 연산합니다; 그러나 SH 라이트는 작동하는 방식 때문에 그 비용은 얼마나 많은 SH 라이트가 있느냐에 상관없이 항성 정확하게 동일합니다).

SH 라이트의 단점은:

  • 그들이 오브젝트의 픽셀이 아닌 버텍스에서 연산됩니다. 그것은 라이트 쿠키나 노멀 맵을 지원하지 않는 다는 것을 의미합니다.
  • SH라이팅은 매우 낮은 주파수를 가지고 있습니다. SH 라이트를 가지고는 선명한 라이트 전환을 할 수 없습니다. 그들은 또한 분산 라이팅 (반사성 하이라이트에는 너무 낮은 주파수)에만 영향을 미칩니다.
  • SH 라이팅은 지역적이지 않습니다; 점이건 집중조명이건 일부 표면에 근접한 SH 라이트는 "look wrong" 것 입니다.

요약하면, SH 라이팅은 대부분 작은 동적 오브젝트에 사용하기에 충분합니다.

'TA > Unity Shader 레퍼런스' 카테고리의 다른 글

Writing vertex and fragment shaders  (0) 2012.07.12
Blending  (0) 2012.06.25
Fallback  (0) 2012.05.21
RenderTech-VertexLit  (0) 2012.05.17
RenderTech-DeferredLighting  (0) 2012.05.17
Posted by 프리랜서 디자이너

http://unitykoreawiki.com/index.php?n=KrMain.RenderTech-DeferredLighting

지연된(Deferred) 라이트 랜더링 경로의 상세사항

이 페이지는 Deferred Lighting rendering path의 상세사항을 설명합니다. 지연된 라이팅(Deferred Lighting)에 대한 기술 적인 개요는 Deferred Lighting Approaches article을 참조하십시오.

Deferred Lighting은 대부분의 라이팅과 쉐이드 충실도의 랜더링 경로입니다:

  • 얼마나 많은 라이트가 오브젝트에 영향을 끼칠 수 있는지에 대한 제한은 없습니다.
  • 모든 라이트는 픽셀 당 평가됩니다. 이것은 그것이 노멀 맵과 어떻게 적절하게 상호작용 할 수 할 수 있는지 등을 의미합니다.
  • 모든 라이트는 쿠키를 가질 수 있습니다.
  • 모든 라이트는 쉐도우를 가질 수 있습니다.

지연된 라이팅의 이점은:

  • 라이팅 비용은 스크린 상의 라이트 크기에 비례합니다. 이 라이트가 몇 개의 오브젝트 위에서 빛나는 지는 중요하지 않습니다. 작은 라이트 = 저렴 입니다!

일관성. 모든 라이트를 위한 라이팅은 픽셀 당 계산됩니다; 커다란 triangles을 분해하는 라이팅 연산은 없습니다.

단점들:

  • 엘리어싱 제거 기능 없음.
  • 지연된 라이팅은 반 투명 오브젝트는 처리할 수 없습니다. 이들은 Forward Rendering을 사용하여 랜더링 됩니다.
  • 제한 된 라이팅 모델 지원 (Blinn-Phong). 모든 라이팅은 같은 방식으로 연산 됩니다; 다른 오브젝트라도 아주 상이한 라이팅 모델은 가질 수 없습니다.
  • "receive shadows" 플래그를 지원하지 않고 라이트 Culling Masks에 대한 제한적인 지원 제공.

지연된 라이팅의 요구사항

  • Unity Pro가 필수.
  • Shader Model 3.0 혹은 더 최신 그래픽 카드, Depth 랜더러 텍스처와 양면 스텐실 버퍼 지원. 2004년 이후에 만든 대부분의 그래픽 카드는 이 기능을 지원함: GeForce FX 그리고 더 최신, Radeon X1300 그리고 그 이후, Intel 965 / GMA X3100 그리고 그 이후.
  • 현재 모바일 플랫폼 상에서는 동작하지 않습니다.

성능 고려사항

지연된 라이팅에서 실제 라이트의 비용은 해당 라이트가 빛을 내는 픽셀 수에 비례합니다; 그러나 장면의 복잡도와는 관련이 없습니다. 그러므로 작은 점이나 집중 조명은 랜더링 비용이 저렴합니다. 점이나 집중 조명은 전적으로 혹은 부분적으로 GPU에서 오브젝트가 픽셀을 건너뛰는 장면에 의해 가려지므로 더 저렴합니다.

물론 쉐이드가 있는 라이트는 없는 것보다 값비쌉니다. 지연된 라이팅에서 그림자를 던지는 주체는 각각의 그림자를 드리우는 라이트를 위한 한번 혹은 그 이상 랜더링을 할 필요가 있습니다. 그리고 쉐도우가 적용되는 그 라이팅 쉐이더는 쉐도우가 없는 것보다 더 값비쌉니다.

구현 시 상세사항

지연된 라이팅이 사용될 때, 유니티의 랜더링 과정은 다음과 같이 진행됩니다:

  1. 기본 패스: 오브젝트가 랜더링 되며 depth, normals, and specular power을 가진 스크린-공간 버퍼를 만들어냅니다.
  2. 라이팅 패스: 라이팅이 이전 버퍼로부터 연산됩니다. 라이팅은 다른 스크린 공간 버퍼로 연산됩니다.
  3. 최종 패스: 오브젝트가 다시 랜더링 됩니다. 연산된 라이팅을 가지고 와서 색상 텍스처와 합치고 ambient/emissive 라이팅을 추가 합니다.

지연된 라이팅을 처리 할 수 없는 쉐이더를 가진 오브젝트들은 이 과정을 마치고 RenderTech-ForwardRendering 경로를 사용하여 랜더링 됩니다.

기본 패스(Base Pass)

기본 패스는 각 오브젝트를 한번 랜더링 합니다. 뷰 공간의 노멀과 반사 파워(specular power)는 하나의 ARGB32 Render Texture으로 랜더링 됩니다 (RGB 채널에서 노멀로, A에서 반사 파워로). 만일 플랫폼과 하드웨어가 Z 버퍼를 텍스처로 읽는 것을 지원한다면, 그러면 Depth는 명시적으로 랜더링 되지 않습니다. 만일 Z 버퍼가 텍스처로 접근할 수 없다면, 그 depth는 추가적인 랜더링 패스에서 shader replacement을 사용하여 랜더링 됩니다.

기본 패스의 결과는 노멀과 반사파워를 가진 장면 콘텐츠와 랜더러 텍스처로 채워진 Z 버퍼입니다.

라이팅 패스

라이팅 패스는 depth, normal 그리고 반사파워를 기반으로 라이팅을 연산합니다. 라이팅은 스크린 공간에서 연산되므로, 장면의 복잡도와는 관련이 없습니다. 라이팅 버퍼는 하나의 ARGB32 랜더러 텍스처로 RGB 채널에 분산 라이팅이, A 채널에 흑백 반사 라이팅이 있습니다. 라이팅 값은 대수 인코딩을 사용하여 ARGB32 텍스처에서 일반적으로 가능한 범위보다 더 확장된 동적 범위를 제공하기 위하여 암호화됩니다.

라이팅 모델은 Blinn-Phong에 고정됩니다.

카메라의 근접 평면을 지나지 않는 점과 집중 조명은 장면(scene)에 대한 Z buffer test 가 허용된 상태로 3D 형태로 랜더링 됩니다. 이것은 부분적으로 혹은 전적으로 가려진 점이나 집중조명을 랜더링 하기 아주 저렴하게 만들어 줍니다. 근접 평면을 가로지르는 방향성 라이트(Directional lights)와 점/집중조명(Point/Spot lights)은 풀 스크린 쿼즈로써 랜더링 됩니다.

만일 라이트의 그림자가 허용되면, 그 들 역시 이 패스에서 랜더링 되고 적용됩니다. 쉐도우는 "free"가 아님을 주의하십시오; 그림자 던지기(shadow casters)도 랜더링이 되어야 하고 좀더 복잡한 라이트 쉐이더가 적용되어야 합니다.

최종 패스

최종 패스는 최종 랜더링 이미지를 생산합니다. 여기서 모든 오브젝트는 다시 랜더링 됩니다; 쉐이더는 라이팅을 가져오고, 텍스처와 합하여 방사적인 라이팅을 추가합니다.

라이트맵은 역시 최종 패스에 적용됩니다. 카메라 가까이, 현실세계 라이팅이 사용되고 오직 bake 된 간접 라이팅만 추가 됩니다. 이것은 화면 겹치기(crossfades)로 완전히 bake 된 라이팅으로 카메라에서 멀어집니다.

'TA > Unity Shader 레퍼런스' 카테고리의 다른 글

Writing vertex and fragment shaders  (0) 2012.07.12
Blending  (0) 2012.06.25
Fallback  (0) 2012.05.21
RenderTech-VertexLit  (0) 2012.05.17
RenderTech-ForwardRendering  (0) 2012.05.17
Posted by 프리랜서 디자이너