반응형

샘플러 상태 사용

연결된 텍스처와 샘플러

대부분의 경우 텍스처를 셰이더에서 샘플링할 때 텍스처 샘플링 상태는 텍스처 설정에서 나옵니다. 기본적으로 텍스처와 샘플러는 함께 결합되어 있습니다. 다음은 DX9 스타일 셰이더 구문을 사용할 때의 기본 동작입니다.

sampler2D _MainTex;
// ...
half4 color = tex2D(_MainTex, uv);

sampler2D, sampler3D, samplerCUBE HLSL 키워드를 사용하여 텍스처와 샘플러를 선언합니다.

대부분의 경우는 이 옵션이 적합하며, 구형 그래픽스 API(예: OpenGL ES)는 이 옵션만 지원합니다.

분리된 텍스처와 샘플러

많은 그래픽스 API와 GPU에서는 텍스처보다 적은 샘플러를 사용할 수 있으며, 연결된 텍스처+샘플러 구문으로 인해 더 복잡한 셰이더를 작성하지 못할 수도 있습니다. 예를 들어, Direct3D 11은 한 셰이더에 최대 128개의 텍스처와 16개의 샘플러를 지원합니다.

Unity는 DX11 스타일 HLSL 구문으로 텍스처와 샘플러를 선언할 수 있으며, 특수한 명명 규칙으로 일치시킵니다. “sampler”+TextureName 형식의 이름을 가진 샘플러는 샘플링 상태를 해당 텍스처에서 가져옵니다.

위 섹션의 셰이더 스니핏은 DX11-style HLSL 구문으로 재작성해도 같은 동작을 합니다.

Texture2D _MainTex;
SamplerState sampler_MainTex; // "sampler" + "_MainTex"
// ...
half4 color = _MainTex.Sample(sampler_MainTex, uv);

그러나 이렇게 하면 한 개 이상의 텍스처를 샘플링하면서 샘플러를 다른 텍스처에서 “재사용”하게 셰이더를 작성할 수 있습니다. 아래 예제에서는 3개의 텍스처에 하나의 샘플러만을 사용합니다.

Texture2D _MainTex;
Texture2D _SecondTex;
Texture2D _ThirdTex;
SamplerState sampler_MainTex; // "sampler" + "_MainTex"
// ...
half4 color = _MainTex.Sample(sampler_MainTex, uv);
color += _SecondTex.Sample(sampler_MainTex, uv);
color += _ThirdTex.Sample(sampler_MainTex, uv);

그러나 DX11-style HLSL 구문은 일부 구형 플랫폼(예: OpenGL ES 2.0)에서는 동작하지 않는다는 점에 유의하십시오. 자세한 내용은 Unity에서 HLSL 사용를 참조하십시오. #pragma target 3.5(셰이더 컴파일 타겟 참조)을 지정해 구형 플랫폼이 해당 셰이더를 건너뛰도록 할 수 있습니다.

Unity는 이 “분리된 샘플러” 접근 방법을 사용한 선언과 텍스처 샘플링에 도움이 될만한 여러 셰이더 매크로를 제공하고 있으니 빌트인 매크로를 참조하십시오. 위의 예제는 이 매크로를 사용해 이렇게 재작성할 수 있습니다.

UNITY_DECLARE_TEX2D(_MainTex);
UNITY_DECLARE_TEX2D_NOSAMPLER(_SecondTex);
UNITY_DECLARE_TEX2D_NOSAMPLER(_ThirdTex);
// ...
half4 color = UNITY_SAMPLE_TEX2D(_MainTex, uv);
color += UNITY_SAMPLE_TEX2D_SAMPLER(_SecondTex, _MainTex, uv);
color += UNITY_SAMPLE_TEX2D_SAMPLER(_ThirdTex, _MainTex, uv);

위는 Unity가 지원하는 모든 플랫폼에서 컴파일할 수 있지만 DX9 같은 구형 플랫폼에서는 3개의 샘플러를 사용하도록 폴백합니다.

인라인 샘플러 상태

“sampler”+TextureName 이름의 HLSL SamplerState 오브젝트 외에도 Unity는 샘플러 이름의 다른 패턴도 인식합니다. 간단하게 하드코드된 샘플링 상태를 셰이더에 바로 선언할 때 유용합니다. 예를 들면, 다음과 같습니다.

Texture2D _MainTex;
SamplerState my_point_clamp_sampler;
// ...
half4 color = _MainTex.Sample(my_point_clamp_sampler, uv);

“my_point_clamp_sampler” 이름은 포인트(가장 가까운) 텍스처 필터링과 클램프 텍스처 랩 모드를 사용하는 샘플러로 인식됩니다.

샘플러 이름은 “inline” 샘플러 상태로 인식됩니다(대소문자 구분).

  • “Point”, “Linear” 또는 “Trilinear”(필수)는 텍스처 필터링 모드를 설정합니다.
  • “Clamp”, “Repeat”, “Mirror” 또는 “MirrorOnce”(필수)는 텍스처 랩 모드를 설정합니다.
    • 랩 모드는 축(UVW)에 지정할 수 있습니다. 예를 들어, “ClampU_RepeatV”
  • “Compare”(선택)는 뎁스 비교용 샘플러를 설정합니다. HLSL SamplerComparisonState 타입과 SampleCmp/SampleCmpLevelZero 함수를 사용합니다.
  • “AnisoX”(X는 2/4/8 또는 16일 수 있음. 예: Ansio8)를 추가하여 이방성 필터링을 요청할 수 있습니다.

다음은 각각 sampler_linear_repeat 샘플링 텍스처와 sampler_point_repeat SamplerState의 예제로, 이름이 필터링 모드를 조절하는 방식을 보여줍니다.

다음은 각각 SmpClampPoint, SmpRepeatPoint, SmpMirrorPoint, SmpMirrorOncePoint, Smp_ClampU_RepeatV_Point SamplerState의 예제로, 이름이 랩 모드를 조절하는 방식을 보여줍니다. 이 마지막 예제에서는 가로(U)와 세로(V) 축에 대해 다른 랩 모드가 설정되었습니다. 모든 경우에 텍스처 좌표는 –2.0 - +2.0입니다.

분리된 텍스처+샘플러 구문과 마찬가지로 인라인 샘플러 상태가 모든 플랫폼에서 지원되지는 않습니다. 현재는 Direct3D 11/12 및 Metal에서 구현됩니다.

참고로, “MirrorOnce” 텍스처 랩 모드는 대부분의 모바일 GPU/API에서 지원되지 않고, 이런 경우 Mirror 모드로 폴백합니다.

“AnisoX” 필터링 모드는 플랫폼 기능과 일부 API에서 최선의 수단입니다. 실제 값은 지원되는 최대 이방성 수준에 기반하여 고정됩니다(이방성 필터링이 지원되지 않는 경우의 비활성화 포함).

 

https://docs.unity3d.com/kr/current/Manual/SL-SamplerStates.html

반응형
반응형

픽킹이란 2D 스크린 좌표를 3D 좌표로 변경한 후 동일한 공간 내에서 교차 판정 하는 것이다.

 

여기서 중요한 것은 2D 좌표를 3D 좌표로 변경한다는 것이고, 동일한 공간! 내에서 교차판정한다는 것이다.

동일한 공간이 중요하다.

지형의 좌표가 존재하는 공간이 월드 공간이기 때문에 반드시 월드 공간으로 바꿔줘야한다.

 

과정을 간단히 말하자면,

1. 윈도우 좌표를 3D 좌표로 변경시킨다.

2. 폴리곤과 충돌체크를 한다. (IntersectTriangle)

이다.

 

픽킹이 처음에 어렵게 느껴지는 이유는 이 IntersectTriangle 함수가 프로그램을 위해 최적화된 함수로 몇몇 과정들이 축소되어있기 때문이다.

 

 

( Picking에 관한 코드는 Dx SampleBrowser에도 존재 하니 그걸 참고하세용 ~ 저도 그걸 참고해서 했어용>,<! )

 

 

먼저 2D 좌표에서 3D 공간 Ray를 뽑아내는 것을 보겠다.

 

 

<설명>

 

1) 윈도우 좌표를 알아낸 후

 

2) 공식을 통해 3D 좌표로 변경시킨다.

 

3) m_vOrig 는 카메라의 위치이고, m_vDir은 Ray의 방향이다. 

m_vDir에 3D 벡터 Ray를 설정한다.

 

4) 카메라 공간 좌표를 월드 공간 좌표로 변경하기 위해 뷰행렬의 역행렬을 곱한다.

여기서 주의해야 할 점은 m_vOrig는 D3DXVec3TransformCoord이고, m_vDir은 D3DXVec3TransformNormal라는 점이다.

왜냐하면 Coord는 위치를 변환하는 것이고, Normal은 벡터를 변환하는 것이다.

 

5) m_vDir 을 정규화하면 Ray가 완성된다.

 

 

이제 폴리곤과 충돌체크를 하는 IntersectTriangle 함수를 알아보겠다.

앞서 말했듯 이 함수는 몇몇 식이 축약됬기 때문에 이해하기가 어렵다.

 

BOOL IntersectTriangle

(D3DXVECTOR3& v0,D3DXVECTOR3& v1,D3DXVECTOR3& v2, FLOAT* t,FLOAT* u,FLOAT* v);

 

m_vOrig는 월드 공간 상의 카메라의 원점이고, m_vDir은 Ray의 방향이다.

v0, v1, v2  삼각형 정점의 위치이다. 

t vOrig에서 v0까지의 거리이다.

u v0에서 v1의 비율이고, v v0에서 v2의 비율이다

 

 

 

점 p(벡터 p)가 v0,v1,v2내에 있는지 판단하는 방법은 벡터로 한다. 

만약, 삼각형을 △OAB라고 할 때, 벡터 P가 삼각형 내에 있을 조건

 

1) u ≥ 0

2) v ≥ 0

3) u + v ≤ 1 

 

이다. 때문에 u와 v를 구해 점 p가 폴리곤 내에 있는지 판단한다. 

 

 

 

 

위 그림을 보면

 

v = |Ob|/|OB| 이다. (왜냐하면, |OB|= |Ob| * v 니까.)

 

nA라는 A의 법선벡터를 구한다.

 

 

 

v = |Ob|/|OB|

 

이 식의 분모와 분자에 nAcosΘ 를 곱한다.

 

v = |Ob| nAcosΘ /|OB| nAcosΘ 

 

이것은 내적을 풀어쓴 것으로 

 

v = Ob · nA / OB · nA 

라고 할 수 있다.

 

그런데 nA에 투영했을 때, OP와 Ob의 정사영이 같으므로 

 

v = OP · nA / OB · nA

 

라고 할 수 있다. 이와 마찬가지로 

 

u = OP · nB / OA · nB

 

라고 할 수 있다.

 

 

N을  v0와 v2의 평면의 법선벡터라고 하자.

여기서 (vOrig – v0) · N = (P – v0) · N 의 정사영이 같음을 볼 수 있다.

따라서 N = vDir × (v2 – v0) 이다. 

 

 

t는 거리라고 했다.

 

tvec = vOrig – v0

e1 = v1 – v0

e2 = v2 – v0

 

라고 하자.

 

u = OP · nB / OA · nB

 

에서 OP는 거리이므로 tvec으로 고칠 수 있다. 그리고 nB는 위에서 법선벡터 N을 구했으므로 vDir x e2 라고 할 수 있다.

 

따라서, 

 

u = tvec · (vDir x e2) / e1 · (vDir x e2) 
v = tvec · (vDir x e1) / e2 · (vDir x e1) 
 
이렇게 구한 u와 v를 통해 p가 삼각형 내에 있는지 판단하는 것이다.
 
IntersectTriangle은 이 식을 풀어서 쓴 것이다.
 
마지막으로 거리 t 를 구하기 위해서 분모를 fDet로 놓는다.
 
fDet = e1 · (vDir x e2) = vDir · (e1 x e2)
 
[ fDet 가 0이면, Ray가 같은 평면이고, 0보다 작으면 뒤에서 쏜 Ray, 0보다 크면 앞에서 쏜 Ray이다. ]
[평면의 방정식이 0보다 크면 Ray가 앞에 있는 것이다.]
 

 

 
위의 식에서
 
|P - vOrig | = t * |vDir |
 
이다. 따라서
 
t = |P - vOrig |/ |vDir |
 
분모, 분자에 |N|cosΘ 를 곱하면
 
t = |P - vOrig | |N|cosΘ  / |vDir | |N|cosΘ 
 
이고, 이것은 내적을 풀어쓴 것이므로 정리하면
 
t = (P - vOrig) · N / vDir · N 
 
이다. 근데 (P - vOrig)과 –tvec의 법선벡터 N에 대한 정사영이 같으므로
 
t = -tvec · N / vDir · N 
 
이다.
 

 

 

ref : https://showmiso.tistory.com/140

반응형
반응형

 

 

 

 

dx 는 왼손 법칙을 사용

opengl 은 오른손 법칙 사용

 

그래서 깊이가 서로 반대로 표현 되게 됩니다

 

 

깊이에 관련된 내용중 하나는 아래와 같은 것이 있습니다

 

 

동차 좌표란 투영행렬연산 까지 거친 좌표를 말하는데

투영행렬까지 연산을 하면  x,y 가  -1~1 그리고 z 는 0~1 사이 범위로 아직 가있지 않게 되고

이것을 동차 좌표라 합니다(동차 절단공간Homogeneous clip space)

 

이때 투영행렬가지 연산 된것을 z 로 나누게 되면 그제서야 직사각형 

 x,y 가  -1~1 그리고 z 는 0~1 

의 범위안에 들어오게 됩니다

 

 

나눈 좌표를 Normalized Coordination space 라 합니다

 

 

반응형
반응형

텍스쳐 한 장을 받는 쉐이더 스크립터를 만들어준다.

램버트 라이팅 세팅으로

 

Shader "Custom/Test"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Lambert

        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

 

----

 

큐브맵을 받을수 있는 프로퍼티를 뚫는다 .

 

 

큐브맵을 받는 형식은 Cube로

 

윈도우 - 렌더링 - 라이팅 세팅에 넣어야한다.

 

 

먼저

 

 

메터리얼에 큐브맵을 넣어준다.

 

 

메터리얼을 스카이박스메터리얼에 넣어준다.

★ 책에서 나온게 버전업 되면서 바뀌었다.

우리가 만든 메테리얼을 넣는게 아니라 검색해서 넣어야한다.

 

우리가 만든 메테리얼은 로봇에다가 넣을 것 이기 때문에

 

로봇 켈리 넣고,

메터리얼 넣고

 

 

sky박스가 반사되도록 만들것이다.

 

 

Input 에

 

 

 

월드 리플렉션이 있음 이걸 사용

 

이걸 똑같이 안쓰면 하드한 리플렉션 공식을 넣어야함

 

 

texCUBE (엔디비아 CG 용어)를 사용해서 이미지를 넣어야한다.

 

 

texCUBE(큐브텍스트 이미지 , 월드 리플렉션) = float4 로 반한

 

 

이미지션에 넣어서 테스트 하기

 

Shader "Custom/Test"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Cube ("CubeMap", Cube) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Lambert

        sampler2D _MainTex;
        samplerCUBE _Cube;

        struct Input
        {
            float2 uv_MainTex;
            float3 worldRefl;
        };

        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            float4 ref =  texCUBE(_Cube, IN.worldRefl);
            o.Albedo = c.rgb;
            o.Emission = ref.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

 

 

 

 

반사되는걸 확인 가능

 

이미션에 넣는 이유, 밝고 어두움을 반사하는데 빛을 넣는다고 밝아 지는게 아니기 때문에 (거울 예제)

 

지금은 흰색인 이유는 알베도에 흰색에 있고, 이미션에도 있어서 1이 넘어서 그럼

 

// 알베도랑 이미션을 같이 쓰면 밝아진다.

 

그래서 알베도를 0으로 만들고, 이미션에 반사된 값을 출력해보자

 

 

---

 

노멀을 넣으면 오류가 생긴다 .

 

 

 

 

이터널 데이터를 써줘야한다

 

 

이터널 데이터를 써주지 않으면

 

★ 월드 리플렉션이랑 월드 노멀을 같이 쓸수가 없다.

 

인풋에 넣어주고

 

 

 

노멀을 넣으면 월드 노멀을 넣ㅇ어줘야한다

 

 

 

노멀에 해당하는 반사벡터를 얻을려면 WorldReflctionVector 써줘야함

노멀을 먼저 쓰고 그걸 받아서 써주는 것이다 !

float4 ref = texCUBE(_Cube, WorldReflectionVector(IN, o.Normal));

 

 

----

Shader "Custom/Test"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Normal ("Normal (RGB)", 2D) = "white" {}
        _Cube ("CubeMap", Cube) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Lambert

        sampler2D _MainTex;
        samplerCUBE _Cube;
        sampler2D _Normal;
        struct Input
        {
            float2 uv_MainTex;
            float3 worldRefl;
            float2 uv_Normal;
            INTERNAL_DATA
        };

        void surf (Input IN, inout SurfaceOutput o)
        {
            o.Normal = UnpackNormal(tex2D(_Normal, IN.uv_Normal));
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            float4 ref =  texCUBE(_Cube, WorldReflectionVector(IN, o.Normal));

            o.Albedo = 0;
            o.Emission = ref.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

 

 

 

 

 

 

----

 

총 과정

 

메터리얼 하나 받는 쉐이더 작성

기본 Lambert 라이트 모델 적용

큐브맵을 받게 프로퍼티 작성

타입을 Cube

변수선언시 타입을 samplerCUBE로

Input 구조체에 worldRefl 정의

texCUBE 함수를 사용해 큐브맵 적용 (_Cube, IN.worldRefl)

결과값이 re 값을 확인(Emission에서)

범프 텍스쳐 넣을 수 있게 속성정의

노멀이 적용된 반사벡터를 구하기 위해서 WorldReflectionVector함수 사용

결과 확인 (Emission)


노말맵을 추가시켰더니 에러가 난다. 에러 내용을 대충 훑어보면

 

Shader error in 'Custom/Post_Work1': Surface shader Input structure needs

INTERNAL_DATA for this WorldNormalVector or WorldReflectionVector usage at

line 200 (on d3d11)

 

INTERNAL_DATA , WorldNormalVector, WorldReflectionVector 관련해서

키워드를 얻을 수 있어서 유니티 홈페이지에서 찾아보았다.

 

정확히는 무슨 말인지는 모르겠지만 worldRefl을 사용할 때 노말 맵을 쓸 경우

WorldReflectionVector를 사용하는데, 픽셀당 반사 벡터를 사용하기 위해

Input구조체에서 INTERNAL_DATA를 선언해서 뭔가 컴파일해주는 듯..?

 

좀 더 쉽게 설명하면 탄젠트 좌표계를 월드 좌표계로 변환시켜주어야 하는데

그 과정에서 변환이 되지 않아 에러가 났던 것이고 INTERNAL_DATA를 통해서

변환해주는 듯하다. (자세한 것은 모르겠다..)

 

유니티가 PBR로 바뀌면서 이런 식으로 큐브 맵 사용이 변경되었다 한다.

그러면 일단 한번 수정해보자.

struct Input { float2 uv_MainTex, uv_NormalMap; float3 worldRefl; INTERNAL_DATA }; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex); o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap)); o.Emission = texCUBE(_CubeMap, WorldReflectionVector(IN, o.Normal)); o.Albedo = c.rgb; o.Alpha = c.a; }

헬멧 MaskMap 추가

만들었던 큐브맵 머티리얼을 헬멧 모델에 적용하여

금속 재질인 것처럼 표현하고 마스크 맵을 적용하여 부분적으로 적용되도록 해보겠다.

Shader "Custom/Post_Work1"
{
    Properties
    {
        _MainTex("Albedo (RGB)", 2D) = "white" {}
        _NormalMap("NormalMap", 2D) = "bump" {}
        _MaskMap("MaskMap", 2D) = "bump" {}    
        _CubeMap("CubeMap", cube) = "" {}
 
        [HDR]_Color("Color", color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
 
        CGPROGRAM
        #pragma surface surf Lambert 
        #pragma target 3.0
 
        sampler2D _MainTex, _NormalMap, _MaskMap;
        samplerCUBE _CubeMap;
 
        struct Input
        {
            float2 uv_MainTex, uv_NormalMap, uv_MaskMap;
            float3 worldRefl;
            INTERNAL_DATA
        };
 
        float4 _Color;
 
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            fixed4 m = tex2D(_MaskMap, IN.uv_MaskMap);
 
            o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap));
            o.Emission = texCUBE(_CubeMap, WorldReflectionVector(IN, o.Normal)) * m.a * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

Reflection Probe 사용하기

리플렉션 프로브란 해당 오브젝트를 배치하여 구역 내에 주변 static오브젝트들을

캡처하여 큐브 맵 데이터로 저장하는 기능이다.

 

 


void surf (Input IN, inout SurfaceOutput o)

{

fixed4 c = tex2D (_MainTex, IN.uv_MainTex);

fixed4 m = tex2D(_MaskMap, IN.uv_MaskMap);


o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap));


fixed3 wr = WorldReflectionVector(IN, o.Normal);

//fixed3 p = texCUBE(_CubeMap, wr);

fixed3 r = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, wr) * unity_SpecCube0_HDR.r;


o.Emission = r * m.a;

o.Albedo = c.rgb;

o.Alpha = c.a;

}

유니티에서 리플렉션 프로브로 큐브 맵을 받기 위해서는 UNITY_SAMPLE_TEXCUBE()를 사용해야 한다.

unity_SpecCube0, unity_SpecCube0_HDR은 리플렉션 프로브 데이터를 가지고 있으며,

unity_SpecCube가 아니라 unity_SpecCube0이 붙은걸 보면 뒤에 1도 존재한다는 것도 알 수 있고

HDR도 1이 존재한다.

 

이를 설명하기 위해서 오브젝트 Mesh Renderer 인스펙터 부분을 보면 Light Probes에

Blend Probes라는 항목이 존재하는데, 리플렉션 프로브 영역으로 이동할 때 리플렉션 이미지가 갑자기 바뀌는 것을 방지하기 위해 블렌딩 해주는 것이라고 한다.

 

 

 

 

 

ref : https://minquu.tistory.com/161

ref : https://bornsoul.tistory.com/15

반응형
반응형

반지름 r 인 구의 표면적 구하는 공식은

 

입니다.

평면에 수직으로 비친 구의 그림자는 원입니다.

이 그림자 원의 면적은

 

입니다.

즉, 반지름이 r 인 구의 표면적은 그림자 면적의 4배가 됩니다.

출처: 3Blue1Brown YouTube Channel

구의 표면적 구하는 공식이 생각나지 않을 때 그림자의 4배를 기억해보면 됩니다.

또한, 구의 지름 높이만큼 구를 둘러싼 원통의 표면적과도 같습니다.

아래 그림에서 원통 측면의 표면적은 원주율에 높이를 곱한 것이므로

입니다.

출처: 위키백과

구의 표면적 구하는 공식이 생각나지 않을 때 기억해보면 됩니다.

다음 그림을 보면 구와 원통의 관계를 알 수 있습니다.

지름과 높이가 구의 지름과 같은 원통측면의 표면적이 어떻게 구의 표면적과 같아지는지 그림으로 쉽게 설명이 됩니다.

출처: 3Blue1Brown YouTube Channel

구와 관련된 공식들을 정리해봤습니다.

구(sphere)의 지름(diameter): D = 2r

구의 둘레(circumference) : C = πD = 2πr

구의 표면적(surface area) : A = 4πr2

구의 체적(volume) : V = (4/3)πr3


 

빛의 성질-5 (Characteristics of Light-5): 빛의 방사와 조사(Radiance and Irradiance of Lights), 입체각(Solid Angle)

 

일단 기계공학 전공자의 입장에서는 빛이라는 것을 다를  장님 코끼리 다리 만지듯이 헷갈리는 것이   가지가 아닙니다.

요즘 강의나가는 대학의 박사과정 학생과 둘이서 몬가를 아주 욜심히 하고 있기에 구찮지만 빛에 대해 기본적으로 알아야  것이 많이 생기고 있습니다. 갑자기 색깔,  Color 대해서 엄청난 궁금증이 생긴 관계로… (논문이 될려나?? 만일 안되몬 이 짜슥이 내 죽일려고 할텐데...)  ^_^!

암튼 색깔이고 지랄이고 간에 빛이 밝다!” 혹은 빛이 어둡다!!”라고   무엇을 기준으로 하는지에 대한 것부터 알아야만 합니다.

여기 저기 빛의 세기에 관해 찾아보니 “Radiance” “Irradiance”라는 용어가 먼저 튀어나옵니다. ~ 몰랑몰랑~~ 일단 한국물리학회(The Koran Physics Society)에서 발간한 물리학 용어집을 찾아보니 “Radiance” “(복사)휘도라고 번역해놓았지만 아쉽게도 “Irradiance”라는 용어에 대해서는 물리학 용어집에서 번역어를 찾을 수가 없군요. 참고로 Journal of the Korean Physics Society는 SCI(SCI IF=0.493)입니다. 한 14~15년 전에 논문 3편 실은 기억이 있네요... 그 때는 SCI IF 1.0 이상이었는데... 쩝!! 만일 JKPS의 IF가 3.0 이상으로 올라가면 이 곳에 논문을 다시 낼 의향은 충분히 있습니다... ^_^!!

영어사전에서는 “Irradiance” 조사(照射), 조도(照度)”라고 번역하고 있습니다. 우리가 흔히 현광등과 같은 조명장치를 이야기   사용하는 한자인 비칠 ()” 사용하고 있습니다.

그럼 빛을 받는 것인가(수광)? 빛을 발하는 것인가(발광)?? “Radiance” “Irradiance” 분명 다른 의미일 것입니다. 그러니까 당연히 단어가 다르겠지요. 구글링(Gooling) 하니 다음과 같은 그림이 나오는군요.

 

몬지는 몰겠지만 [Fig.1] 보니 “Irradiance” 빛을 받는 것이고 “Radiance” 빛이 나가는 것이군요. 그렇다면 “Irradiance” 빛의 입사(들어오는)강도, “Radiance” 빛의 방사(나가는)강도 받아들이면 되겠습니다.

그렇다면 이젠  놈들의 정의와 단위에 대해서 생각해보아야 합니다. 만일 인터넷이라는 것이 없었다면  같이 무식한 인간은 어찌 하였을꼬??? 인터넷의 바다로 다시 퐁당~~

 

What is Solar Irradiance?

Irradiance is the amount of light energy from one thing hitting a square meter of another each second.

(출처https://www.nasa.gov/mission_pages/sdo/science/solar-irradiance.html)

 

 유명한 넘사벽인 NASA임에도 영어가 별로 내요 약간 애매모호한 표현입니다. 저도 영어를  못하지만  생각을 구겨 넣은 의역을 대충 해보면 어떤 광원으로부터 방출되는  에너지가 다른 어떤 표면에 대해서 단위시간당 단위면적당 입사되는(두들겨 패버리는) 총에너지량이다.” 정도로 받아들이면 되겠습니다.

 

그렇다면 단위는 당연히 J/(m2·s) 것이고, 따라서 W/ m2입니다.

가장 쉬운 예가 태양이라는 광원에서 지구의 지표면에 쏟아지는 태양광일 것입니다. 내가  있는 1.0 m2 지표면에 1.0 초당 쏟아지는 태양빛 에너지의  총량을 태양광 입사량,  “Solar Irradiance”라고 합니다.

이제 “Radiance” 알아보기 위해 구글의 이미지 검색을 하니 단색광(Monochromatic Ray) 대한 정의를 [Fig.2] 그림으로 표현하고 있습니다. 그런데  놈의 dW 뭐지? 굳이 우리말로 나타내자면 입체각(Solid Angle) 나타내는 것입니다.

각도(Angle) 측정에는  아시다시피 Degree(o) Radian(rad) 사용하지만 Steradian(sr)이라는 입체각도 사용합니다.

강의를 하다 보면 대학생들임에도 불구하고 학생들이 각도가 단위를 가지는 물리량으로 착각을 하는 경우가 상당히 많은데 각도는 단위를 가지지 않는 무차원(Dimensionless)입니다. 즉, 단순히 숫자를 나타내는 것입니다. 지금 내가 말하고 있는 숫자가 그냥 단순히 숫자를 나타내는 것이 아니고 각도의 의미로 사용하고 있다는 것을 상대방에게 알려주기 위해서 편의상 o, rad, sr 붙여주는 것에 불과합니다.

......

중략..

......

제 기준에서만 말하자면 “Radiance” “Irradiance”의 단위로부터 강도 혹은 세기(Intensity)라는 용어보다는 단위시간당(per unit time) 단위면적당(per unit area)의 에너지량이라는 용어가 더 어울릴 것 같습니다. 일종의 유속(Flux)입니다.

ref : https://blog.naver.com/choi_s_h/221405669446

 

 

 

반응형
반응형

BSDF (Bidrectional ScatteringDistribution Function, 양반향 산란 분포 함수)

BSDF (Bidrectional ScatteringDistribution Function, 양반향 산란 분포 함수)
- 우리가 일반적으로 사용하는 shading model들은 BxDF라는 함수들을 특수화한 model들 이다.  BSDF는 빛이 어떤 물체에 부딪쳤을 때 얼마나 많은 빛이 반사되는가를 나타낸다.  BSDF는 반사현상을 위한 Reflected 요소와 투과현상을 위한 Transmitted 요소로 나뉘며  이를 각각 BRDF(B Reflectance D F, 양반향 반사율 분포 함수)과   BTDF(B Transmittance D F 양반향 투과율 분포 함수)라고 한다.

 

   즉, BSDF가 BRDF와 BTDF의 상위집합이다.

   

   BRDF는 불투명한 표면에서 빛이 반사하는 방식을 정의하는 4차 함수이며 BSSRDF를 간략화 하였다.   간단하게 말하자면 BSSRDF(Bidirectional Surface Scattering Reflectance Distribution Function)은 8차함수이며,
   표면 내부에서의 산란을 표현한다.

 

BRDF (Bidrectional Reflectance

 

Function, 양반향 반사율 분포 함수)
- 단순하게 말하자면 입사하는 빛에 따른 반사하는 빛의 비율에 대한 함수.

- BRDF는 빛이 표면에서 어떻게 반사되는지 설명해 주는 함수, BTDF는 표면에서 어떻게 투과되는지 설명해 주는 함수.

- BRDF의 양방향성 : 만약 BRDF가 실제 물리법칙을 따르는 함수로 만들어졌다면, 

  입사와 반사의 방향이 바뀌어도 그 함수값은 바뀌지 않는다.

 

- BRDF의 비등방성 : 입사와 반사된 방향이 고정되고 표면의 법선을 축으로 표면이 회전된다면

  해당 표면요소의 반사되는 비율은 변할 것이다.

  즉, 표면의 재질이 들어오는 빛의 방향에 따라 다른 반사율을 가진다는 뜻이다 (알루미늄이나 옷감등).

  하지만 다른 많은 재질들이 매끄러워 표면의 위치와 방향에 상관없이 같은 반사율을 가진다 (등방성 재질)

- 일반적인 BRDF를 질적으로 다른 세 개의 컴퍼넌트로 다루는 것이 편리하다.

  각각을 완전 거울 반영(perfect mirror specular)반사, 완전 산란 반사(perfect diffuse reflection), 그리고 광택반사(glossy)이다.

 

 

BRDF가 왜 중요한가?

예를 들어 물을 물처럼 보이기 위해 Lighting이라는 개념이 들어가는데 렘버트의 단순함 만으로는

사실적인 결과물을 얻기 힘들다. 렘버트는 빛이 입사하는 방향에 관계없이 모든 방향으로 같은 양의 빛을

반사한다고 가정하기 때문이다. (렘버트 모델은 상수 BRDF에 의해 완벽하게 분산 표면을 표현한다).

하지만 실제로 물을 본다면 물을 바라보는 위치에 따라 물의 빛의 반사정도나 투과정도가 다르다는 것을 알 수 있다.

즉, 특정 표면이 현재 내가 바라보는 시점과 빛과의 관계에 따른 특정값을 얻을 필요가 있는 것이다.

 

 

BRDF 이론중 특별히 중요한 미립면(microfacet) 이론.

평평해 보이는 면도 사실 세밀하게 거친 면이 있기에 거울처럼 완전 평평한 물체에서 나타나는 정반사가 

나타나틑게 아니라, 각 미립자간의 상호 반사나 표면 물질의 자체적인 산란으로 인해 빛의 반사가 희미해 진다.

이 미립자 BRDF 샘플의 공식을 보면 아래와 같다.

F는 프레넬, G는 기하감쇠, D는 미세면 분포함수이다.

 


BSSRDF (B Surface Scattering Reflectance D F, 양방향 표면 분산 반사 분포 함수)에 대한 글.

- 빛의 입사량에 대해 바깥으로의 복사휘도(radiance)에 관한 함수

- BRDF는 빛이 같은점에서 입출이 된다는 가정하에서 BSSRDF의 근사값이다.

 

 

bssrdf.pdf


 

 

bssrdf2.pdf


 

 

bssrdf3.pdf

 


    
Reference Link

- 3. 왜 물은 물처럼 보이는 걸까? (미세면 brdf)

- Fake BRDF만들기

- 9. May There Be Light

-

 

- Wiki, Bidirectional scattering distribution function

- Mental Ray를 이용한3S(Sub Surface Scattering)의 실체

- 반사함수(Reflectioin Function)

- ShadowGun의 BRDF 셰이더 

- Oren Nayar BRDF

- Microfacet BRDF

-

 

 

ref: https://mgun.tistory.com/1290#google_vignette 

반응형
반응형

One of the most useful effects that isn’t already present in Unity is outlines.

Screenshot from Left 4 Dead.
There are some scripts online that make the geometry bigger and then render it a second time, behind the first. This isn't what we want.

Artifacts from normal extrusion

What we need is a post-processing effect. We'll start by rendering only the objects we want outlined to an image.
To do this, we'll use culling masks, which use layers to filter what objects should be rendered.

Make a new 'Outline' layer
Then, we attach a script to our camera, which will create and render a second camera, masking everything that isn't to be outlined.

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class outline : MonoBehaviour
{
	public Shader DrawAsSolidColor;
	public Shader Outline;
	Material _outlineMaterial;
	Camera TempCam;

	void Start(){
		_outlineMaterial = new Material(Outline);

		//setup the second camera which will render outlined objects
		TempCam = new GameObject().AddComponent<Camera>();
	}
	
	//OnRenderImage is the hook after our scene's image has been rendered, so we can do post-processing.
	void OnRenderImage(RenderTexture src, RenderTexture dst){
		//set up the temporary camera
		TempCam.CopyFrom(Camera.current);
		TempCam.backgroundColor = Color.black;
		TempCam.clearFlags = CameraClearFlags.Color;

		//cull anything that isn't in the outline layer
		TempCam.cullingMask = 1 << LayerMask.NameToLayer("Outline");
		
		//allocate the video memory for the texture
		var rt = RenderTexture.GetTemporary(src.width,src.height,0,RenderTextureFormat.R8);
		
		//set up the camera to render to the new texture
		TempCam.targetTexture = rt;

		//use the simplest 3D shader you can find to redraw those objects
		TempCam.RenderWithShader(DrawAsSolidColor, "");
		
		//pass the temporary texture through the material, and to the destination texture.
		Graphics.Blit(rt, dst, _outlineMaterial);
		
		//free the video memory
		RenderTexture.ReleaseTemporary(rt);
	}
}

 

Note the line:

TempCam.cullingMask = 1 << LayerMask.NameToLayer("Outline");

<< is the bit-shift operator. This takes the bits from the number on the left, and shifts them over by the number on the right. If 'Outline' is layer 8, it will shift 0001 over by 8 bits, giving the binary value 100000000. The hardware can this value to efficiently mask out other layers with a single bitwise AND operation.

We'll also need a shader to redraw our objects. No need for lighting or anything complicated, just a solid color.

 

 

 

 

Shader "Unlit/NewUnlitShader"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(1,1,1,1);
            }
            ENDCG
        }
    }
}

 

 

Finally, let's make a shader that passes an image through as it received it. We'll use this file for outlines later.

Shader "Custom/Post Outline"
{
    Properties
    {
        //Graphics.Blit() sets the "_MainTex" property to the source texture
        _MainTex("Main Texture",2D)="black"{}
    }
    SubShader 
    {
        Pass 
        {
            CGPROGRAM
            sampler2D _MainTex;
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
             
            struct v2f 
            {
                float4 pos : SV_POSITION;
                float2 uvs : TEXCOORD0;
            };
             
            v2f vert (appdata_base v) 
            {
                v2f o;
                 
                //Despite only drawing a quad to the screen from -1,-1 to 1,1, Unity altered our verts, and requires us to use UnityObjectToClipPos.
                o.pos = UnityObjectToClipPos(v.vertex);
                 
                //Also, the UVs show up in the top right corner for some reason, let's fix that.
                o.uvs = o.pos.xy / 2 + 0.5;
                 
                return o;
            }
             
             
            half4 frag(v2f i) : COLOR 
            {
                //return the texture we just looked up
                return tex2D(_MainTex,i.uvs.xy);
            }
             
            ENDCG
 
        }
        //end pass        
    }
    //end subshader
}
//end shader

 


Put the outline component on the camera, drag the shaders to the component, and now we have our mask!

* Render just the geometry to be outlined

 

 

 

Once we have a mask, let's sample every pixel nearby, and if the mask is present, add some color to this pixel. This effectively makes the mask bigger and blurry.

Shader "Custom/Post Outline"
{
    Properties
    {
        _MainTex("Main Texture",2D)="black"{}
    }
    SubShader 
    {
        Pass 
        {
            CGPROGRAM
     
            sampler2D _MainTex;            
            
            //<SamplerName>_TexelSize is a float2 that says how much screen space a texel occupies.
            float2 _MainTex_TexelSize;

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
             
            struct v2f 
            {
                float4 pos : SV_POSITION;
                float2 uvs : TEXCOORD0;
            };
             
            v2f vert (appdata_base v) 
            {
                v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
                o.uvs = o.pos.xy / 2 + 0.5;
                return o;
            }
                          
            half4 frag(v2f i) : COLOR 
            {
                //arbitrary number of iterations for now
                int NumberOfIterations=19;
 
                //turn "_MainTex_TexelSize" into smaller words for reading
                float TX_x=_MainTex_TexelSize.x;
                float TX_y=_MainTex_TexelSize.y;
 
                //and a final intensity that increments based on surrounding intensities.
                float ColorIntensityInRadius=0;
 
                //for every iteration we need to do horizontally
                for(int k=0;k < NumberOfIterations;k+=1)
                {
                    //for every iteration we need to do vertically
                    for(int j=0;j < NumberOfIterations;j+=1)
                    {
                        //increase our output color by the pixels in the area
                        ColorIntensityInRadius+=tex2D(
                                                      _MainTex, 
                                                      i.uvs.xy+float2
                                                                   (
                                                                        (k-NumberOfIterations/2)*TX_x,
                                                                        (j-NumberOfIterations/2)*TX_y
                                                                   )
                                                     ).r;
                    }
                }
 
                //output some intensity of teal
                return ColorIntensityInRadius*half4(0,1,1,1)*0.005;
            }
             
            ENDCG
 
        }
        //end pass        
    }
    //end subshader
}
//end shader


* Sampling surrounding pixels, we redraw the previous image but all colored elements are now bigger

 

 

Then, sample the input pixel directly underneath, and if it's colored, draw black instead:

*Hey, now it's looking like an outline!

 

Looking good! Next, we'll need to pass the scene to our shader, and have the shader draw the original scene instead of black.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class outline : MonoBehaviour
{

	public Shader DrawAsSolidColor;
	public Shader Outline;
	Material _outlineMaterial;
	Camera TempCam;
	
	void Start()
    {
		_outlineMaterial = new Material(Outline);
		TempCam = new GameObject().AddComponent<Camera>();
	}
	
	void OnRenderImage(RenderTexture src, RenderTexture dst){
		TempCam.CopyFrom(Camera.current);
		TempCam.backgroundColor = Color.black;
		TempCam.clearFlags = CameraClearFlags.Color;

		TempCam.cullingMask = 1 << LayerMask.NameToLayer("Outline");

		var rt = RenderTexture.GetTemporary(src.width,src.height,0,RenderTextureFormat.R8);
		TempCam.targetTexture = rt;

		TempCam.RenderWithShader(DrawAsSolidColor, "");

		_outlineMaterial.SetTexture("_SceneTex", src);
		Graphics.Blit(rt, dst, _outlineMaterial);

		RenderTexture.ReleaseTemporary(rt);
	}
}

 

Shader "Custom/Post Outline"
{
    Properties
    {
        _MainTex("Main Texture",2D)="black"{}
        _SceneTex("Scene Texture",2D)="black"{}
    }
    SubShader 
    {
        Pass 
        {
            CGPROGRAM
            sampler2D _MainTex;    
            sampler2D _SceneTex;
            float2 _MainTex_TexelSize;
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
             
            struct v2f 
            {
                float4 pos : SV_POSITION;
                float2 uvs : TEXCOORD0;
            };
             
            v2f vert (appdata_base v) 
            {
                v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
                o.uvs = o.pos.xy / 2 + 0.5;
                return o;
            }
                          
            half4 frag(v2f i) : COLOR 
            {                
            //if something already exists underneath the fragment, discard the fragment.
                if(tex2D(_MainTex,i.uvs.xy).r>0)
                {
                    return tex2D(_SceneTex,i.uvs.xy);
                }
                int NumberOfIterations=19;
 
                float TX_x=_MainTex_TexelSize.x;
                float TX_y=_MainTex_TexelSize.y;
 
                float ColorIntensityInRadius=0;
 
                for(int k=0;k < NumberOfIterations;k+=1)
                {
                    for(int j=0;j < NumberOfIterations;j+=1)
                    {
                        ColorIntensityInRadius+=tex2D(
                                                      _MainTex, 
                                                      i.uvs.xy+float2
                                                                   (
                                                                        (k-NumberOfIterations/2)*TX_x,
                                                                        (j-NumberOfIterations/2)*TX_y
                                                                   )
                                                     ).r;
                    }
                }

                //this value will be pretty high, so we won't see a blur. let's lower it for now.
                ColorIntensityInRadius*=0.005;
 
                //output some intensity of teal
                half4 color= tex2D(_SceneTex,i.uvs.xy)+ColorIntensityInRadius*half4(0,1,1,1);

                //don't want our teal outline to be white in cases where there's too much red
                color.r=max(tex2D(_SceneTex,i.uvs.xy).r-ColorIntensityInRadius,0);
                return color;
            }
             
            ENDCG
 
        }
        //end pass        
    }
    //end subshader
}
//end shader

 

 

 

 

 

 

여기까지는 평균내어 블러 처리 한것인데 하단 부터는 가우시안 블러를 적용하여 블러 처리 한 부분으로 변경한 것이다

 


This is almost where we want it, but we do have one more step! There are some problems with this outline right now. You might notice it looks a little off, and that the outline doesn't contour the corners nicely.

There's also the issue of performance - at 19x19 samples per pixel for the outline, this isn't a big issue. But what if we want a 40 pixel outline? 40x40 samples would be 1600 samples per pixel!

We're going to solve both of these issues with a gaussian blur.
In a Gaussian blur, a kernel(a weight table) is made and colors are added equal to the nearby colors times the weight.


Gaussian blurs look nice and natural, and they have a performance benefit.
If we start with a 1-dimensional Gaussian kernel, we can blur everything horizontally,


and then blur everything vertically after, to get the same result.


This means our 40 width outline will only have 80 samples per pixel.

I found some code online that will generate a Gaussian kernel. It's a bit beyond my understanding, but just know that this is computationally expensive, and shouldn't be done often. I'm running it on the script's start method.

 

//http://haishibai.blogspot.com/2009/09/image-processing-c-tutorial-4-gaussian.html
public static class GaussianKernel
{
	public static float[] Calculate(double sigma, int size)
	{
		float[] ret = new float[size];
		double sum = 0;
		int half = size / 2;
		for (int i = 0; i < size; i++)
		{
			ret[i] = (float) (1 / (Math.Sqrt(2 * Math.PI) * sigma) * Math.Exp(-(i - half) * (i - half) / (2 * sigma * sigma)));
			sum += ret[i];
		}
		return ret;
	}
}

 

 

 

The sigma is an arbitrary number, indicating the strength of the kernel and the blur.

The size is to stop generating tiny numbers in an infinite kernel. Technically, Gaussian kernels would have no zeroes, so we want to cut it off once it isn't noticeable. I recommend the width be 4 times the sigma.

We calculate the kernel, and pass it to the shader. I have it set to a fixed size, but you could pass a kernel texture instead to handle varying widths.

using System;
using UnityEngine;
public class outline : MonoBehaviour
{
	public Shader DrawAsSolidColor;
	public Shader Outline;
	Material _outlineMaterial;
	Camera TempCam;
	float[] kernel;
	void Start()
	{
		_outlineMaterial = new Material(Outline);
		TempCam = new GameObject().AddComponent<Camera>();

		kernel = GaussianKernel.Calculate(5, 21);
	}

	void OnRenderImage(RenderTexture src, RenderTexture dst)
	{
		TempCam.CopyFrom(Camera.current);
		TempCam.backgroundColor = Color.black;
		TempCam.clearFlags = CameraClearFlags.Color;

		TempCam.cullingMask = 1 << LayerMask.NameToLayer("Outline");

		var rt = RenderTexture.GetTemporary(src.width, src.height, 0, RenderTextureFormat.R8);
		TempCam.targetTexture = rt;

		TempCam.RenderWithShader(DrawAsSolidColor, "");

		_outlineMaterial.SetFloatArray("kernel", kernel);
		_outlineMaterial.SetInt("_kernelWidth", kernel.Length);
		_outlineMaterial.SetTexture("_SceneTex", src);

		//No need for more than 1 sample, which also makes the mask a little bigger than it should be.
		rt.filterMode = FilterMode.Point;

		Graphics.Blit(rt, dst, _outlineMaterial);
		TempCam.targetTexture = src;
		RenderTexture.ReleaseTemporary(rt);
	}
}

 

 

We make a pass with the kernel, sampling every pixel and adding our pixel's value by the kernel times the sampled value. Call GrabPass to grab the resulting texture, and then make a second pass. The second pass does the vertical blur.

Shader "Custom/Post Outline"
{
    Properties
    {
        _MainTex("Main Texture",2D)="black"{}
        _SceneTex("Scene Texture",2D)="black"{}
        _kernel("Gauss Kernel",Vector)=(0,0,0,0)
        _kernelWidth("Gauss Kernel",Float)=1
    }
    SubShader 
    {
        Pass 
        {
            CGPROGRAM
            float kernel[21];
            float _kernelWidth;
            sampler2D _MainTex;    
            sampler2D _SceneTex;
            float2 _MainTex_TexelSize;
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct v2f 
            {
                float4 pos : SV_POSITION;
                float2 uvs : TEXCOORD0;
            };
            v2f vert (appdata_base v) 
            {
                v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uvs = o.pos.xy / 2 + 0.5;
                return o;
            }
                          
            float4 frag(v2f i) : COLOR 
            {
                int NumberOfIterations=_kernelWidth;
				
                float TX_x=_MainTex_TexelSize.x;
                float TX_y=_MainTex_TexelSize.y;
                float ColorIntensityInRadius=0;

                //for every iteration we need to do horizontally
                for(int k=0;k<NumberOfIterations;k+=1)
                {
                        ColorIntensityInRadius+=kernel[k]*tex2D(
                                                      _MainTex, 
                                                      float2
                                                                   (
                                                                        i.uvs.x+(k-NumberOfIterations/2)*TX_x,
                                                                        i.uvs.y
                                                                   )
                                                     ).r;
                }
                return ColorIntensityInRadius;
            }
            ENDCG
 
        }
        //end pass

        //retrieve the texture rendered by the last pass, and give it to the next pass as _GrabTexture
        GrabPass{}
		
		//this pass is mostly a copy of the above one.
        Pass 
        {
            CGPROGRAM
            float kernel[21];
            float _kernelWidth;       
            sampler2D _MainTex;    
            sampler2D _SceneTex;
			
            sampler2D _GrabTexture;
            float2 _GrabTexture_TexelSize;

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct v2f 
            {
                float4 pos : SV_POSITION;
                float2 uvs : TEXCOORD0;
            };
            v2f vert (appdata_base v) 
            {
                v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
                o.uvs = o.pos.xy / 2 + 0.5;
                 
                return o;
            }
            float4 frag(v2f i) : COLOR 
            {
                float TX_x=_GrabTexture_TexelSize.x;
                float TX_y=_GrabTexture_TexelSize.y;

                //if something already exists underneath the fragment, draw the scene instead.
                if(tex2D(_MainTex,i.uvs.xy).r>0)
                {
                    return tex2D(_SceneTex,i.uvs.xy);
                }

                int NumberOfIterations=_kernelWidth;
                float4 ColorIntensityInRadius=0;

                for(int k=0;k < NumberOfIterations;k+=1)
                {
                        ColorIntensityInRadius+=kernel[k]*tex2D(
                                                      _GrabTexture,
                                                      float2(i.uvs.x,
                                                             1-i.uvs.y+(k-NumberOfIterations/2)*TX_y)
                                                     );
                }

                //output the scene's color, plus our outline strength in teal.
                half4 color= tex2D(_SceneTex,i.uvs.xy)+ColorIntensityInRadius*half4(0,1,1,1);
                return color;
            }
             
            ENDCG
 
        }
        //end pass        
    }
    //end subshader
}
//end shader

 

 

The finished effect!

 

결과를 보면 좀더 밖으로 빠질 수록 자연스럽게 블러처리가 된 것을 볼 수 있다

 

Where to go from here


Try adding multiple colors.
You could also sample the added color from another texture to make a neat pattern.
Regenerate the kernel when the resolution changes to get an effect that scales up.

Help!

My outline looks blocky.
Most likely, your sigma is too high and your width too low, so the kernel is cut off earlier than it should be.
Or, your kernel isn't being generated or used correctly.

Performance problems/crashes
Are you using RenderTexture.GetTemporary() and RenderTexture.ReleaseTemporary()? Unity doesn't release normal RTs for some reason, so you need to use the temporary methods.
Check the profiler to see what's up.

Something is upside-down/too big/in the corner of the screen
Unity has inconsistencies and so does OpenGL. There is a pragma, #if UNITY_UV_STARTS_AT_TOP, that can be used.

Something isn't working.
Use the frame debugger to see what's happening to your graphics. Otherwise, check for any errors.

 

 

 

ref : https://willweissman.com/unity-outlines

http://haishibai.blogspot.com/2009/09/image-processing-c-tutorial-4-gaussian.html

https://gamedev.stackexchange.com/questions/144702/how-can-i-make-a-soft-outline-shader

https://docs.unity3d.com/kr/530/ScriptReference/Graphics.Blit.html (Graphics.Blit)

https://docs.unity3d.com/kr/2021.3/Manual/SL-BuiltinFunctions.html

 

 

 

반응형
반응형



라이트맵핑을 사용해 실감나는 조명 효과를 표현할 수 있지만 움직이는 개체는 적용할 수 없다.

라이트맵핑은 정적인 객체(Static Object)에만 영향을 미치기 때문이다.

그렇다고 실시간 조명을 이용해 움직이는 객체의 조명 효과를 부여하기에는 불가능하다.


이점을 개선하기 위해 유니티에서는 라이트 프로브(Light Probe) 기법을 제공한다.

스테이지의 라이트가 있는 곳 주변에 라이트 프로브를 배치하고 라이트 맵을 베이킹할 때

해당 라이트 프로브에 주변부의 광원 데이터를 미리 저장해 둔다.

라이트 프로브에 저장된 광원 데이터는 실행 시 근처를 지나가는 객체에 광원 데이터를 전달하여 

해당 물체의 색상과 보간(Interpolite)시켜 마치 실시간 조명과 같은 효과를 표현하는 방식이다.


<절대강자 유니티4/이재현> 발췌


http://bililic.com/220759932463








 프로브 프록시 볼륨

http://blog.naver.com/blue9954/220819325189




http://www.devblog.kr/r/8y0gFPAvJ2Z8y1vfYWHFXs9XobwcKq6QkAP2lrVXIYx60o


대마왕 님의 블로그에서 퍼온 자료입니다. 자세한 내용은 링크를 확인해 주세요.


기존의 라이트 프로브의 단점이었던 커다란 오브젝트의 라이트 프로브 문제점이 해결되었습니다. 예를 들어 대형 몬스터를 표현할때 기존의 라이트 프로브로는 몬스터 오브젝트 전체가 하나의 라이트 프로브만 영향받는 문제가 있어서 주변의 라이팅과 어울리지 않았지만 이번에 들어간 라이트 프로브 프록시 볼륨을 이용하면 훨씬 리얼한 라이팅 효과를 얻을수 있습니다.


반응형
반응형



출처 : http://chulin28ho.tistory.com/256




 tex2dlod (sampler, float4(UV, 0,miplevel))

 

tex2Dlod 는 밉맵의 레벨을 불러온다. 일부러 텍스쳐를 흐리게 할 때 유용함.

단 이걸 사용하면 거리에 따른 밉맵 조절이 안되는 문제가 있다.

miplevel은 0 부터 사용한다. 숫자가 늘어날수록 밉 레벨이 늘어난다.

여기서의 miplevel은 2.5라고 넣는다면 2와 3 사이를 보간해 준다

#pragma target 3.0 필요

 

http://kblog.popekim.com/2011/04/tex2dlod.html

 

 

 

tex2dbias (sampler, float4(UV, 0,miplevel))

 

tex2Dbias 는 밉맵의 레벨을 불러온다. 일부러 텍스쳐를 흐리게 할 때 유용함.

음.. 여기서의 miplevel은 2.5 라고 넣는다면 지금의 밉레벨 + 2.5 ... 인건가

결과물을 봐도 bias가 더 나쁘고, 3.0도 요구하지 않는다.

 

즉 lod는 밉맵레벨을 고정하는거고

bias는 지금 레벨에 원하는 레벨을 더해줘서 결과물을 내보낸다..

하이브리드한테까지 물어봐서 결과냄. 도무지 이해가 안가길래..

 

 

 

 

 

참고 스레드

http://forum.unity3d.com/threads/tex2dlod-vs-tex2dbias.249140/

 

http://jyblues.blogspot.kr/2011/12/direct3d11-intrinsic-functions.html

 

tex2Dbias(s,t): t.w에 의해 mip map level을 정한 후 샘플러 s를 이용 2D 텍스쳐의 픽셀 색깔 정보를 얻는다.

tex2Dlod(s,t): t.w에 의해 mip map level을 정한 후 샘플러 s를 이용 2D 텍스쳐의 픽셀 정보를 얻는다. t.w에 의해 LOD가
              어느단계에서 변할지를 결정한다.







출처 : http://chulin28ho.tistory.com/258



둘은 상당히 비슷한 기능을 하죠. mipmap 제어라는...

그치만 공식의 작동법은 미묘하게 달라요.

lod가 shader3.0을 요구한다는 것도 다르고요...

 

프로그래머들에게 물어보면 공식에 대한 차이만 말해주니까요

자세한 느낌을 보기 위해 직접 테스트를 해 봤습니다.

 

 

 

예제를 만들어 봤습니다. 둘이 똑같아 보이죠?

근데 제 눈에는 살짝 오른쪽 텍스쳐가 추가 샘플링 된 것처럼 보입니다.

 

그래서 정말 그런가 테스트

 

 

 

 

 

오호라 과연 정말입니다.

tex2Dlod는 고정된 mipmap을 유지하지만, bias 는 이름값대로 추가로 바이어스를 걸어주고 있는 느낌입니다.

 

그럼 이번에는 값에 따른 변화를 테스트 하기 위해 다시 근접시킵니다.

 

 

 

음 겜브리오때는 밉맵을 수동 설정 가능했는데. 유니티는 방법이 없을까낭...

뭐 투덜대도 할 수 없으니까 이번에는 값을 비교해 보죠

 

지금 두 그림이 똑같아 보입니다만... 오른쪽이 밉맵 1 상태라고 보여집니다. 한 단계 낮은 이미지요.

 

어쨌거나 일단 비교.

 

 

lod/bias 값 1

 

 

lod/bias 값 1.5

 

 

lod/bias 값 2

 

 

lod/bias 값 2.5

 

 

lod/bias 값 3

 

 

lod/bias 값 3.5

 

 

lod/bias 값 4

 

 

lod/bias 값 4.5

 

 

lod/bias 값 5

 

 

lod/bias 값 5.5

 

 

 

lod/bias 값 6

 

 

음... 일단 오른쪽 bias의 값이 더 낮아 보입니다.

그 이유는... 현재 보이는 거리 때문에 밉맵이 한 단계 더 먹은 상태라서 그렇습니다.

그에 반해 tex2Dlod는 거리고 뭐고 상관 안하고 mipmap 값이 강제지정됩니다. 호옹이.

이런 차이로군요.

 

 

그래서 정말 그런가 추가 테스트

 

 

 

 

다시 6단계에서부터 추가 테스트. 이번에는 필터링을 개뿌연 Box 필터링에서 그나마 날카로운  Kaiser 필터링으로 바꿔 보았습니다. 카메라를 좀 근접시키니 두 이미지가 상당히 비슷하네요 그래도 lod가 더 선명해 보이는게, 1단계 앞인 느낌입니다.

 

 

거리를 멀게 하자 오호라. 역시 bias는 거리에 따라 mipmap이 더 먹습니다.

 

그렇다는것은...

 

 

 

 

거리만 가깝다면 bias가 더 선명하다는 말도 된단 말입니다. lod는 고정이니까. 오호호라...

 

즉  lod는 무조건 고정시키는 거고 bias는 말그대로 기존 값에 bias 시킨다는 말이겠습니다.

 

이 기능은 주로 싸구려 블러 효과로 사용됩니다만, 값을 반대로 처리하면 싸구려 샤픈 효과도 가능하겠죠.

반응형

'그래픽스(Graphics) > Shader' 카테고리의 다른 글

Object Outlines in Unity  (0) 2022.08.21
라이트 프로브(Light Probe)  (0) 2016.11.25
radiance, radiosity,irradiance  (0) 2013.07.25
SSS(Subsurface scattering) 정리  (0) 2013.07.14
Subsurface scattering  (0) 2013.07.14
반응형
http://cafe.naver.com/houdinilife/56




radiance : 광원에서 발광radiance하는 양이나 세기를 말하고
radiosity : 광원으로 부터 받은 빛을 surface의 단위면적당 얼마나 확산시키는것을 말하고...
irradiance : 광원으로부터 단위면적당 얼마만큼의 빛을 surface가 받았냐이다...



 radiance_equation.ppt



http://www.google.co.kr/url?sa=t&rct=j&q=&esrc=s&source=web&cd=3&ved=0CF8QFjAC&url=http%3A%2F%2Fwww.cs.ucl.ac.uk%2Fstaff%2Fucacajs%2Fbook_tmp%2FCGVE%2Fslides%2Fradiance_equation.ppt&ei=2Be1T4CaBYqWiQeFuLCOAw&usg=AFQjCNFAJz7qrgGKvuK6L1aDV40lNTfyfw&sig2=2L21l7kiSO7HynKZvFj--g

반응형
반응형




우선 시간이 지나 나도 까먹을 가능성이 짙기때문에 아래 내용중 중요한 내용을 써놓으면...




이미지 출처 : 위키(en)






대략  SSS(Subsurface scattering)은/는  빛이 사람표면에 반사될때 6%를 제외한 94% 가 표면에서 표면으로 빛이 반사되는것에 대한 거리를 구해야 이 알고리즘을

구현 할 수 있다고 하는것인데, 표면으로 흩어지는것들의 거리를 어케 찾는 것이 관건이는 것임다!


아래 설명은 뭐 여러가지 방법이 있겠지만 그중에서도 depth map 을 이용한 방법을 설명하고

왼쪽 상단에 보이는게 depth map 이고 depth map 을 생성하는 기준점이 light position 이라 한다면 


이것들의 거리를 텍스쳐의 offset 으로 구할 수 있다는것!!! 깊이는 반사지점 주위의 흩어진곳에서의 깊이를 대략 비교해

이게 같은 표면에 있는거냐 등을 조사하면 될것 같고, 대략 이것도 샘플링/kernel 로 들어가면 될듯

이것으로 보아 이 방식을 사용할 경우 deferred 와도 잘 연동될것으로 보이고, 실시간으로도 처리 가능할것으로 보이네요


으하~
 


이쯤되면 대략 예상되는것은 샘플링 개수와 어떠한 랜덤한 분포를 쓰느냐에 따라 달라질듯....





참고 사이트 : 

http://en.wikipedia.org/wiki/Subsurface_scattering   추천!!!!

http://chulin28ho.egloos.com/5591833  SSS에 대한것이 일단 무엇인지 보고싶다면 이곳으로

http://blog.naver.com/gogator100/80001535673







반응형
반응형
구현해보려고 돌아다니다 봤는데 이것만 봐도 도움이 되는듯
좀 더 디테일한 자료를 위해 구굴링 으~~~~



출처 : http://en.wikipedia.org/wiki/Subsurface_scattering


Subsurface scattering

From Wikipedia, the free encyclopedia
  (Redirected from Sub Surface Scattering)



Direct surface scattering (left), plus subsurface scattering (middle), create the final image on the right.

Example of Subsurface scattering made in Blender software.

Subsurface scattering (or SSS) is a mechanism of light transport in which light penetrates the surface of a translucent object, is scatteredby interacting with the material, and exits the surface at a different point. The light will generally penetrate the surface and be reflected a number of times at irregular angles inside the material, before passing back out of the material at an angle other than the angle it would have if it had been reflected directly off the surface. Subsurface scattering is important in 3D computer graphics, being necessary for the realistic rendering of materials such as marble, skin, and milk.

Contents

  [hide

Rendering Techniques[edit]

Most materials used in real-time computer graphics today only account for the interaction of light at the surface of an object. In reality, many materials are slightly translucent: light enters the surface; is absorbed, scattered and re-emitted — potentially at a different point. Skin is a good case in point; only about 6% of reflectance is direct, 94% is from subsurface scattering.[1] An inherent property of semitransparent materials is absorption. The further through the material light travels, the greater the proportion absorbed. To simulate this effect, a measure of the distance the light has traveled through the material must be obtained.

Depth Map based SSS[edit]


Depth estimation using depth maps

One method of estimating this distance is to use depth maps [2] , in a manner similar to shadow mapping. The scene is rendered from the light's point of view into a depth map, so that the distance to the nearest surface is stored. The depth map is then projected onto it using standard projective texture mapping and the scene re-rendered. In this pass, when shading a given point, the distance from the light at thepoint the ray entered the surface can be obtained by a simple texture lookup. By subtracting this value from the point the ray exited the objectwe can gather an estimate of the distance the light has traveled through the object.

The measure of distance obtained by this method can be used in several ways. One such way is to use it to index directly into an artist created 1D texture that falls off exponentially with distance. This approach, combined with other more traditional lighting models, allows the creation of different materials such as marble, jade andwax.

Potentially, problems can arise if models are not convex, but depth peeling [3] can be used to avoid the issue. Similarly, depth peeling can be used to account for varying densities beneath the surface, such as bone or muscle, to give a more accurate scattering model.

As can be seen in the image of the wax head to the right, light isn’t diffused when passing through object using this technique; back features are clearly shown. One solution to this is to take multiple samples at different points on surface of the depth map. Alternatively, a different approach to approximation can be used, known as texture-space diffusion.

Texture Space Diffusion[edit]

As noted at the start of the section, one of the more obvious effects of subsurface scattering is a general blurring of the diffuse lighting. Rather than arbitrarily modifying the diffuse function, diffusion can be more accurately modeled by simulating it in texture space. This technique was pioneered in rendering faces in The Matrix Reloaded,[4] but has recently fallen into the realm of real-time techniques.

The method unwraps the mesh of an object using a vertex shader, first calculating the lighting based on the original vertex coordinates. The vertices are then remapped using the UV texture coordinates as the screenposition of the vertex, suitable transformed from the [0, 1] range of texture coordinates to the [-1, 1] range of normalized device coordinates. By lighting the unwrapped mesh in this manner, we obtain a 2D image representing the lighting on the object, which can then be processed and reapplied to the model as a light map. To simulate diffusion, the light map texture can simply be blurred. Rendering the lighting to a lower-resolution texture in itself provides a certain amount of blurring. The amount of blurring required to accurately model subsurface scattering in skin is still under active research, but performing only a single blur poorly models the true effects.[5] To emulate the wavelength dependent nature of diffusion, the samples used during the (Gaussian) blur can be weighted by channel. This is somewhat of an artistic process. For human skin, the broadest scattering is in red, then green, and blue has very little scattering.

A major benefit of this method is its independence of screen resolution; shading is performed only once per texel in the texture map, rather than for every pixel on the object. An obvious requirement is thus that the object have a good UV mapping, in that each point on the texture must map to only one point of the object. Additionally, the use of texture space diffusion provides one of the several factors that contribute to soft shadows, alleviating one cause of the realism deficiency of shadow mapping.


반응형
반응형

http://en.wikipedia.org/wiki/ARB_assembly_language


ARB Assembly Instructions [edit]

ARB assembly supports the following instructions:

  • ABS - absolute value
  • ADD - add
  • ARL - address register load
  • DP3 - 3-component dot product
  • DP4 - 4-component dot product
  • DPH - homogeneous dot product
  • DST - distance vector
  • EX2 - exponential base 2
  • EXP - exponential base 2 (approximate)
  • FLR - floor
  • FRC - fraction
  • LG2 - logarithm base 2
  • LIT - compute light coefficients
  • LOG - logarithm base 2 (approximate)
  • MAD - multiply and add
  • MAX - maximum
  • MIN - minimum
  • MOV - move
  • MUL - multiply
  • POW - exponentiate
  • RCP - reciprocal
  • RSQ - reciprocal square root
  • SGE - set on greater than or equal
  • SLT - set on less than
  • SUB - subtract
  • SWZ - extended swizzle
  • TEX - Texture lookup
  • XPD - cross product

This is only a partial list of assembly instructions ; a reference can be found here : Shader Assembly Language (ARB/NV) Quick Reference Guide for OpenGL.

반응형
반응형

http://www.gamedev.net/topic/478820-derivative-instruction-details-ddx-ddy-or-dfdx-dfdy-etc/
Hi All, From my understanding the derivative instructions ddx,ddy,fwidth etc. Use 2x2 blocks of pixels to compute the derivative. Can someone please explain: 1.) What happens on triangle boundaries where a triangle only intersects one or 2 pixels of the 2x2 region 2.) The exact formula for the derivatives for each pixel in the 2x2 block. For example, given the block: a b c d the ddx derivative for pixel a/b/c/d could be ((b-a)+(d-c))/2 or it could be b-a for a/b and d-c for c/d. I have scoured the net and haven't found a good reference for this. I have found that if I draw a box and take the derivative of a complicated calculated value that *sometimes* i get artifacts on the border of the box's polygons. I imagine this is due to how it is computed on polygon boundaries. Any help would be appreciated. Cheers! Eric [Edited by - WizardOfOzzz on January 9, 2008 8:16:57 PM] 
    Sponsor:

    #2AndyTX   Members   -  Reputation: 798

    Like
    0Likes1Likes
    Like

    Posted 09 January 2008 - 02:42 PM

    Quote:
    Original post by WizardOfOzzz
    From my understanding the derivative instructions ddx,ddy,fwidth etc. Use 2x2 blocks of pixels to compute the derivative.

    That's correct.

    Quote:
    Original post by WizardOfOzzz
    1.) What happens on triangle boundaries where a triangle only intersects one or 2 pixels of the 2x2 region

    The shader is evaluated for all of the pixels in the 2x2 "quad", even if some of them fall outside of the triangle.

    Quote:
    Original post by WizardOfOzzz
    2.) The exact formula for the derivatives for each pixel in the 2x2 block. For example, given the block:
    a b
    c d

    IIRC the ddx(a) and ddx(b) will be (b-a), ddy(a) and ddy© will be (c-a) and so on.

    Quote:
    Original post by WizardOfOzzz
    I have found that if I draw a box and take the derivative of a complicated calculated value that *sometimes* i get artifacts on the border of the box's polygons.

    This problem can indeed occur if you're computing some function that is undefined or otherwise not well-behaved outside of the normal triangle interpolant range. Similar problems can occur with multi-sampling, but there you can often solve them using "centroid" interpolation. I don't recall exactly how derivatives interact with centroid interpolation but I wouldn't be surprised if it is implementation-dependent. 


    반응형

    '그래픽스(Graphics) > Shader' 카테고리의 다른 글

    Subsurface scattering  (0) 2013.07.14
    셰이더에서 사용되는 어쎔명령들  (0) 2013.06.08
    셰이더 변수,함수들  (0) 2013.06.03
    웹에서 셰이더 보기 웹 GLSL  (0) 2013.06.03
    SSAO( Screen Space Ambient Occlusion )  (0) 2013.05.20
    반응형






    복사

    http://pusna25.blog.me/100187965288


    셰이더의 종류:

    • HLSL: DirectX에서 사용하는 셰이더 언어.
    • GLSL: OpenGl Shader Language의 약자로, OpenGL에서 지원하는 셰이더 언어. HLSL과 문법이 조금 다름.
    • CgFX: 엔비디아에서 지원하는 셰이더 언어로 HLSL과 거의 동일함.

     

    HLSL(High-Level Shading Languae):

    • 고수준 셰이딩 언어.
    • 버텍스와 픽셀 셰이더는 고정 기능 파이프라인을 대체하는 작은 커스텀 프로그램.
    • 그래픽 카드의 GPU에서 실행된다.
    • 간단히 말해서, 화면에 존재하는 각 픽셀의 위치와 색상을 계산하는 함수.

     

    정점 셰이더(vertex shader):

    • 3D 물체를 구성하는 정점들의 위치를 화면 좌표로 변환하는 것.

     

    픽셀 셰이더(pixel shader):

    • 화면에 출력할 최종 색상을 계산하는 것.

    간략하게 만든 3D 파이프라인:

    • 정점 데이터(위치 등)->정점 셰이더(정점 위치)->레스터라이저(픽셀 위치)->픽셀 셰이더(색상)->화면.

     

     HLSL 셰이더의 컴파일:

    • 상수 테이블:
      • 모든 셰이더는 내부의 변수를 보관하기 위한 상수 테이블을 가지고 있으며, D3DX 라이브러리는 애플리케이션이 셰이더의 상수 테이블에 접근할 수 있도록 하는 ID3DXConstantTalbe 인터페이스를 제공한다.
      • 즉, 이 인터페이스를 이용하면 애플리케이션(VS) 코드에서 셰이더 소스 코드 내의 변수 값을지정할 수 있다.
    • 상수로의 핸들 얻기:
      • 애플리케이션 코드에서 셰이더의 특정 변수를 지정하기 위해서는 먼저 변수를 참조 하는 방법이 필요하며, 이를 위해 D3DXHANDLE이 이용된다.
      • ID3DXConstantTalbe::GetConstantByName()이란 함수로 핸들을 얻는다.
    • 상수 값 설정하기:
      • 셰이더 코드 내의 변수를 연결하는 D3DXHANDLE을 얻은 다음에는 ID3DXConstantTalbe::SetXXX 메서드를 이용해 애플리케이션에서 셰이더의 변수 값을 지정할 수 있다.
      • 여기에서 XXX는 지정하려는 지정하려는 변수의 형을 의미하는 형 이름으로 대체된다. ex) SetFloat, SetBool 등...

     

    SetXXX() 함수:

    • SetBool():
      • 불 값을 지정.
      • bool b = true;
      • SetBool(Device, handle, b);
    • SetBoolArray():
      • 불 배열의 값을 지정.
      • bool b[3] = { true, false, true };
      • SetBoolArray(Device, handle, b, 3);
    • SetFloat():
      • 부동 소수  값을 지정.
      • float f = 3.14f;
      • SetFloat(Device, handle, f);
    • SetFloatArray():
      • 부동 소수 배열의 값을 지정.
      • float f[2] = { 1.0f, 2.0f };
      • SetFloatArray(Device, handle, f, 2);
    • SetInt():
      • 정수 값을 지정.
      • int x = 4;
      • SetInt(Device, handle, x);
    • SetIntArray():
      • 정수 배열의 값을 지정.
      • int x[4] = { 1,2,3,4 };
      • SetIntArray(Device, handle, x, 4);
    • SetMatrix():
      • 4x4 행렬의 값을 지정.
      • D3DXMATRIX M(...);
      • SetMatrix(Device, handle, &M);
    • SetMatrixArray():
      • 4x4 행렬 배열의 값을 지정.
      • D3DXMATRIX M[4];
      • ... 행렬 초기화.
      • SetMatrixArray(Device, handle, M, 4);
    • SetMatrixPointerArray():
      • 4x4 행렬 포인터의 배열 값을 지정.
      • D3DXMATRIX*  M(...);
      • ... 행렬 포인터를 할당하고 초기화.
      • SetMatrixPointerArray(Device, handle, M,  4);
    • SetMatrixTranspose():
      • 전치 4x4 행렬의 값을 지정.
      • D3DXMATRIX M(...);
      • D3DXMatrixTranspose(&M, &M);
      • SetMatrixTranspose(Device, handle, &M);
    • SetMatrixTransposeArray():
      • 전치 4x4 행렬의 배열 값을 지정.
      • D3DXMATRIX M[4];
      • ... 행렬을 초기화하고 전치.
      • SetMatrixTransposeArray(Device, handle, M, 4);
    • SetMatrixTransposePointerArray():
      • 4x4 전치 행렬의 포인터 배열 값을 지정.
      • D3DXMATRIX* M[4];
      • ... 행렬 포인터를 할당, 초기화하고 전치.
      • SetMatrixTransposePointerArray(Device, handle, M, 4);
    • SetVector():
      • D3DXVECTOR4 형 변수의 값을 지정.
      • D3DXVECTOR4  v(1.0f, 2.0f, 3.0f, 4.0f);
      • SetVector(Device, handle, &v);
    • SetVectorArray():
      • 벡터 배열의 변수 값을 지정.
      • D3DXVECTOR4 v[3];
      • ... 벡터를 초기화.
      • SetVectorArray(Device, handle, v, 3);
    • SetValue():
      • 구조체와 같이 임의의 크기를 가진 형의 값을 지정.
      • D3DXMATRIX M(...);
      • SetValue(Device, handle, (void*)&M, sizeof(M));
      • 위에서는 D3DXMATRIX 값을 지정하는 데 사용함.
    • SetDefaults():
      • 상수의 디폴트 값 지정.
      • SetDefaults(Device);

    변수 형:

    • 스칼라 형:
      • bool: True, False 값. HLSL은 True와 False 키워드를 제공.
      • int: 32비트 부호 정수.
      • half: 16비트 부동 소수점 수.
      • float: 32비트 부동 소수점 수.
      • double: 64비트 부동 소수점 수.
    • 벡터 형:
      • vector: float 성분을 가지는 4D 벡터.
      • vecot<T, n>: n-차원의 벡터. 각각의 성분은 T형의 스칼라이다. 차원 n은 반드시 1~4내에 있어야 한다.
        • ex) vector<double, 2> vec2;
      • float2 vec2;   // 2D 벡터.
      • float3 vec3;   // 3D 벡터.
      • float4 vec4;   // 4D 벡터. 처럼 미리 정의된 형을 이용할 수 있다.
    • 행렬 형:
      • matrix: 4x4 행렬. 각 항목은 float이다.
      • matirx<T, M, n>: m*n행렬. T는 스칼라형.
    • 배열:
      • float M[4][4];
      • half p[4];
      • vector v[12];
    • 구조체:
      • C++와 다르게 HLSL 구조체는 멤버함수를 가질 수 없다는 거 말고 동일함.
    • typedef 키워드:
      • C++의 typedef와 완전 동일 함.

     

    변수 접두어:

    • static:
      • 변수 선언에 static 키워드를 이용하면 셰이더 외부에서 변수에 접근할 수 없음을 지정.
      • 다른 말로, 셰이더 내의 지역 변수로 이용.
      • static 키워드를 붙이면 함수가 처음 실행될 때 한번만 초기화되며 나머지 함수가 호출되는 동안 값을 유지한다.
      • 초기화하지 않으면 0으로 초기화된다.
    • uniform:
      • 변수가 셰이더 외부, 예를 들어 C++ 애플리케이션에서 초기화되어 셰이더에 입력됨을 의미.
    • extern:
      • 셰이더 외부에서 변수에 접근할 수 있음을 의미.
      • 전역 변수만이 extern 키워들 가질 수 있다.
      • static이 아닌 전역 변수는 디폴트로 extern이다.
    • shared:
      • 이펙트 프레임웍에게 변수가 다수의 효과에 공유될 것임을 알린다.
      • 전역 변수만이 shared 키워드를 가진다.
    • volatile:
      • 이펙트 프레임웍에게 변수가 자주 수정될 것임을 알린다.
      • 전역 변수만이 volatile 키워드를 가진다.
    • const:
      • C++과 동일함.

     


    반응형
    반응형

    http://glsl.heroku.com/




    반응형
    반응형




    http://kyruie.tistory.com/31




    원문 : http://wiki.gamedev.net/index.php/D3DBook:Screen_Space_Ambient_Occlusion


    SSAO( Screen Space Ambient Occlusion )



    컴퓨터로 생성된 이미지에 사실성을 향상시키기 위한 방법들은 매우 많다. 그런 방법들 중 하나가 물체의 라이팅 방정식을 이용해 값을 구할때 그림자 효과를 계산하는 방법이다. 실시간 컴퓨터 그래픽에서 유용한 그림자 기법들은 매우 많으며, 그러한 각각 기법들은 장단점을 모두 보인다. 일반적으로, 그림자 기법들은 그림자 품질과 실행 효율 사이의 균형을 취하려 노력한다.

    그런 기법들중 하나가 바로 SSAOScreen Space Ambient Occlusion라고 불리는 기법이다. 이 기법은 Martin Mittring이 2007 SIGGRAPH 컨퍼런스에서 발표한 논문 "Finding Next Gen"에서 처음 논의되었다. 이 알고리즘의 기본 컨셉은  
     전역 조명 과정Ambient lighting term을 장면의 한 점이 얼마나 차폐되는지를 구하는 기반으로 바꾸는 것이다. 이 방법이 AO의 컨셉을 처음으로 이용하는 기법은 아니지만, 이후의 장chapter에서 보게 되겠찌만 SSAO는 꽤나 현명한 추정과단순함으로 매우 높은 성능을 유지하면서도 만족할만한 결과를 보여준다.



    그림 1. SSAO를 이용한 샘플 렌더링 화면

    이 번 장에서는, SSAO의 배경이 되는 이론을 살펴보고, Direct3d 10 파이프라인을 이용하여 이 기법을 구현하는 방법을 제공하며, 성능과 품질의 향상을 위해 구현 단계에서 사용할 수 있는 파라미터들에 대하여 논의할 것이다. 마지막으로, 주어진 구현을 이용한 데모에 대하여 논의하여 이를 통해 알고리즘의 성능에 대해서 몇 가지 사항을 알리고자 한다.

    알고리즘 이론

    이제 우리는 SSAO의 유래에 관한 배경지식에 대해서 살펴보았으므로, 기법에 대하여 더욱 자세하게 살펴볼 준비가 되었다. SSAO는 단 하나의 단순한 명제를 사용함으로써 이전의 기법들과는 다른점을 보인다: SSAO는 이전에 레스터화되기 이전의 장면 정보를 사용하는 대신 현재 장면에서 주어진 점의 차폐의 양을 구하는데 오직 깊이 버퍼만을 이용한다. 이를 위해 깊이 버퍼를 직접적으로 사용하는 방법이나 깊이 정보를 보관하기 위해 특별한 렌더타겟을 생성하는 방법 모두 가능하다. 깊이 정보는 주어진 점의 주변점을 구하는 즉시 세밀하게 조사하여 그 점이 얼마나 차폐되었는지 계산하는데 사용한다. 차폐 인수occlution factor은 한지점에 얼마만큼의 전역광을 적용할것인지, 그 양을 조절하는데 사용한다.

    SSAO 알고리즘에서 사용하는 차폐 인수을 생성하는 방법은 논리적으로 두 단계 과정으로 이루어져 있다. 먼저 우리는 장면의 현재 시점을 기준으로 깊이 텍스처를 생성해야하고, 이 깊이 텍스처를 이용하여 현 장면의 최종 화면에서 각 픽셀의 차폐 정도를 결정한다. 이 과정은 아래의 그림 2에서 볼 수 있다.

    그림2. SSAO 알고리즘의 개요
    깊이 버퍼만을 사용하는 이 단순함은 성능과 품질면에서 몇가지 효과가 있다. 장면 정보가 렌더타겟으로 레스터화 하는 경우, 장면을 나타내는데 사용하는 정보가 3차원에서 2차원으로 줄어들게 된다. 데이터 축소는 매우 효율적인 명령을 제공하는 표준 GPU 파이프라인 하드웨어를 통해 수행된다. 또한 장면의 전체 차원을 줄임으로, 2D 장면 표현을 장면의 가시 영역으로 한정하여 이 프레임에 보이지 않는 물체에 대한 불필요한 연산을 제거할 수 있게 된다.

    데이터 축소는 차폐 인수을 계산하는데 있어서 확실한 성능상의 이득을 제공하지만, 동시에 연산시에 필요한 정보 또한 제거한다. 이것은 우리가 계산으로 얻은 차폐 인수 값이 꼭 정확한 값이 아닐 수 있음을 의미하지만, 이후의 장에서 보게 되겠지만  전역 조명 과정은 근사치 일지라도 훌륭하게 장면의 현실감을 부여해 준다.

    텍스처에 장면의 깊이 정보를 저장한 뒤, 깊이 값의 직접 인접에 기반하여 각 점이 얼마나 차폐되었는지 결정해야 한다. 이 연산은 다른 AO 기법들과 꽤나 비슷하다. 그림 3에 보이는 바와같이 깊이 버퍼를 고려해야한다 :

    그림 3. 뷰view 방향이 아래쪽으로 향한 샘플 깊이 버퍼

    이상적으로 주어진 점을 중심으로 둘러싼 반지름 r인 구를 생성하고, 그 구와 깊이 버퍼에 있는 물체와 교차하는 부피를 구하기 구를 따라 통합해 나간다. 이 컨셉은 여기 그림 4에서 볼 수 있다.

    그림 4. 차폐를 계산하기 위한 이상적인 샘플링 구

    물론, 렌더된 이미지의 모든 픽셀에서 복잡한 3D 통합을 수행하는 것은 전적으로 비효율적이다. 대신, 이 구의 부피 내에 존재하는 3D 샘플링 커널을 이용하여 이 구를 추정할 것이다. 샘플링 커널은 명시된 지점의 깊이 버퍼를 살펴, 그 각 가상의 점이 깊이 표면에 의해 가려지는지 결정하는데 이용된다. 샘플링 커널의 예시는 그림 5에 나타나 있으며, 우리의 표면점에 어떻게 적용해야 하는지도 보여준다.

    그림 5. 단순화한 샘플링 커널과 깊이 버퍼 내에의 위치

    차폐 인수는 차폐가 발생한 샘플링 커널의 합으로 구할 수 있다. 구현 부분에서 살펴보겠지만, 이 개별적인 계산은 카메라부터의 거리, 차폐 지점과 기준점 간의 거리, 아티스가 지정한 비례축소 인수 등의 몇가지 다른 인수들에 따라 더 민감하게, 혹은 덜 민감하게 할 수 있다.

    구현

    알고리즘이 어떻게 기능하는지 이해했따면, 이제 구현하는 방법에 대해 논의해 보자. 알고리즘 이론 파트에서 논의했듯이, 장면의 깊이 정보가 담긴 소스가 필요하다. 간단하게 하기 위해, 우리는 깊이 정보를 저장하기 위해 서로다른 부동소수 버퍼를 사용할 것이다. 비록 조금 더 복잡할지는 모르겠지만, 더 효율적인 기법은 장면의 깊이 값을 얻기 위해 z-버퍼를 이용할 것이다. The following code snippet shows how to create the floating point buffer, as well as the render target and shader resource views that we will be binding it to the pipeline with: 

    01.// 깊이 버퍼 생성
    02.D3D10_TEXTURE2D_DESC desc;
    03.ZeroMemory( &desc, sizeof( desc ) );
    04.desc.Width = pBufferSurfaceDesc->Width;
    05.desc.Height = pBufferSurfaceDesc->Height;
    06.desc.MipLevels = 1;
    07.desc.ArraySize = 1;
    08.desc.Format = DXGI_FORMAT_R32_FLOAT;
    09.desc.SampleDesc.Count = 1;
    10.desc.Usage = D3D10_USAGE_DEFAULT;
    11.desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
    12. 
    13.pd3dDevice->CreateTexture2D( &desc, NULL, &g_pDepthTex2D );
    14. 
    15.// 렌더 타겟 리소스 뷰 생성
    16.D3D10_RENDER_TARGET_VIEW_DESC rtDesc;
    17.rtDesc.Format = desc.Format;
    18.rtDesc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2D;
    19.rtDesc.Texture2D.MipSlice = 0;
    20. 
    21.pd3dDevice->CreateRenderTargetView( g_pDepthTex2D, &rtDesc, &g_pDepthRTV );
    22. 
    23.// 셰이더-리소스 뷰 생성
    24.D3D10_SHADER_RESOURCE_VIEW_DESC srDesc;
    25.srDesc.Format = desc.Format;
    26.srDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
    27.srDesc.Texture2D.MostDetailedMip = 0;
    28.srDesc.Texture2D.MipLevels = 1;
    29. 
    30.pd3dDevice->CreateShaderResourceView( g_pDepthTex2D, &srDesc, &g_pDepthSRV );


    깊이 정보를 생성한 후, 다음 단계는 최종 장면 내 각 픽셀의 차폐 인수를 계산하는 것이다. 이 정보는 두번째 단일 성분 부동소수 버퍼에 저장할 것이다. 이 버퍼를 생성하기 위한 코드는 부동소수 깊이 버퍼를 생성하기 위해 사용한 코드와 매우 유사하므로, 페이지 길이를 줄이기 위해 생략한다.

    이제 두 버퍼가 모두 생성되었으므로, 각 버퍼에 저장할 정보를 생성하는 부분을 상세히 살펴보자. 깊이 버퍼는 뷰 공간의 깊이값을 기본적으로 저장한다. 절단 공간 깊이clip space depth 대신 선형 뷰 공간 깊이linear view space depth를 사용하여 투시 투영perspective projection으로 인한 깊이 범위 왜곡을 방지한다. 뷰 공간 깊이는 입력 버텍스와 월드뷰world-view 행렬을 곱해서 구할 수 있다. 그런뒤 뷰 공간 깊이는 단일 성분 버텍스 속성으로 픽셀 셰이더로 전달된다. 

    01.fragment VS( vertex IN )
    02.{
    03.fragment OUT;
    04.// 절단 공간 좌표를 출력
    05.OUT.position = mulfloat4(IN.position, 1), WVP );
    06.// 뷰 공간 좌표를 계산
    07.float3 viewpos = mulfloat4( IN.position, 1 ), WV ).xyz;
    08.// 뷰 공간 깊이값을 저장
    09.OUT.viewdepth = viewpos.z;
    10.return OUT;
    11.}


    깊이값을 부동소수 버퍼에 저장하기 위해, 픽셀 셰이더에서는 근거리 클리핑 평면과 원거리 클리핑 평면간의 거리에 따라 깊이값을 확대/축소한다. 이것은 범위 [ 0, 1 ]을 가진 선형, 정규화된 깊이 값을 생성한다. 

    01.pixel PS( fragment IN )
    02.{
    03.pixel OUT;
    04. 
    05.// 뷰 공간 범위에 따라 깊이값을 확대/축소
    06.float normDepth = IN.viewdepth / 100.0f;
    07. 
    08.// 확대/축소된 값을 출력
    09.OUT.color = float4( normDepth, normDepth, normDepth, normDepth );
    10. 
    11.return OUT;
    12.}


    저장된 정규화된 깊이 정보를 가지고, 각 픽셀의 차폐 인수를 생성하기 위해 깊이 버퍼를 셰이더 리소스로 바인딩하고 각 차폐 버퍼에 저장한다. 차폐 버퍼 생성은 단일 전체 스크린 화면을 렌더링함으로써 초기화한다. 전체 스크린 화면의 버텍스들은 단지 단일 2-성분 속성을 가지고 있으며, 이는 깊이 버퍼 내의 자신의 위치에 대응하는 텍스처 좌표를 나타내고 있다. 버텍스 셰이더는 단순히 이 파라미터들을 픽셀 셰이더에 전달만 한다.

    1.fragment VS( vertex IN )
    2.{
    3.fragment OUT;
    4.OUT.position = float4( IN.position.x, IN.position.y, 0.0f, 1.0f );
    5.OUT.tex = IN.tex;
    6. 
    7.return OUT;
    8.}


    필셀 셰이더는 3D 샘플링 커널의 형태를 3-성분 벡터의 배열로 정의하는 것으로 시작한다. 벡터의 길이는 [ 0, 1 ] 범위 사이에서 다양히 변하며 각 차폐 테스트에서 약간의 변화를 주게 된다.

    01.const float3 avKernel[8] ={
    02.normalizefloat3( 1, 1, 1 ) ) * 0.125f,
    03.normalizefloat3( -1,-1,-1 ) ) * 0.250f,
    04.normalizefloat3( -1,-1, 1 ) ) * 0.375f,
    05.normalizefloat3( -1, 1,-1 ) ) * 0.500f,
    06.normalizefloat3( -1, 1 ,1 ) ) * 0.625f,
    07.normalizefloat3( 1,-1,-1 ) ) * 0.750f,
    08.normalizefloat3( 1,-1, 1 ) ) * 0.875f,
    09.normalizefloat3( 1, 1,-1 ) ) * 1.000f
    10.}


    다음으로, 픽셀 셰이더는 텍스처 룩업 내에서 샘플링 커널을 반사시키기 위한 렌덤 벡터를 찾는다. 이것은 사용한 샘플링 커널을 이용해 매우 다양한 변화를 줄 수 있으며, 이는 적은 수의 차폐 테스트를 하더라도 높은 수준의 결과를 제공할 수 있게 해준다. 이는 차폐를 계산하기 위한 깊이를 효과적으로 "흐트려jitter" 주어, 우리가 현재 픽셀의 주변 공간을 언더샘플링 한다는 사실을 숨겨준다.

    1.float3 random = VectorTexture.Sample( VectorSampler, IN.tex.xy * 10.0f ).xyz;random = random * 2.0f - 1.0f;


    이제 픽셀 셰이더가 현제 픽셀의 깊이값을 바탕으로 샘플링 커널에 적용하기 위한 확대/축소값을 계산할 것이다. 이 픽셀의 깊이값은 깊이 버퍼로부터 읽어들여 근거리 평면과 원거리 평면간의 거리를 곱함으로 뷰 공간으로 도로 확장된다. 그런 뒤 샘플링 커널의 x 와 y 성분을 위한 확대/축소값을 계산하는데, 이는 샘플링 커널의 희망 반지름( 미터 단위 )을 픽셀의 깊이값( 미터 단위 )로 나누면 된다. 이는 개별 샘플을 찾기위해 사용되는  텍스처 좌표를 확대/축소한다. z 성분의 확대/축소값은 희망 커널 반지름을 근거리 평면과 원거리 평면간의 거리로 나누어 구할 수 있다. 이는 모든 깊이값의 비교를 우리가 깊이 버퍼에 저장한것과 같은 정규화된 깊이 공간에서 이루어지도록 하게 해준다.

    1.float fRadius = vSSAOParams.y;
    2.float fPixelDepth = DepthTexture.Sample( DepthSampler, IN.tex.xy ).r;
    3.float fDepth = fPixelDepth * vViewDimensions.z;
    4.float3 vKernelScale = float3( fRadius / fDepth, fRadius / fDepth, fRadius / vViewDimensions.z );


    커널 확대/축소값이 구해지면, 개별 차폐 테스트를 수행할 수 있게 된다. 이것은 for 루프 내에서 샘플링 커널이 가르키는 각 점들을 계속해서 반복하는 것으로 이루어진다. 현재 픽셀의 텍스처 좌표는 렌덤하게 반사된 커널 벡터의 x와 y 성분을 통해 차감 계산되고 새 좌표에서 깊이값을 살펴보기 위해 사용된다. 이 깊이 값은 커널 벡터의 z 성분을 통해 차감 계산되고 현재 픽셀의 깊이와 비교된다. 
     
    01.float fOcclusion = 0.0f;
    02.for int j = 1; j < 3; j++ )
    03.{
    04.float3 random = VectorTexture.Sample( VectorSampler, IN.tex.xy * ( 7.0f + (float)j ) ).xyz;
    05.random = random * 2.0f - 1.0f;
    06. 
    07.for int i = 0; i < 8; i++ )
    08.{
    09.float3 vRotatedKernel = reflect( avKernel[i], random ) * vKernelScale;
    10.float fSampleDepth = DepthTexture.Sample( DepthSampler, vRotatedKernel.xy + IN.tex.xy ).r;
    11.float fDelta = max( fSampleDepth - fPixelDepth + vRotatedKernel.z, 0 );
    12.float fRange = abs( fDelta ) / ( vKernelScale.z * vSSAOParams.z );
    13.fOcclusion += lerp( fDelta * vSSAOParams.w, vSSAOParams.x, saturate( fRange ) );
    14.}
    15.}


    깊이값의 델타값( = 차이값 )은 어플리케이션의 선택가능한 범위값으로 정규화된다. 이것은 깊이값 차이의 관계 등급을 결정하기 위한 인수를 생성하기 위함이다. 차례로, 이 정보는 이 특정의 차폐 테스트의 범위를 수정할 수 있다. 예를 들어, 한 물체가 장면의 전경에 위치하고 다른 하나의 물체가 그 뒤에 부분적으로 가려져 있다면, 전경의 물체가 유일한 차폐물인 경우 뒷쪽 물체를 위해 불필요한 차폐 테스트를 계산할 필요가 없다. 반대로 배경의 물체에는 앞 물체의 가장자리를 따라 그림자 같은 후광이 나타날 것이다. The implementation of this scaling is to linearly interpolate between a scaled version of the delta value and a default value, with the interpolation amount based on the range value. 

    1.fOcclusion += lerp( ( fDelta * vSSAOParams.w ), vSSAOParams.x, saturate( fRange ) );


    최종 픽셀 색상을 내놓기 전의 마지막 단계는 모든 커널 샘플로부터 평균 차폐값을 계산하고, 그 값을 최대/최소 차폐 값 사이를 보간하는데 사용하는 것이다. 이 마지막 보간은 광범위한 차폐값을 압축해주고 보다 부드러운 출력을 제공해준다. 이 장의 데모 프로그램에서 주의할 것은, 사진의 차폐를 계산하는데 단지 16샘플만을 사용했다는 것이다. 만일 더 많은 샘플을 사용했다면, 출력 차폐 버퍼는 더 부드러운 결과를 보여줄 것이다. 이것은 다른 등급의 하드웨어들에서 성능과 품질을 조정하는 좋은 방법이 된다.
     
    1.OUT.color = fOcclusion / ( 2.0f * 8.0f );
    2. 
    3.// 범위 재매핑
    4.OUT.color = lerp( 0.1f, 0.6, saturate( OUT.color.x ) );


    생성한 차폐 버퍼를 가지고, 최종 렌더링 패스에서 셰이더 자원에 포함해 이를 이용할 수 있다. 렌더된 기하는 단수히 스크린 공간 텍스쳐 좌표를 계산하고, 차폐 버퍼를 샘플링한 뒤, 그 값을 이용하여 ambient 조건을 조정하면 된다. 예시로 제공되는 파일은 단순히 다섯 샘플의 평균을 이용하는 방법을 사용하였지만, 가우시안 브럴와 같은 좀더 세련된 방법을 대신 사용하는 것도 좋다.


    SSAO 데모

    데모 다운로드 : SSAO_Demo

    이번 장을 위해 만든 데모 프로그램은 단순히 다수의 정방면체를 우리의 SSAO 셰이더만을 이용하여 렌더링한 것이다. 이번 기법에서 논의된 조절가능한 파라미터들에 대해서는 화면상의 슬라이더 콘트롤러를 이용하여 실시간으로 바꿀 수 있다. 그림 6 이하는 차폐 버퍼의 모습과 최종 출력 렌더링의 결과를 볼 수 있다. 한가지 알릴 사실은 차폐 효과를 보다 과장하기 위하여 차폐 파라미터들을 조절했다. 

    그림 6. 데모 프로그램의 차폐 버퍼의 모습


    그림 7. 데모 프로그램의 최종 출력 렌더링의 모습

    결론 

    In this chapter we developed an efficient, screen space technique for adding realism to the ambient lighting term of the standard phong lighting model. This technique provides one implementation of the SSAO algorithm, but it is certainly not the only one. The current method can be modified for a given type of scene, with more or less occlusion for distant geometry. In addition, the minimum and maximum amounts of occlusion, interpolation techniques, and sampling kernels are all potential areas for improvement or simplification. This chapter has attempted to provide you with an insight into the inner workings of the SSAO technique as well as a sample implementation to get you started.










    http://www.eppengine.com/zbxe/programmig/2982



    SSAO 는 최근에 많이 이슈가 되고 있는 GI 기법중 하나이다.

     

    GI 라고 하면 단순히 모든 오브젝트가 빛을 발산하는 주체가 된다고만 생각하는데, (빛 반사를 통해.. )

     

    빛으로인해 생겨지는 그림자 역시 GI중 하나이다.

     

    SSAO 는 많은 사람들이 알고 있듯이 환경광 차폐를 화면공간에서 처리하는 기법으로

     

    크게 깊이를 이용한 기법, 깊이와 상방벡터를 이용하는 기법 두가지가 있다.

     

    환경광 차폐가 일어나는 경우에 대해서는 KGC2009 강연자료중 Lighting In Screen Space 의 ppt 자료를 참고.

     

    여하튼.. 위에서 이야기한 두가지 기법 모두 장단이 있기에 적절한 방식을 선택해서 사용하면 되겠다.

     

     

     

    uniform sampler2D som;  // Depth texture 

    uniform sampler2D rand; // Random texture 
    uniform vec2 camerarange = vec2(1.01024.0);
          
       float pw = 1.0/800.0*0.5
       float ph = 1.0/600.0*0.5;  

       float readDepth(in vec2 coord)  
       {  
         if (coord.x<0||coord.y<0return 1.0;
          float nearZ = camerarange.x;  
          float farZ =camerarange.y;  
          float posZ = texture2D(som, coord).x;   
          return (2.0 * nearZ) / (nearZ + farZ - posZ * (farZ - nearZ));  
       }   

       float compareDepths(in float depth1, in float depth2,inout int far)  
       {  

         float diff = (depth1 - depth2)*100//depth difference (0-100)
         float gdisplace = 0.2//gauss bell center
         float garea = 2.0//gauss bell width 2

         //reduce left bell width to avoid self-shadowing
         if (diff<gdisplace){ 
            garea = 0.1;
         }else{
            far = 1;
         }
         float gauss = pow(2.7182,-2*(diff-gdisplace)*(diff-gdisplace)/(garea*garea));

         return gauss;
       }  

       float calAO(float depth,float dw, float dh)  
       {  
         float temp = 0;
         float temp2 = 0;
         float coordw = gl_TexCoord[0].x + dw/depth;
         float coordh = gl_TexCoord[0].y + dh/depth;
         float coordw2 = gl_TexCoord[0].x - dw/depth;
         float coordh2 = gl_TexCoord[0].y - dh/depth;

         if (coordw  < 1.0 && coordw  > 0.0 && coordh < 1.0 && coordh  > 0.0){
         vec2 coord = vec2(coordw , coordh);
            vec2 coord2 = vec2(coordw2, coordh2);
            int far = 0;
         temp = compareDepths(depth, readDepth(coord),far);

            //DEPTH EXTRAPOLATION:
            if (far > 0){
              temp2 = compareDepths(readDepth(coord2),depth,far);
              temp += (1.0-temp)*temp2; 
            }
         }

         return temp;  
       }   
         
       void main(void)  
       {  
         //randomization texture:
         vec2 fres = vec2(20,20);
         vec3 random = texture2D(rand, gl_TexCoord[0].st*fres.xy);
         random = random*2.0-vec3(1.0);

         //initialize stuff:
         float depth = readDepth(gl_TexCoord[0]);  
         float ao = 0.0;

         for(int i=0; i<4; ++i) 
         {  
           //calculate color bleeding and ao:
           ao+=calAO(depth,  pw, ph);  
           ao+=calAO(depth,  pw, -ph);  
           ao+=calAO(depth,  -pw, ph);  
           ao+=calAO(depth,  -pw, -ph);

           ao+=calAO(depth,  pw*1.20);  
           ao+=calAO(depth,  -pw*1.20);  
           ao+=calAO(depth,  0, ph*1.2);  
           ao+=calAO(depth,  0, -ph*1.2);
         
           //sample jittering:
           pw += random.x*0.0007;
           ph += random.y*0.0007;

           //increase sampling area:
           pw *= 1.7;  
           ph *= 1.7;    
         }         

         //final values, some adjusting:
         vec3 finalAO = vec3(1.0-(ao/32.0));


         gl_FragColor = vec4(0.3+finalAO*0.7,1.0);  
       }  

    장점 : 따로 blur pass 를 실행할 필요가 없다. ( 랜덤맵 텍스쳐를 활용하기에 가능한 부분)

               시각적 품질이 나쁘지 않다.

               노멀 버퍼없이도 깔끔하다.

     

    단점 : 전통적 ssao 보다는 좀 느리다.

               역시 노멀 버퍼를 사용하지 않는 것 으로 인한 약간의 시각적 어색함이 존재.. 약간...








    http://ozlael.egloos.com/2963328



    SSAO (Screen Space Ambient Occlusion) 링크 모음 Game develop




    AO(Ambient Occlusion)는 사실적인 그림자 효과 라이팅의 방법 중 하나로써, 전역 조명 과정에서 한 점의 차폐 양을 계산 하는 것을 나타낸다. 이로써 장면 내의 입체 볼륨감을 더욱 부각시킬 수 있다. SSAO는 이를 Screen Space에서 수행하는 것이다. HBAO, SSDO등도 이와 한 뿌리다. 

    실제 적용 해보면 제일 애를 먹는 것이 half-resolution에서 AO 처리를 한 뒤 upscale을 하는 부분인데, 이는 inferred lighting의 DSF필터를 적용 하면 쉽게 해결 될 듯 하다.( DSF 적용 해보진 않음;; 언능 해봐야집)


    하지만 지글지글 문제는 해결이 안되지 싶은데.. 이놈때문에 엔진 디폴트로 AO는 꺼트린 상태다. 섬바뤼헬미~

    관련 링크 (문서 내 링크 중복도 많이 있다 =ㅈ=):

    http://www.gamedev.net/community/forums/topic.asp?topic_id=545825
    http://en.wikipedia.org/wiki/Screen_Space_Ambient_Occlusion
    http://www.gamedev.net/community/forums/topic.asp?topic_id=545825
    http://developer.amd.com/gpu_assets/S2008-Filion-McNaughton-StarCraftII.pdf
    http://www.gamerendering.com/2009/01/14/ssao/
    http://www.gamedev.net/community/forums/topic.asp?topic_id=550699
    http://www.vizerie3d.net/
    http://developer.download.nvidia.com/presentations/2008/SIGGRAPH/HBAO_SIG08b.pdf
    http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_Ambient_Occlusion.pdf
    http://developer.download.nvidia.com/SDK/10.5/direct3d/Source/ScreenSpaceAO/doc/ScreenSpaceAO.pdf
    http://gamepro.tistory.com/539
    http://www.gamedev.net/reference/programming/features/simpleSSAO/

    반응형
    반응형

    http://goo.gl/3eW9r


    Alpha test is perhaps the simplest Direct3D 9 functionality to emulate. It does not require the user to set a alpha blend state. Instead the user can simply choose to discard a pixel based upon its alpha value. The following pixel shader does not draw the pixel if the alpha value is less than 0.5. 

    // 
    // PS for rendering with alpha test 
    // 
    float4 PSAlphaTestmain(PSSceneIn input) : COLOR0 
    {
    float4 color = tex2D( g_samLinear, g_txDiffuse, input.tex ) * input.colorD; 
    if( color.a < 0.5 ) 
    discard; 
    return color; 


    반응형
    반응형

    BLOG main image



    셰이더를 직접 작성할때는 아래 옵션을 셰이더 코드안에서 enable해주고


    .a   알파값을 1에서 0 으로 가게 하면 서서히 반투명처리가 된다




    _pD3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); //반투명 on

    _pD3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); //(1-a)*바닥색

    _pD3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); //a*덮일 색

    반응형
    반응형
    http://parkpd.egloos.com/3790654

    이번에는 [http]Adding a Skybox to RenderMonkey 블로깅을 간단하게 번역해 봤습니다. 쉐이더 코드에 한글 주석을 넣으면 error X3000 가 생기는 경우가 있군요. 컴파일 에러가 나면 한글 주석을 다 빼고 해 보세요.

    RenderMonkey 에 스카이박스 붙이기

    전에는 [http]RenderMonkey 에 대한 기본적인 튜토리얼을 블로깅했습니다. 이번에는 어떻게 하면 RenderMonkey 에서 화면에 보여지는 단색 배경 대신 스카이박스(skybox) 를 보여줄 수 있을지를 얘기해 볼까 합니다. 저는 이미 여러분이 [http]이전 튜토리얼을 읽고 따라해 봤고, HLSL 에 대해 익숙하며 RenderMonkey 가 아닌 다른 곳에서 스카이박스를 어떻게 만드는지 알고 있다고 가정하겠습니다. 인터넷 찾아보시면 (이 블로깅처럼) 스카이박스를 어떻게 구현하는지 좋은 자료를 많이 찾아볼 수 있습니다. 그나저나 제가 구현한 방식은 Riemer Grootjans 의 책 [http]XNA 3.0 Game Programming Recipes 에 기초하고 있습니다. 이 책에서는 이 외에도 여러 다양한 주제를 다루고 있는데 도움되는 내용이 많습니다.

    소개

    RenderMonkey 에서 처음 쉐이더를 짜기 시작하면서 환경매핑(environment mapping) 같은 이펙트를 제대로 테스트하기 위해 씬에 스카이박스를 붙이는 법을 찾아봤습니다. RenderMonkey 의 인터페이스를 뒤져봤지만 스카이박스를 그려주는 기능을 찾을 수가 없더군요. 결국 직접 구현하기로 했습니다. 뭔가 더 쉽고 직관적인 방법이 있을 거 같기도 해서, 사실 제 방식을 공개하는게 큰 의미가 있을까 망설이기도 했습니다. 그러니 더 쉬운 방법이 있다면 제발 저에게 알려주세요.

    제가 스카이박스를 그리는 비결은 이펙트에 두 번째 쉐이더 패스를 추가해서, 첫 번째 쉐이더로는 스카이박스를 그리고, 두 번째 쉐이더로 모델을 그리는 것입니다. 여기에서 보여드릴 구현에서는 항상 카메라 중점에 위치하는 (RenderMonkey 에서 생성한) 간단한 텍스처 맵핑 큐브모델을 사용할 겁니다. 이렇게 하면 카메라가 어디 있더라도 스카이박스는 충분한 거리를 항상 유지할 수 있습니다. 그래픽 카드의 렌더 스테이트를 바꾸고 감기 순서(culling order)를 뒤집어서 안이 그려지도록 하고, 스카이박스를 그릴때 Z-버퍼 쓰기를 막아서 두 번째 패스에서 모델을 그리는데 아무 문제가 없게 했습니다. 스카이박스에 텍스처를 입히기 위해서는 기본으로 제공되는 큐브맵과 texCUBE 이라는 HLSL 기본함수만 있으면 됩니다. 이제 시작해 봅시다!

    이펙트 시작하기

    먼저 워크스페이스에 새로운 DirectX 이펙트를 만들고(Effect Workspace -> Add Default Effect -> DirectX -> DirectX), 이펙트 위에서 우클릭한 뒤 "Add Pass" 를 선택해 두 번째 패스를 추가합시다.

    a_pass1.jpg


    이후 워크스페이스는 다음과 같을 겁니다.

    a_pass2.jpg


    지금부터 스카이박스를 설치하는 대부분의 작업은 첫 번째 패스(Pass 0)에서 하게 됩니다. 큐브 모델을 만들고, 몇가지 변수와 쉐이더에서 쓸 큐브맵 텍스처를 준비하고, 쉐이더를 작성하고 그래픽 카드의 렌더 스테이트도 제대로 잡아줘야 합니다. 단계별로 나갈테니 너무 걱정마세요.

    큐브 만들기

    스카이박스로 쓸 큐브 모델을 만들기 위해서는, 이펙스 위에서 우클릭해서 새로운 모델을 추가해야 합니다. 하지만 목록 중에서 모델을 선택하진 마세요. 대신에 목록 최상단에 있는 기본 모델(Model) 을 선택합니다. myModel1 이라는 객체가 생길텐데 기존 모델과 헷갈리는 게 싫으니 이름을 "Box" 로 바꿔줍시다.

    b_model1.jpg


    이제 새로 만든 모델 Box 위에서 우클릭한 뒤 "Generator" -> "Geometry Generator" 를 선택합니다. 이러면RenderMonkey 가 우리의 선택에 따라서 적당한 데이터를 모델에 채워줍니다.

    b_model2.jpg


    팝업창에서 적당한 크기의 상자를 생성합니다. near 평면과 far 평면을 어떻게 설정하느냐에 따라서 박스 크기가 너무 작거나 클 경우에 문제가 생길 수 있습니다. 여기에서는 25x25x25 크기의 박스를 만들겠습니다.

    b_model3.jpg


    까먹기전에 Pass 0 를 열어서 model 위에서 우클릭한 뒤 새로 생성한 상자를 할당합시다(아마 model 은 처음 추가한 model 을 가리키고 있었을 겁니다). 

    b_model4.jpg


    변수 생성

    아래에서 보여드릴 스카이박스 쉐이더에서는 3 개의 전역 변수를 사용하는데, 각각 카메라의 위치, 뷰-프로젝션 행렬, 하늘(sky)에 사용할 큐브맵 텍스처입니다.
    float4 ViewPosition;
    float4x4 ViewProjection;
    sampler Texture;

    이전 튜토리얼에서 봤던 것처럼 이들 변수를 생성합시다. 적당한 데이터타입과 시멘틱을 지정하는 것을 잊지 맙시다. ViewProjection 행렬은 기본적으로 들어있지만(matViewProjection), 기본 이름이 맘에 들지 않기 때문에 이름을 ViewProjection으로 바꿨습니다. Pass 0, Pass 1 의 쉐이더 코드에서 matViewProjection 를 찾아 전부 ViewProjection 로 바꿔줍시다. ViewPosition 같은 경우 Float 의 미리 정의된 (predefined) 변수에서 찾을 수 있습니다. 

    c_var1.jpg


    텍스처를 로딩하기 위해서 Cubemap 옵션을 사용합니다(Add Texture -> Add Cubemap -> Snow.dds).

    c_var2.jpg


    방금 생성한 큐브맵(Snow)과 링크되는 Texture Object 를 "Texture" 라는 이름으로 Pass 0 에 추가하는 것도 잊지 맙시다.

    c_var4.jpg


    원한다면 텍스처 객체가 좀더 나은 필터링 방식을 쓰게 할 수도 있습니다.

    c_var5.jpg


    코드 추가

    이제 정점 쉐이더와 픽셀 쉐이더 코드를 추가해 봅시다. 아래에 이미 코드가 준비되어 있습니다. 각각 Pass 0 의 정점 쉐이더와 픽셀 쉐이더에 복사해 붙여넣습니다. 코드가 어떤 작업을 하는지를 주석으로 설명해 넣었습니다만, 궁금한 점이 있으면 언제든지 물어봐주세요.

    정점 쉐이더 :
    float4 ViewPosition;
    float4x4 ViewProjection;
      
    struct VS_INPUT
    {
       float4 position : POSITION0;
    };
      
    struct VS_OUTPUT
    {
       float4 position : POSITION0;
       float3 position3D : TEXCOORD0;
    };
      
    // 이동 행렬을 만든다.
    float4x4 translation(float3 position)
    {
       return float4x4(
                  float4(1,0,0,0),
                  float4(0,1,0,0),
                  float4(0,0,1,0),
                  float4(position.xyz, 1)
              );
    }
      
    VS_OUTPUT vs_main(VS_INPUT input)
    {
       VS_OUTPUT output;
      
       // 스카이박스를 카메라 위치로 옮기는 월드 행렬을 만든다.
       float4x4 world = translation(ViewPosition.xyz);
      
       // 월드행렬을 기존 뷰-프로젝션 행렬과 곱한다.
       float4x4 WorldViewProjection = mul(world, ViewProjection);
      
       // 이렇게 만든 공간으로 정점을 이동시킨다.
       output.position = mul(input.position, WorldViewProjection);
     
       // 모델 공간에서의 원래 정점 위치를 저장한다.
       // 큐브 모델은 중심점이 원점에 있기 때문에
       // 원래 정점 위치값을 큐브맵의 방향값으로 바로 활용할 수 있다.
       output.position3D = input.position;
      
       return output;
    }
    (한글 주석 때문에 error X3000: syntax error: unexpected end of file 에러가 뜨는 경우가 있네요. 한글 주석을 제거하면 에러가 사라집니다. 한글주석이나 경로에 한글이 들어가면 이런 현상이 있다고 하는데 RenderMonkey 의 버그인지 뭔지 정확한 원인을 모르겠네요.)

    픽셀 쉐이더 :
    sampler Texture;
      
    struct VS_OUTPUT
    {
       float4 position : POSITION0;
       float3 position3D : TEXCOORD0;
    };
      
    float4 ps_main(VS_OUTPUT input) : COLOR0
    {
       // 원래 정점 위치를 큐브맵 텍스처를 샘플링하는데 사용한다.
       return texCUBE(Texture, input.position3D);
    }

    렌더 스테이트 설정

    아직 화면에는 아무 것도 안 나오겠지만, 쉐이더는 아무 문제없이 컴파일 될 겁니다. 아무 것도 보이지 않는 이유는 우리가 큐브 모델 내부에서 그려지지 않는 안쪽 평면을 보고 있기 때문입니다. 그래픽 카드의 감기 방식(culling mode)를 바꿔줍시다. 또한 우리는 하늘(sky)가 씬의 마지막까지 남게 하고 싶기 때문에 z-버퍼 쓰기를 금지시켜야 합니다.

    이를 위해서 Pass 0 에 우클릭한 뒤 "Add Render State Block" 을 추가, 편집해 줘야 합니다. 이런 변경사항은 모델 그릴 때에는 원래대로 되돌려놔야 하기 때문에 Pass 1 에서 같은 작업을 반복해야 합니다.

    Pass 0 의 Render State Options :
    D3DRS_CULLMODE 를 D3DCULL_CW 로 변경
    D3DRS_ZWRITEENABLE 을 FALSE 로 변경
    e_state1.jpg


    Pass 1 의 Render State Options:
    D3DRS_CULLMODE 를 D3DCULL_CCW 로 변경
    D3DRS_ZWRITEENABLE 을 TRUE 로 변경
    e_state2.jpg


    결론

    이제 끝났습니다! 이제 Pass 1 를 바꿔서 원하는 쉐이더를 써 넣어도, 스카이박스는 계속 보일 것이고 카메라에 맞춰서 반응할 것입니다. 결과는 다음과 같습니다 :

    g_end.jpg


    적당한 스카이박스가 준비되어 있다면 아래와 같은 반사(reflection) 나 굴절(refraction) 등의 여러 이펙트도 시험해 볼 수 있습니다.

    example.jpg


    마지막 팁 : 기본적으로 Pass 1 은 Pass 0 과 똑같은 Stream Mapping 구조체를 참조하고 있습니다. 각 pass 별로 다른 구조체가 필요하다면, 이펙트별로 새로운 Stream Mapping 객체를 만들어 우클릭한 뒤 Pass 1 의 참조를 새로운 인스턴스로 바꿔주기만 하면 됩니다.

    오늘은 여기까지입니다. 도움이 되었으면 좋겠네요.

    반응형
    반응형

    http://stackoverflow.com/questions/5149544/can-i-generate-a-random-number-inside-a-pixel-shader




    What you generally do when you want a random value in a pixel shader is to pass in a texture containing noise. While it's not actually "random" - it looks random.

    For example, here's some code from a pixel shader I have lying around:

    float3 random = (tex2D(noiseTexture, texCoord * noiseScale + noiseOffset));
    

    The texture I use is an RGB-noise texture, which can come in handy some times. But the same technique would work for a grayscale one.

    By scaling it I ensure that the pixels in the noise texture line up to on-screen pixels (you may also want to set the texture sampler to "point" mode so you don't blur the noise texture).

    By using an offset you can scroll the texture - which is kind of like seeding a random number generator. Use a random offset if you want to avoid that "scrolling" look.

    반응형
    반응형

    BLOG main image





    texture Map_Tex

    <

       string ResourceName = ".\\";

    >;

    sampler2D MapSampler = sampler_state

    {

       Texture = (Map_Tex);

    };


    굵은글씨의 경로를 넣어보면 됨, 경로 설정

    반응형
    반응형

    http://blog.naver.com/frustum/150008165621





    이 문서의 저작권은 김용준( http://3dstudy.net )에게 있습니다.
    상업적인 이용을 금지합니다.

     

    제목 : ‘DirectX 9.0 셰이더 프로그래밍’

     

    2부 고수준 셰이더 언어(High Level Shader Language)
    DirectX 8.0부터 소개된 셰이더는 그 강력한 능력을 빠르게 3D프로그래밍 기술의 주류시장을 잠식해 들어가고 있다. 그러나, 8.0에서의 셰이더 프로그래밍은 불편한 어셈블러였기 때문에 개발자들을 상당히 힘들게했던 것이 사실이다. 이번에 DirectX 9.0에 포함된 HLSL을 사용해서 고수준의 셰이더를 제작해 보도록 하자.

     

    1장 문법
    셰이더는 기본적으로 C언어를 기반으로 하고 있으므로, C언어에 대한 이해를 필수이다. 여기서는 독자들이 C언어를 이미 알고있다고 가정하고 설명을 진행해 나가도록 하겠다. 그러나, C언어를 잘 모르는 독자라도 베이직등의 기본언어를 한번이라도 익혀본 독자라면 HLSL을 이해하는데 별로 무리가 없을 것이다. HLSL의 문법은 그만큼 쉽기 때문이다.

     

    1절 자료형(data types)
    HLSL은 많은 자료형을 지원하는데, 간단한 불린, 정수, 부동소수부터 벡터, 행렬, 구조체 처럼 확장된 자료형까지도 지원한다.

     

    우리가 이번에 살펴볼 내용은 다음과 같다.
    • 스칼라 형(Scalar Types)
    • 변수 선언(Declaring a Variable)
    • 형 변경자(Type Modifiers)
    • 지억부류 변경자(Storage Class Modifiers)
    • 벡터 형(Vector Types)
    • 행렬 형(Matrix Types)
    • 객체 형(Object Types)
    • 구조체 형(Structure Types)
    • 사용자정의 형(User-Defined Types)
    • 형 변환(Type Casts)
    • 변수(Variables)
    • 구성성분 접근과 뒤섞기(Component Access and Swizzles)

     

    1.1 스칼라 형(Scalar Types)
    다음과 같은 스칼라 형을 지원한다.
     
    bool : true 혹은 false    
    int : 32비트 부호있는 정수    
    half : 16비트 부동소수    
    float : 32비트 부동소수    
    double : 64비트 부동소수 


    모든 타겟 플랫폼이 정수형을 지원하지는 않기 때문에 하드웨어에서 float를 사용해서 에뮬레이션 될 수도 있으며, 만약 정수형의 범위를 넘어서는 값이 있다면 이 값을 사용한 함수는 정확하게 작동하지 않을 수도 있다. 마찬가지로 half와 double역시 지원되지 않을 경우에는 float를 사용하여 에뮬레이션 된다.

     

    1.2 변수 선언(Declaring a Variable)
    C언어와 마찬가지로 변수를 선언한다. 다음은 fVar이라는 부동소수 변수를 선언한 것이다.
    float fVar; 

     

    특정한 값으로 선언과 동시에 초기화를 할 수도 있다. 
    float fVar = 3.1f; 

     

    배열형태로 선언하는 것도 가능하다. 
    int iVar[3]; 

     

    배열 선언과 동시에 초기화는 다음과 같이 한다. 
    int iVar[3] = {1,2,3}; 


    1.3 형 변경자(Type Modifiers)
    형 변경자는 변수형의 바로앞에 붙어서 컴파일러에 추가적인 정보를 제공해주는 선택적 명령어이다. 다음과 같은 명령어가 있다.
    • const
    • row_major
    • col_major

     

    1.3.1 const
    const는 변수가 상수임을 나타내는 명령어로서 셰이더 실행중에 값이 바뀌지 않는 다는 것을 나타낸다. 변수를 const로 선언하면 컴파일러는 변수를 쓰기접근이 불가능한 메모리 영역에 넣는다. 당연히 값을 바꿀 수 없으므로, 선언과 동시에 초기화가 되어야 한다.
    const float fConstant = 0.2f; 


    프로그램에 의해서 변화가 가능한 셰이더 상수는 static변경자를 사용하지 않은 전역변수이다. static에 대한 자세한 내용은 '1.4 지억부류 변경자(Storage Class Modifiers)'를 참고하자.

     

    1.3.2 row_major, col_major
    행렬성분은 행위주(row-major)나 열위주(column-major)방식으로 구성된다. 열위주란 행렬의 한 열이 하나의 상수레지스터에 보관되는 방식이며, 행위주는 행렬의 각 행이 하나의 레지스터에 보관되는 방식이다.
    row_major float4x4 worldMatrix; 

     

    행위주 행렬은 다음과 같이 놓이게 된다. 
    11 12 13 14 
    21 22 23 24 
    31 32 33 34 
    41 42 43 44    


    col_major float4x4 transposedWorldMatrix; 

     

    열위주 행렬은 다음과 같이 놓이게 된다. 
    11 21 31 41 
    12 22 32 42 
    13 23 33 43 
    14 24 34 44  

    row_major와 column_major명령어는 행렬이 상수테이블이나 셰이더 입력으로부터 읽혀질때만 효력이 있으며, 행렬성분이 HLSL코드안에서 사용되는 데에는 아무런 효력이 없다.

     

    1.4 지억부류 변경자(Storage Class Modifiers)
    기억부류 변경자는 변수의 생성소멸시점과 접근범위를 컴파일러에게 지정해주는 역할을 한다. 단, 셰이더 전연벽수는 셰이더의 가장 앞머리에서 선언되어야 한다.(함수의 바깥쪽)


    float globalShaderVariable;

    void function()
    {
      float localShaderVariable;
      ... 

     

    HLSL은 다음과 같은 기억부류 변경자를 지원한다.
    • static
    • extern
    • uniform
    • shared

     

    1.4.1 static
    전역범위볼때 static은 다른 프로그램에서 이 변수에 접근할 수 없도록 한다. GetVertexShaderConstantx()나 SetVertexShaderConstantx(), ID3DXConstantTable을 통해서 이 변수에 접근 할 수 없다.


    static float fConstant_Hidden_From_the_App = 0.2f; 

     

    지역범위에서 볼때(함수의 내부에 선언될때) static은 다르게 해석되는데, 처음 함수가 실행되고 나서도 그 값이 계속 유지된다는 의미이다.

     

    1.4.2 extern
    extern은 static의 반대되는 의미이다. extern변수는 셰이더의 바깥에서 값이 설정되어야 한다. 전역변수는 extern과 static이 동시에 선언될 수 없다.


    extern float4 fogColor; 

     

    extern변수에 값을 설정하기 위해서는 SetVertexShaderConstantx()나 ID3DXConstantTable을 사용한다. 전역변수가 extern이나 static중 어느쪽도 지정되지 않았다면, 컴파일러는 extern으로 가정한다.

     

    1.4.3 uniform
    uniform변수는 API함수를 사용해서 화면에 그려질때만 값이 바뀔수 있다.


    uniform float fConstant_Between_Draw_Calls = 0.2f; 

     

    이 말은 일단 값이 설정되면, 모든 정점들(정점셰이더)이나 픽셀들(픽셀셰이더)에서 초기설정값이 일정하게 계속 유지된다는 뜻이다.

     

    1.4.4 shared
    effect들간에 공유될 전역변수에는 shared를 사용한다.


    shared float sharedAlphaValue 

     

    1.5 벡터 형(Vector Types)
    벡터형은 하나 혹은 4개의 구성성분를 가진 특별한 자료 구조체이다.
     
    bool    bVector;   // 1개의 Boolean값을 가진 스칼라
    bool1   bVector;   // 1개의 Boolean값을 가진 벡터
    int1    iVector;   // 1개의 int값을 가진 벡터
    half2   hVector;   // 2개의 half값을 가진 벡터
    float3  fVector;   // 3개의 float값을 가진 벡터
    double4 dVector;   // 4개의 double값을 가진 벡터 

     

    자료형의 바로뒤에 붙은 숫자가 벡터의 구성성분 개수를 나타낸다.

     

    물론, 선언과 동시에 초기화도 가능하다.
    bool    bVector = false;
    int1    iVector = 1;
    half2   hVector = { 0.2, 0.3 };
    float3  fVector = { 0.2f, 0.3f, 0.4f };
    double4 dVector = { 0.2, 0.3, 0.4, 0.5 }; 


    vector라는 명령어를 사용해서 동일한 선언을 할수도 있다.
    vector <bool,   1> bVector = false;
    vector <int,    1> iVector = 1;
    vector <half,   2> hVector = { 0.2, 0.3 };
    vector <float, 3> fVector = { 0.2f, 0.3f, 0.4f };
    vector <double, 4> dVector = { 0.2, 0.3, 0.4, 0.5 };  

    vector형은 <>을 사용해서 자료형과 구성성분의 개수를 지정한다.

     

    1.5.1 벡터 구성성분 접근하기
    벡터는 4개의 구성성분을 가질수 있는데, 각각의 성분은 2가지 명명집합(naming set)을 사용해서 접근할 수 있다.

     

    • 위치값 방식: x,y,z,w
    • 컬러값 방식: r,g,b,a

     

    다음의 소스는 모두 3번째 벡터성분의 값을 읽어온다. 
    float4 pos = float4(0,0,2,1);
    pos.z    // 값은 2
    pos.b    // 값은 2 

     

    명명집합은 한 개 이상의 성분을 사용할 수 있지만, 섞을 수는 없다. 
    float4 pos = float4(0,0,2,1);
    float2 temp;
    temp = pos.xy  // 유효
    temp = pos.rg  // 유효
    temp = pos.xg  // 오류! 위치값 방식과 컬러값 방식은 섞어서 사용할 수 없다. 

     

    1.5.2 벡터 구성성분 뒤섞기(swizzle)
    값을 읽을 때 하나 혹은 그 이상의 구성성분을 지정하는 것을 뒤섞기라고 한다.
    float4 pos = float4(0,0,2,1);
    float2 f_2D;
    f_2D = pos.xy;   // 2개의 값을 읽는다
    f_2D = pos.xz;   // 어떠한 순서로 읽어도 상관없다
    f_2D = pos.zx;

    f_2D = pos.xx;   // 같은 값을 한번이상 읽는것도 가능하다
    f_2D = pos.yy; 

     

    1.5.3 벡터 구성성분 마스킹
    마스킹은 몇개의 값이 쓰여질 것인지를 조절한다.
    float4 pos = float4(0,0,2,1);
    float4 f_4D;
    f_4D    = pos;     // 4개의 성분을 쓴다

    f_4D.xz = pos.xz;  // 2개의 성분을 쓴다
    f_4D.zx = pos.xz;  // 쓰기 순서를 바꾼다

    f_4D.xzyw = pos.w; // 하나의 성분을 여러 개에 쓸 수도 있다
    f_4D.wzyx = pos; 

     

    다음처럼 하나의 값에 두개의 값을 쓸수는 없다.
    f_4D.xx = pos.xy;   // 오류! 

     

    구성성분의 명명집합을 섞어서 사용할 수 없다.
    f_4D.xg = pos.rgrg; // 오류! 

     

    1.5.4 벡터 수학
    HLSL은 표준수학 표기법과 약간 다른데, 그 이유는 연산자가 구성 성분별로 정의되기 때문이다. 예를들어서 float4 v = a*b 는 다음문장과 동일한 문장이다.
    float4 v;

    v.x = a.x*b.x;
    v.y = a.y*b.y;
    v.z = a.z*b.z;
    v.w = a.w*a.w; 

     

    이것은 4개의 구성성분 곱셈이며, 내적이 아니다. 내적은 •(a,b)로 표기한다. 두 연산의 차이를 비교해 보면 다음과 같다.


    // 4개성분 벡터 곱셈의 첫번째 성분
    a.x * b.x = a.x*b.x

     

    // 4개성분 내적의 첫번째 성분
    a.x • b.x = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; 

     

    행렬도 구성성분별 연산을 사용한다.
    float3x3 mat1,mat2;
    ...   
    float3x3 mat3 = mat1*mat2;  

    이것은 행렬곱이 아니라, 두 행렬의 구성성분별 곱셈이다.


    // 구성성분별 행렬 곱셈
    mOne._m00 * mTwo._m00 = mOne._m00 * mTwo._m00; 

     

    행렬곱은 mul()이라는 내부함수를 사용한다.
    float3x3 mat1,mat2;
    ...
    float3x3 mat3 = mul(mat1,mat2); 

     

    다음은 두 행렬의 적(product)이다.
    // 4개성분 행렬 곱셈의 첫번째 성분
    mul(mOne._m00, mTwo._m00) = mOne._m00 * mTwo._m00 + mOne._m01 * mTwo._m10
                               +  mOne._m02 * mTwo._m20 + mOne._m03 * mTwo._m30; 

     

    중복지정된 mul()함수는 벡터와 행렬간의 곱셈을 수행한다.
    예) vector * vector, vector * matrix, matrix * vector, matrix * matrix

     

    다음 소스는 동일한 역할을 한다.
    float4x3 World;

    float4 main(float4 pos : POSITION) : POSITION
    {
        float4 val;
        val.xyz = mul(pos,World);
        val.w = 0;

        return val;

     

    float4x3 World;

    float4 main(float4 pos : POSITION) : POSITION
    {
        float4 val;
        val.xyz = (float3) mul((float1x4)pos,World);
        val.w = 0;

        return val;
     
    이 예제는 pos벡터를 (float1x4)형변환을 사용해서 열벡터로 변환한다. 벡터를 형변환하거나, mul()함수에 전달되는 인수의 순서를 바꾸는 것은 행렬을 전치(transpose)하는 것과 같다.

     

    자동형변환의 결과로 다음의 예제는 mul()과 dot()함수가 같은 결과를 반환하게 된다.
    {
      float4 val;
      return mul(val,val);
    }
    mul()의 결과는 1x4 * 4x1 = 1x1벡터이다.  

     

    {
      float4 val;
      return dot(val,val);
    }
    내적의 반환값은 하나의 스칼라 값이다. 

     

    1.6 행렬 형(Matrix Types)
    행렬은 행과 열을 가진 자료형이다. 행렬의 구성성분은 어떠한 스칼라 값도 가능하지만, 모든 구성성분은 동일한 자료형이어야 한다. 행과 열의 개수는 "행x열"의 형태로 자료형의 뒤에 추가된다.
    int1x1    iMatrix;   // 1행1열의 int행렬
    int2x1    iMatrix;   // 2행1열의 int행렬
    ...
    int4x1    iMatrix;   // 4행1열의 int행렬
    ...
    int1x4    iMatrix;   // 1행4열의 int행렬


    double1x1 dMatrix;   // 1행1열의 double행렬
    double2x2 dMatrix;   // 2행2열의 double행렬
    double3x3 dMatrix;   // 3행3열의 double행렬
    double4x4 dMatrix;   // 4행4열의 double행렬 

    단, 행과 열의 최대 값은 4이며, 최소값은 1이다.

     

    행렬은 선언과 동시에 초기화 될 수 있다.
    float2x2 fMatrix = { 0.0f, 0.1, // 1행
                         2.1f, 2.2f // 2행 }; 

     

    혹은 matrix라는 명령어를 사용해서 동일한 선언을 할 수도 있다.
    matrix <float, 2, 2> fMatrix = { 0.0f, 0.1, // 1행
                                       2.1f, 2.2f // 2행 }; 

     

    이 예제는 부동소수형의 2행 2열짜리 행렬을 생성한 것이다.
    matrix형도 vector와 마찬가지로 <>을 사용해서 자료형과 구성성분의 개수를 지정한다.

     

    다음 선언은 2행 3열의 half자료형 행렬을 정의한다.
    matrix <half, 2, 3> fHalfMatrix; 

     

    1.6.1 행렬성분 접근
    행렬의 성분은 구조체 연산자인 "."을 통해서 접근가능한데, 2가지의 명명집합을 지원한다.


    첨자가 0부터 시작하는 방식

    _m00, _m01, _m02, _m03
    _m10, _m11, _m12, _m13
    _m20, _m21, _m22, _m23
    _m30, _m31, _m32, _m33

     

     첨자가 1부터 시작하는 방식 

     _11, _12, _13, _14
    _21, _22, _23, _24
    _31, _32, _33, _34
    _41, _42, _43, _44    


    각각의 명명집합은 밑줄("_") 바로뒤에 행과 열의 숫자를 적어주면 된다. 첨자가 0부터 시작하는 명명집합은 앞에 "m"자를 붙여준다.

     

    2개의 명명집합을 사용한 접근방식 예제를 보도록 하자.
    float2x2 fMatrix = { 1.0f, 1.1f, // 1행
                         2.0f, 2.1f  // 2행 };

    float f_1D;
    f_1D = matrix._m00; // 1행 1열의 값 : 1.0
    f_1D = matrix._m11; // 2행 2열의 값 : 2.1

    f_1D = matrix._11;  // 1행 1열의 값 : 1.0
    f_1D = matrix._22;  // 2행 2열의 값 : 2.1 

     

    벡터와 마찬가지로, 명명집합은 하나이상의 성분을 동시에 사용할 수 있다.
    float2x2 fMatrix = { 1.0f, 1.1f, // 1행
                         2.0f, 2.1f  // 2행 }; 
    float2 temp;

    temp = fMatrix._m00_m11 // 유효
    temp = fMatrix._m11_m00 // 유효
    temp = fMatrix._11_22   // 유효
    temp = fMatrix._22_11   // 유효 

     

    1.6.2 행렬의 배열접근
    행렬은 첨자가 0으로 시작하는 배열표기법을 사용해서 접근할 수도 있다. 4x4행렬은 배열방식을 사용하면 다음과 같이 구성되어 있다.
    [0][0], [0][1], [0][2], [0][3]
    [1][0], [1][1], [1][2], [1][3]
    [2][0], [2][1], [2][2], [2][3]
    [3][0], [3][1], [3][2], [3][3]
     

     

    행렬에 접근하는 예제를 보도록 하자.
    float2x2 fMatrix = { 1.0f, 1.1f, // 1행
                         2.0f, 2.1f  // 2행 };
    float temp;

    temp = fMatrix[0][0] // 성분 하나 읽기
    temp = fMatrix[0][1] // 성분 하나 읽기 

     

    구조체 연산자인 "."이 사용되지 않았다는 것을 명심하자. 배열표기법은 하나이상의 값을 읽기위해서 뒤섞기(swizzle)를 사용할 수 없다.
    float2 temp;
    temp = fMatrix[0][0]_[0][1] // 오류! 2개의 성분을 읽을수 없다. 

     

    그러나, 배열접근은 다중성분벡터를 읽을 수 있다.
    float2 temp;
    float2x2 fMatrix;
    temp = fMatrix[0] // 첫번째 행을 읽는다 

     

    1.6.3 행렬요소 뒤섞기(Swizzling)
    벡터처럼, 행렬도 하나이상의 성분을 읽는 것을 뒤섞기라고 한다.
    float4x4 worldMatrix = float4( {0,0,0,0}, {1,1,1,1}, {2,2,2,2}, {3,3,3,3} );
    float4x4 tempMatrix;
    float2  tempFloat; 

     

    다음과 같은 대입은 유효한 대입연산이다.
    tempMatrix._m00_m11 = worldMatrix._m00_m11; // 다중 성분
    tempMatrix._m00_m11 = worldMatrix.m13_m23;

    tempMatrix._11_22_33 = worldMatrix._11_22_33; // 뒤섞기 순서도 마음대로
    tempMatrix._11_22_33 = worldMatrix._24_23_22; 

     

    1.6.4 행렬성분 마스킹
    몇개의 성분이 쓰여질 것인지를 제어하는 것을 마스킹이라고 한다.
    float4x4 worldMatrix = float4( {0,0,0,0}, {1,1,1,1}, {2,2,2,2}, {3,3,3,3} );
    float4x4 tempMatrix;

    tempMatrix._m00_m11 = worldMatrix._m00_m11; // 2개의 성분 쓰기
    tempMatrix._m23_m00 = worldMatrix.m00_m11; 

     

    다음처럼 하나의 성분에 2개의 성분을 대입할 수 없다.
    tempMatrix._m00_m00 = worldMatrix.m00_m11; // 오류! 

     

    다음처럼 명명집합을 섞어서 사용할 수 없다.
    tempMatrix._11_m23 = worldMatrix._11_22; // 오류! 

     

    1.6.5 행렬 순서
    행렬결합순서(matrix packing order)는 uniform인 경우 기본적으로 열위주(column-major)방식이다. 이 말은 행렬의 각 열이 하나의 상수 레지스터에 보관된다는 뜻이다. 행위주(row-major) 행렬결합은 각행이 하나의 상수레지스터에 저장된다.

     

    행렬결합은 #pragma pack_matrix를 사용하거나, row_major, col_major명령을 사용해서 바꿀 수 있다.

     

    일반적으로 열위주 행렬이 행위주 행렬보다 효율적인데, 다음 예제를 보면 이유를 알 수 있을 것이다.

     

    <HLSL코드> 
    // 열위주 행렬결합
    float4x3 World;

    float4 main(float4 pos : POSITION) : POSITION
    {
        float4 val;
        val.xyz = mul(pos,World);
        val.w = 0;

        return val;
    }

     

    <컴파일한 어셈블리 코드 >

    vs_2_0
    def c3, 0, 0, 0, 0
    dcl_position v0
    m4x3 oPos.xyz, v0, c0
    mov oPos.w, c3.x
    // 4개의 명령슬롯 사용    

    [표 1-1] 열위주 결합

     

    <HLSL코드>
    // 행위주 행렬결합
    #pragma pack_matrix(row_major)

    float4x3 World;

    float4 main(float4 pos : POSITION) : POSITION
    {
        float4 val;
        val.xyz = mul(pos,World);
        val.w = 0;

        return val;

     

    <컴파일한 어셈블리 코드 >

    vs_2_0
    def c4, 0, 0, 0, 0
    dcl_position v0
    mul r0.xyz, v0.x, c0
    mad r2.xyz, v0.y, c1, r0
    mad r4.xyz, v0.z, c2, r2
    mad oPos.xyz, v0.w, c3, r4
    mov oPos.w, c4.x
    // 5개의 명령슬롯 사용    
    [표 1-2] 행위주 결합

     

    2개의 어셈블리 코드를 비교해 보면 알 수 있지만, 열위주 행렬결합방식을 사용하면 명령슬롯이 더 절약되며, 사용하는 상수 레지스터의 개수도 적다.

     

    1.7 객체 형(Object Types)
    HLSL에서 지원하는 객체 형은 다음과 같은 것들이 있다.
    • 샘플러(sampler)
    • 구조체(structure)
    • 문자열(string)
    • 정점셰이더 객체(vertex shader object)
    • 픽셀셰이더 객체(pixel shader object)
    • 텍스처(texture)

     

    1.7.1 샘플러(sampler)
    샘플러는 샘플러 상태(sampler state)를 포함하고 있는데, 샘플러 상태란 샘플링될 텍스처를 정하고, 샘플링중에 사용할 필터링 방법을 정하는 것으로 다음의 3가지가 필요하다.
    • 텍스처
    • 샘플러
    • 샘플링 명령

     

    다음은 2차원 텍스처 샘플링을 하는 예제이다.
    texture tex0;
    sampler2D s_2D;

    float2 sample_2D(float2 tex : TEXCOORD0) : COLOR
    {
      return tex2D(s_2D, tex);
     

    텍스처는 texture형의 tex0라는 이름으로 선언되며, 이 예제에서, s_2D라는 샘플러변수는 sampler2D형과 sampler_state명령으로 선언되었다. 샘플러는 샘플러 상태를 {}안에 포함하게 되는데, 여기에는 샘플링될 텍스처와 랩핑모드, 필터모드 등의 필터상태가 포함된다. 만약, 샘플러 상태가 생략되면, 기본 상태 선형필터링과 랩모드가 적용된다.

     

    다음은 기본 샘플러 상태와 필터, 텍스처 주소 모드를 선택한 예이다.
    texture tex0;
    sampler3D s_3D;

    float3 sample_3D(float3 tex : TEXCOORD0) : COLOR
    {
      return tex3D(s_3D, tex);

     

    이것은 입방체 맵핑 텍스처 샘플러이다.
    texture tex0;
    samplerCUBE s_CUBE;

    float3 sample_CUBE(float3 tex : TEXCOORD0) : COLOR
    {
      return texCUBE(s_CUBE, tex);

     

    샘플러 상태 설정 중에서 밉맵필터를 선형(LINEAR)로 설정한 것이다.
    sampler s = sampler_state { texture = NULL; mipfilter = LINEAR; }; 

     

    마지막으로 1차원 샘플러를 보도록 하자.
    texture tex0;
    sampler1D s_1D;

    float sample_1D(float tex : TEXCOORD0) : COLOR
    {
      return tex1D(s_1D, tex);

     

    DirectX 런타임이 1차원 텍스처를 지원하지 않기 때문에, 컴파일러는 y좌표을 무시하는 2차원 텍스처를 생성할 것이다. 결국, tex1D가 2차원 텍스처 탐색방법을 사용하기 때문에 컴파일러는 y좌표를 효율적으로 다루는 방법을 선택하게 된다. 조금 드문 경우지만, 컴파일러가 y항을 효율적으로 처리하는 방법을 선택할 수 없으면 경고를 발생시킨다.

     

    다음의 특별한 예제는 컴파일러가 입력 좌표를 다른 레지스터로 옮겨야 하기 때문에 비효율적이다. (1차원 텍스처 검색은 2차원 검색으로 이루어지는데, 텍스처 좌표가 float1으로 선언되었기 때문이다.)만약 코드가 float2로 쓰여져 있다면 컴파일러는 y좌표를 뭔가 적절하게 초기화해서 텍스처 좌표를 사용할 수 있을 것이다.
    texture tex0;
    sampler s_1D_float;

    float4 main(float texCoords : TEXCOORD) : COLOR
    {
        return tex1D(s_1D_float, texCoords);

     

    모든 텍스처 검색에는 bias나 proj가 추가될수 있다.(즉, tex2Dbias 혹은 texCUBEproj같은 방식). proj가 접미사가 붙으면 텍스처좌표는 w-성분으로 나눠지게 되고, bias가 붙으면 밉맵 단계가 w성분에 의해서 이동한다. 즉, 접미사가 붙은 모든 텍스처 검색은 float4형식의 입력이 필요하다는 것이다. tex1D와 tex2D는 각각 yz요소와, z요소를 무시한다.

     

    샘플러는 또한 배열형태로 사용될 수도 있는데, 아직까지는 샘플러의 동적배열 형태를 지원하지는 않는다. 즉, tex2D(s[0],tex)는 컴파일 시에 값을 결정 할 수 있으므로 유효하지만, tex2D(s[a],tex)는 불가능하다는 얘기다. 샘플러의 동적 접근은 주로 루프를 다루는 코드 작성시에 매우 유용하다. 다음 코드를 보도록 하자.
    sampler sm[4];

    float4 main(float4 tex[4] : TEXCOORD) : COLOR
    {
        float4 retColor = 1;

        for(int i = 0; i < 4;i++)
        {
            retColor *= tex2D(sm[i],tex[i]);
        }

        return retColor;

     

    1.7.2 구조체(structure)
    struct명령어는 구조체형을 정의한다.

     

    다음 구조체는 위치값과 법선값, 2개의 멤버를 포함한다.
    struct vertexData 
    { 
        float3 pos;
        float3 normal;
    }; 

     

    모든 HLSL기본 자료형은 구조체에서 사용할 수 있으며, 멤버변수에 접근하기 위해서는 "."연산자를 사용한다.
    struct vertexData data = { { 0.0, 0.0, 0.0 }, 
                               { 1.1, 1.1, 1.1 } 
                             };
    data.pos = float3(1,2,3);
    data.pos = {1,2,3};
    float3 temp = data.normal; 

     

    구조체가 일단 정의되면, struct명령어가 없어도 이름에 의해서 참조할 수 있다.
    vertexData main(vertexData in) 
    { 
        ... // 어쩌구 저쩌구
        return in;
    }; 

     

    구조체 멤버는 초기값을 가질수 없으며, 또한 static, extern, volatile, const등의 접근범위 명령어를 사용해서 개별적으로 선언될 수 없다.

     

    1.7.3 문자열(String)
    아스키 문자열, 문자열을 받아들이는 연산이나 상태는 없다. 문자열과 주석문은 effect에서만 사용될 수 있다.

     

    1.7.4 정점셰이더 객체(Vertex Shader Object)
    vertexshader 자료형은 정점셰이더 객체를 나타내는데, HLSL셰이더가 컴파일 되거나, 어셈블리 셰이더가 어셈블될때 대입된다.

    <어셈블리  셰이더> 
    vertexshader vs = 
      asm 
      { 
        vs_2_0 
        dcl_position v0
        mov oPos, v0 
      };  

     

    <HLSL 셰이더 >

    vertexshader vs=compile vs_2_0 vsmain();    
     

    1.7.5 픽셀셰이더 객체(Pixel Shader Object)
    pixelshader 자료형은 정점셰이더 객체를 나타내는데, HLSL셰이더가 컴파일 되거나, 어셈블리 셰이더가 어셈블될때 대입된다.
    <어셈블리 셰이더> 
    pixelshader ps = 
      asm 
      { 
        ps_2_0 
        mov oC0, c0 
      }; 

     

    <HLSL 셰이더 >

    pixelshader ps = compile ps_2_0 psmain();    


    1.7.6 텍스처(Texture)
    texture자료형은 텍스처 객체를 나타낸다. 이 자료형은 effect에서 텍스처를 디바이스에 설정하기 위해서 사용된다.
    texture tex0; 


    이 정의는 다음과 같은 의미를 갖는다.
    • 텍스처 형
    • 변수명은 tex0

     

    일단 텍스처변수가 선언되면, 샘플러에 의해서 참조될 수 있다. 
    texture tex0;
    sampler Sampler_1D;
    {
      texture = (tex0);
    }; 

     

    1.8 구조체 형(Structure Types)
    struct명령어는 구조체 형을 정의할때 사용하며, 구조체가 정의되면 ID를 통해서 접근할 수 있다.
    struct [id] { member_list } 

     

    member_list는 하나 이상의 멤버 선언으로 구성되는데, 초기값을 가질수 없으며, 또한 static, extern, volatile, const등의 접근범위 명령어를 사용해서 개별적으로 선언될 수 없다.

     

    1.9 사용자정의 형(User-Defined Types)
    typedef라는 명령어는 형의 이름을 정의할 때 사용하는데, const를 사용해서 상수형 임을 명시할 수도 있다. 배열 접미사는 각 ID뒤에 붙을 수 있으며, 일단 형이 선언되면, ID를 사용해서 참조할 수 있다.
    typedef [const] type id [array_suffix] [, id ...] ; 


    배열접미사는 하나이상의 [정수]형태로 구성되어, 차원을 나타낸다.

     

    D3DX 8.0과의 호환성을 위해서 다음과 같은 형들은 전역범위로 대소문자 구분없이 미리 선언되어 있다. 
    typedef int DWORD;
    typedef float FLOAT; 
    typedef vector <float, 4> VECTOR;
    typedef matrix <float, 4, 4> MATRIX;
    typedef string STRING;
    typedef texture TEXTURE;
    typedef pixelshader PIXELSHADER;
    typedef vertexshader VERTEXSHADER; 

     

    편의를 위해서 다음과 같은 형들도 전역범위로 미리 선언되어 있다. 여기서 #은 1~4 사이의 정수값이다. 
    typedef vector <bool, #> bool#;
    typedef vector <int, #> int#;
    typedef vector <half, #> half#;
    typedef vector <float, #> float#;
    typedef vector <double, #> double#;

    typedef matrix <bool, #, #> bool#x#;
    typedef matrix <int, #, #> int#x#;
    typedef matrix <half, #, #> half#x#;
    typedef matrix <float, #, #> float#x#;
    typedef matrix <double, #, #> double#x#; 

     

    1.10 형 변환(Type Casts)
    다음과 같은 형 변환이 지원된다.
     
    스칼라-스칼라 : 항상 가능. 불린형을 정수나 부동소수로 형변환 할 경우, false는 0, true는 1로 간주한다. 정수나 부동소수를 불린으로 형변환 할 경우에는 0이 false, 0이 아닌값이 true이다. 부동소수를 정수로 형변환 할 때는 소수점 이하는 0으로 짤린다.   


    스칼라-벡터 : 항상 가능. 스칼라 값을 벡터에 구성요소들에 복제해 넣는다.   


    스칼라-행렬 : 항상 가능. 스칼라 값을 벡터에 구성요소들에 복제해 넣는다.   


    스칼라-객체 : 불가능   


    스칼라-구조체 : 구조체의 모든 멤버가 숫자라면 가능. 스칼라 값을 벡터에 구성요소들에 복제해 넣는다.   


    벡터-스칼라 : 항상 가능. 벡터의 첫번째 성분을 선택한다.   


    벡터-벡터 : 대상벡터가 원본벡터보가 크면 안된다. 가장 좌측에 있는 값들부터 보관하고 나머지를 자른다. 이 형변환을 위해서 행-행렬(column matrix)과 열-행렬(row matrix), 수치 구조체는 벡터로 다뤄진다.   


    벡터-행렬 : 벡터의 크기가 행렬의 크기와 일치해야 한다.   


    벡터-객체 : 불가능   


    벡터-구조체 : 구조체가 벡터보다 크지 않으며, 구조체의 구성요소가 수치형이면 가능.   


    행렬-스칼라 : 항상 가능. 행렬의 좌측상단을 선택한다.   


    행렬-벡터 : 행렬의 크기가 벡터의 크기와 일치해야 한다.   


    행렬-행렬 : 두개의 차원 모두 대상 행렬이 원본행렬보다 크면 안된다. 좌측상단의 값들부터 보관하고, 나머지를 버린다.   


    행렬-객체 : 불가능   


    행렬-구조체 : 구조체의 크기가 행렬과 같아야 하며, 구조체의 모든 구성요소가 수치형이어야 한다.   


    객체-스칼라 : 불가능   


    객체-벡터 : 불가능   


    객체-행렬 : 불가능   


    객체-객체 : 객체형이 동일하면 가능   


    객체-구조체 : 구조체는 하나이상의 멤버를 갖고 있으면 안된다. 구조체 멤버의 형이 객체와 동일해야 한다.   


    구조체-스칼라 : 구조체는 적어도 하나의 수치형 멤버를 가져야 한다.   


    구조체-벡터 : 구조체는 적어도 벡터의 크기여야 한다. 첫번째 구성요소는 벡터의 크기보다 큰 수치형이어야 한다.

        
    구조체-행렬 : 구조체는 적어도 행렬의 크기여야 한다. 첫번째 구성요소는 행렬으 크기보다 큰 수치형이어야 한다.

        
    구조체-객체 : 구조체는 적어도 하나의 멤버를 포함하고 있어야 한다. 구조체의 멤버는 객체와 동일해야 한다.   


    구조체-구조체 : 대상구조체는 원본구조체보다 크면 안된다. 각각의 원본과 대상 구성요소간의 형변환 가능성에 달려있다. 

     

    1.11 변수(Variables)
    변수는 다음과 같이 선언한다.
    [static uniform extern shared volatile] [const] 
    type id [array_suffix] [: semantic] [= initializers] [annotations] [, id ...] ; 

     

    변수 선언 앞에 static명령어를 붙일 수 있는데, 전역변수에 붙으면 이 변수가 셰이더 외부에서 접근할수 없다는 뜻이고, 지역변수에 사용되면 함수호출뒤에서 계속 값이 유효하다는 것이다. 지역 static변수는 오직 한번만 초기화되며, 명시적인 초기값이 없다면 0으로 가정한다.

     

    • 전역 변수 선언 앞에 uniform을 붙이면, 셰이더에게 uniform입력임을 나타내주는 것이며, 모든 static이 아닌 전역변수는 uniform이다.
    • 전역 변수 선언 앞에 extern을 붙이면, 외부입력 변수라는 뜻이며, 모든 static이 아닌 전역변수는 extern이다.
    • 전역 변수 선언 앞에 shared를 붙이면, effect프레임워크로 하여금 effect들간에 공유되는 변수로 다루도록 해준다.
    • 전역 변수 선언 앞에 volatie을 붙이면, effect프레임워크에 이 변수가 매우 빈번하게 바뀐다는 것을 알려주는 것이다.

     

    초기값은 표현식이나 { expression [, expression [, ...]] }형태 일수 있는데, 전역 extern변수는 리터럴(literal)값이어야 하며, 다른 전역변수나 static지역변수는 상수이어야 한다.

     

    변수는 의미자(semantic)가 붙을 수 있는데, 의미자는 언어적으로는 아무런 의미가 없지만, 
    변수와 결합하여 하드웨어에 전달된다. 대소문자 구별도 없다. 의미자의 효용성과 의미는 사용자가 정의하는 기능에 달려있다. 예를 들어, 정점셰이더라면 입력과 출력 레지스터를 하드웨어에 맵핑하는데 사용된다.

     

    전역 변수는 설명문(annotation)을 가질 수 있는데, 설명문은 { member_list }형태로 되어 있으며, 각 member_list의 멤버들은 리터럴 값으로 초기화 된다. 설명문은 effect와 전달인자를 교환하기위한 방법일 뿐이며, 프로그램안에서 참조할 수 없다. 또한 설명문의 멤버변수들은 의미자를 가질 수 없다.

     

    1.12 구성성분 접근과 뒤섞기(Component Access and Swizzles)
    기본 자료형의 부동소수 성분은 다음의 이름표 첨자를 사용해서 구조체의 멈버변수 처럼 각각 접근할수 있다.
     
    _11, x, r _12, y, g _13, z, b _14, w, a    
    _21       _22         _23        _24    
    _31       _32         _33        _34    
    _41       _42         _43        _44 

     

    벡터의 특정 성분들은 두 세개의 첨자를 조합하여 접근 할 수 있다. 
    bgr, yyzw, _12_22_32_42 

     

    모든 이름들은 같은 첨자 집합을 사용해야 한다. 첨자집합은 섞어서 사용할 수 없으나, 같은 성분을 연속해서 사용할 수는 있다.

    2절 문장과 식(statements & expressions)
    HLSL에서 식(expression)은 변수와 리터럴, 연산자의 나열이며, 문장(statement)은 평가(evaluation)될 식의 순서를 결정한다.

     

    2.1 식(Expression)
    식은 리터럴과 변수를 연산자로 조합한 것이다.
    변수 : 리터럴 리터럴이란 정수를 나타내는 숫자 1이나 , 부동소수를 나타내는 숫자 2.1처럼 명시된 자료값을 말하며, 주로 변수에 값을 대입할 때 사용된다    
    연산자 :

    - 1절의 자료형 항목을 참고    
    - 연산자란 리터럴 혹은 변수들이 어떻게 결합, 비교,선택 될 것인지를 결정하며, 다음과 같은 연산자들이 있다.    
    -  대입 =, +=, -=, *=, /=     
    -  단항 !, -, +    
    -  산술연산 +, -, *, /, %     
    -  불린 &&, ||, ?:    
    -  비교 <, >, ==, !=, <=, >=    
    -  전위,후위 ++, --    
    -  형변환 (type)    
    -  콤마 ,    
    -  구조체멤버 선택 .    
    -  배열멤버 선택 [i]  
    [표 2-1] 식에 사용되는 리터럴,변수,연산자

     

    많은 수의 연산자들이 성분별(per component)로 지원된다. 즉, 변수의 각 구성성분별로 연산이 수행된다는 얘기다. 예를 들어서, 구성성분이 하나뿐인 변수는 연산이 한번만 일어나지만, 4개짜리 변수는 4번 연산이 일어나게 된다.

     

    2.1.1 대입연산자(Assignment Operators)
    연산자 : =, +=, -=, *=, /=

     

    변수에는 리터럴 값이 대입될 수 있다. 
    int i = 1;            
    half h = 3.0;      
    float f2 = 3.1f; 
    bool b = false;
    string str = "string"; 

     

    변수에는 수학적 연산의 결과값이 대입될 수 있다. 
    int i1 = 1;
    i1 += 2;           // i1 = 1 + 2 = 3 

     

    변수는 등호기호(=)의 양쪽에 사용될 수 있다. 
    float f3 = 0.5f;
    f3 *= f3;          // f3 = 0.5 * 0.5 = 0.25 

     

    부동소수로 나누면 나머지 연산에 아무런 문제가 생기지 않는다. 
    float f1 = 1.0;
    f1 /= 3.0f;        // f1 = 1.0/3.0 = 0.333 

     

    그러나, 나누어질 숫자가 정수일 경우에는 주의를 요한다. 특히 버림값이 결과에 영향을 미칠때는 말이다. 다음 예제는 앞의 예제와 같지만, 자료형이 다르기 때문에, 버림에 의해서 전혀 다른 결과가 나오게 된다. 
    int i1 = 1;
    i1 /= 3;           // i1 = 1/3 = 0.333, 소수점 이하를 버리므로 결국 0 

     

    2.1.2 단항 연산자(Unary Operators)
    연산자 : !, -, +

     

    단항 연산자는 단일한 피연산자에 대해서 작용한다. 
    bool b = false;
    bool b2 = !b;      // b2 = true
    int i = 2;
    int i2 = -i;       // i2 = -2
    int j = +i2;       // j = +2 


    2.1.3 산술 연산자(Additive & Multiplicative Operators)
    연산자 : +, -, *, /, %


    int i1 = 1;
    int i2 = 2;
    int i3 = i1 + i2;  // i3 = 3
    i3 = i1 * i2;        // i3 = 1 * 2 = 2  
      
    i3 = i1/i2;       // i3 = 1/3 = 0.333. i3이 정수이므로 0으로 버려짐
    i3 = i2/i1;       // i3 = 2/1 = 2  

      
    float f1 = 1.0;
    float f2 = 2.0f;
    float f3 = f1 - f2; // f3 = 1.0 - 2.0 = -1.0
    f3 = f1 * f2;         // f3 = 1.0 * 2.0 = 2.0  

      
    f3 = f1/f2;        // f3 = 1.0/2.0 = 0.5
    f3 = f2/f1;        // f3 = 2.0/1.0 = 2.0 

     

    잉여 연산자(modulus operator) %는 나눗셈의 나머지 값을 반환한다. 
    이 연산의 결과는 정수와 부동소수일 때의 결과가 다르다. 
    int i1 = 1;
    int i2 = 2;
    i3 = i1 % i2;      // i3 = 1/2의 나머지는 1
    i3 = i2 % i1;      // i3 = 2/1의 나머지는 0
    i3 = 5 % 2;        // i3 = 5/2의 나머지는 1
    i3 = 9 % 2;        // i3 = 9/2의 나머지는 1  

      
    f3 = f1 % f2;      // f3 = 1.0/2.0의 나머지는 0.5
    f3 = f2 % f1;      // f3 = 2.0/1.0의 나머지는 0.0

     

    % 연산자는 양변이 양수이거나 양변이 음수일 때만 정의되며, C와는 다르게, 정수, 부동소수 모두에 대해서 작동한다.

     

    2.1.4 불린 연산자(Boolean Math Operators)
    연산자 : &&, ||, ?:


    bool b1 = true;
    bool b2 = false;
    bool b3 = b1 && b2 // b3 = true AND false = false
    b3 = b1 || b2      // b3 = true OR false = true 

     

    C언어는 &&, ||, ?: 평가 시에 short-circuit을 사용 하는 반면, HLSL은 벡터연산이기 때문에 short-circuit을 사용 하지 않고, 양변의 식이 항상 모두 평가 된다.

     

    <여기서 잠깐> short-circuit
    short-circuit이란 한쪽 변을 먼저 평가하여 결과를 내는 방식이다.
    ( a || b ) 라는 경우를 생각해보자. 만약 a가 TRUE라면 b의 값이 무엇이던 ( a || b )는 항상 TRUE일 것이다. 반대로 ( a && b )를 생각해보면, a가 FALSE라면 b의 값과 무관하게 ( a && b )는 반드시 FALSE이다. 이처럼 한쪽 변의 결과값을 기준으로 빠르게 판정을 내리는 방식을 short-circuit이라 한다.
    </여기서 잠깐>

     

    불린 연산자는 구성요소별로 작동한다. 예를들어, 2개의 벡터를 비교한다면, 이 연산의 결과는 각 구성요소들을 비교한 결과값이 들어있는 벡터이다.

     

    불린 연산자를 사용하는 식은 각 변수의 크기와 구성요소의 형이 같도록 늘어난다. 늘어나는 형은 연산이 발생할 크기와 식의 결과값에 따르게 되는데, 예를 들어서 int3 + float식은 float3+float3로 늘어나게 되고, 결과값도 float3가 된다.

     

    2.1.5. 비교 연산자(Comparison Operators)
    연산자 : <, >, ==, !=, <=, >=

     

    스칼라 값의 크거나(작거나)를 비교한다. 
    if( dot(lightDirection, normalVector)  >  0 )
       // 면에 조명처리

    if( dot(lightDirection, normalVector)  <  0 )
       // 면이 뒷면 

     

    혹은, 스칼라 값의 같음(같지 않음)을 비교한다. 
    if(color.a  == 0)
       // 면이 보지이 않으므로 처리할 것 없음

    if(color.a  != 0)
       // 알파값으로 두개의 색을 섞음
     

    혹은, 스칼라 값의 크거나 같음(작거나 같음)을 비교한다.
    if( position.z >= oldPosition.z )
       // 새로운 면이 현재 면보다 뒤에 있으므로 처리 생략

    if( currentValue <= someInitialCondition )
       // 현재 값을 초기상태로 되돌림 

     

    어떠한 스칼라 자료형도 비교 가능하다. 그러나, 비교 연산자는 벡터,행렬,객체형 등의 복합자료형(complex data type)은 지원하지 않는다.

     

    2.1.6 전위, 후위 연산자(Prefix, Postfix Operators)
    연산자 : ++, --
    전위 연산자는 식이 평가되기 전에 변수의 값을 바꾸고, 후위연산자는 식을 평가한 다음에 값을 바꾼다.

    다음 예제는 반복횟수를 세기 위해서 반복문에서 i값을 사용한다.
    float4 arrayOfFloats[4] = { 1.0f, 2.0f, 3.0f, 4.4f };

    for (int i = 0; i < 4; )
    {
        arrayOfFloats[i++] *= 2; 

     

    후위 증가 연산자 ++가 사용되었으므로, arrayOfFloats[i]가 i값이 증가하기 전에 2가 곱해진다. 이것을 전위연산자로 똑같이 구현하면 약간 복잡해 진다. 
    float4 arrayOfFloats[4] = { 1.0f, 2.0f, 3.0f, 4.4f };

    for (int i = 0; i < 4; )
    {
        arrayOfFloats[++i - 1] *= 2; 

     

    전위 증가 연산자 ++가 사용되었으므로, arrayOfFloats[i+1 - 1]는 i가 증가한 후에 2가 곱해진다.

     

    전위 감소 와 후위 감소 연산자 --도 증가 연산자와 똑같이 사용하면 된다. 다만, 증가 연산자는 1을 더하는데 반해서 후위연산자는 1을 감소 시킨다는 점이 다를 뿐이다.

     

    2.1.7 형변환 연산자(Cast Operator)
    괄호안에 있는 자료형의 명은 명시적 형변환이다.
    형변환은 원래의 식을 변환하려는 형으로 변환한다. 
    일반적으로 간단한 자료형은 복합 자료형으로 형변환 될 수 있다. – 승격 형변환(promotion cast)
    그러나, 일부분의 복합 자료형만이 간단한 자료형으로 형변환 될 수 있다. – 격하 형변환(demotion cast)
    형변환이 가능한 경우에 대해서는 1절을 참고하도록 하자.

     

    2.1.8 콤마 연산자(Comma Operator)
    콤마 연산자는 하나이상의 식을 분할하여 순서대로 평가되도록 하며, 마지막 식의 값이 전체 식의 값이 된다.

     

    매우 재미있는 예제를 하나 보도록 하자.

     

    1. 다음 식에서 실수로 등호(=) 우측의 float4를 빼버리면 어떻게 될까? 
    // 여기서 우측의 float4는 생성자 역할을 한다.
    float4 x = float4(0,0,0,1);  

     

    2. 아마도, 다음과 같은 모양이 될 것이다. 문제는 여기서부터 발생한다.
    컴파일러는 (0,0,0,1)을 단순히 콤마가 3개인 식으로 평가하게 된다. 
    float4 x = (0,0,0,1);  

     

    3. 콤마 연산자는 왼쪽에서 오른쪽으로 식을 평가한다. 결국 최종적으로 다음의 식으로 변화된다. 맙소사! 
    float4 x = 1;  

     

    4. HLSL은 이 경우 스칼라 승격을 사용한다. float4 x의 구성요소에 모두 1을 넣는 것이다. 결국 다음 소스와 동일한 코드가 된다. 
    float4 x = float4(1,1,1,1); 

     

    이 예제에서 보다시피, 등호 우측의 float4를 빼는 것은 결과적으로 치명적인 오류가 된다. 왜냐면 float4 x = (0,0,0,1); 도 분명히 문법적으로 적법한 문장이기 때문이다.

     

    2.1.9 구조체 연산자(Structure Operator)
    연산자 : .


    struct position
    {
    float4 x;
    float4 y;
    float4 z;
    }; 

     

    이것은 다음과 같이 사용한다. 
    struct position pos = { 1,2,3 };

    float 1D_Float = pos.x
    1D_Float = pos.y 

     

    각각의 멤버는 구조체 연산자를 사용해서 읽고 쓸 수 있다. 
    struct position pos = { 1,2,3 };
    pos.x = 2.0f;
    pos.z = 1.0f;       // z = 1.0f
    pos.z = pos.x      // z = 2.0f 

     

    2.1.10 배열 연산자(Array Operator)
    연산자 : [i]
    배열 멤버 선택 연산자 [i]는 배열에서 하나 혹은 그 이상의 배열요소를 선택한다. 이때, []안의 첨자는 0부터 시작하는 정수이다.
    int arrayOfInts[4] = { 0,1,2,3 };
    arrayOfInts[0] = 2;
    arrayOfInts[1] = arrayOfInts[0]; 

     

    배열 연산자를 벡터에 접근할때도 사용된다. 
    float4 4D_Vector = { 0.0f, 1.0f, 2.0f, 3.0f  };         
    float 1DFloat = 4D_Vector[1];          // 1.0f 

     

    추가적인 첨자를 붙여서 행렬에 접근할 수도 있다. 
    float4x4 mat4x4 = {{0,0,0,0}, {1,1,1,1}, {2,2,2,2}, {3,3,3,3} };
    mat4x4[0][1] = 1.1f;
    float 1DFloat = mat4x4[0][1];      // 0.0f  
    첫번째 첨자는 0부터 시작하는 행첨자이며, 두번째 첨자는 0부터 시작하는 렬첨자 이다.

     

    2.1.11 연산자 우선순위
     
    연산자 사용 방법 의미 결합법칙    
    () (value) 부분식 왼쪽에서 오른쪽    
    () id(arguments) 함수의 호출 왼쪽에서 오른쪽    
     type(arguments) 형 생성자    왼쪽에서 오른쪽    
    [] array[int] 배열의 첨자 왼쪽에서 오른쪽    
    .  structure.id 멤버의 선택 왼쪽에서 오른쪽    
     value.swizzle 성분의 교체 왼쪽에서 오른쪽    
    ++ variable++ 후위 증가 (성분마다) 왼쪽에서 오른쪽    
    -- variable-- 후위 감소 (성분마다) 왼쪽에서 오른쪽    
    ++ ++variable 전위 증가 (성분마다) 오른쪽에서 왼쪽    
    -- --variable 전위 감소 (성분마다) 오른쪽에서 왼쪽    
    !  ! value 논리 NOT (성분마다) 오른쪽에서 왼쪽    
    - -value 단항 마이너스 (성분마다) 오른쪽에서 왼쪽    
    + +value 단항 플러스 (성분마다) 오른쪽에서 왼쪽    
    () (type) value 형 변환 오른쪽에서 왼쪽    
    * value*value 곱셈 (성분마다) 왼쪽에서 오른쪽    
    / value/value 나눗셈 (성분마다) 왼쪽에서 오른쪽    
    % value%value 잉여 (성분마다) 왼쪽에서 오른쪽    
    + value+value 덧셈 (성분마다) 왼쪽에서 오른쪽    
    - value-value 뺄셈 (성분마다) 왼쪽에서 오른쪽    
    < value < value 비교 : 보다 작은 (성분마다) 왼쪽에서 오른쪽    
    > value > value 비교 : 보다 큰 (성분마다) 왼쪽에서 오른쪽    
    <= value <= value 비교 : 이하 (성분마다) 왼쪽에서 오른쪽    
    >= value >= value 비교 : 이상 (성분마다) 왼쪽에서 오른쪽    
    == value == value 비교 : 동일한 (성분마다) 왼쪽에서 오른쪽    
    != value != value 비교 : 동일하지 않은 (성분마다) 왼쪽에서 오른쪽    
    && value && value 논리 AND (성분마다) 왼쪽에서 오른쪽    
    || value||value 논리 OR (성분마다) 왼쪽에서 오른쪽    
    ? : float? value:value 조건 오른쪽에서 왼쪽    
    = variable=value 대입 (성분마다) 오른쪽에서 왼쪽    
    *= variable*=value 곱셈 대입 (성분마다) 오른쪽에서 왼쪽    
    /= variable/=value 나눗셈 대입 (성분마다) 오른쪽에서 왼쪽    
    %= variable%=value 잉여 대입 (성분마다) 오른쪽에서 왼쪽    
    += variable+=value 덧셈 대입 (성분마다) 오른쪽에서 왼쪽    
    -= variable-=value 뺄셈 대입 (성분마다) 오른쪽에서 왼쪽    
    , value, value 콤마 왼쪽에서 오른쪽 

     

    2.2 문장(statement)
    문장 블럭은 하나 이상의 문장으로 이루어진 그룹으로서, {}으로 감싼 블럭이다.
    {
       statement 1;
       statement 2;
       ...
       statement n;
     
    문장 블럭이 단일문장이라면 {}을 써도 되고 안써도 된다.

     

    또한 if문 다음에 한 줄짜리 문장이 온다면, {}으로 감싸는 것은 전적으로 개인의 자유이다. 
    if( some expression)
       color.rgb = tex3D(Sampler, texturecoordinates);  

    다음의 코드는 앞의 코드와 완전히 동일하다. 
    if( some expression)
    {
       color.rgb = tex3D(Sampler, texturecoordinates);

    단, 블록 안에 선언된 변수는 그 블록 안에서만 유효하다는 것을 잊지 말자.

     

    2.2.1 return 문(Return Statement)
    return문은 함수의 끝을 나타낸다.

     

    다음 코드는 가장 간단한 return문으로, 제어권을 호출자에게로 넘겨준다. 이 코드는 아무런 값도 반환하지 않는다. 
    void main()
    {
        return ;

     

    return 문은 하나 이상의 값을 반환 할 수 있는데, 다음 코드는 리터럴 값을 반환하는 예이다. 
    float main( float input : COLOR0) : COLOR0
    {
        return 0;

     

    다음 예제는 식의 스칼라 값을 반환한다. 
    return  light.enabled = true ; 

     

    다음 예제는 지역변수와 리터럴 값을 사용해서 생성한 float4를 반환한다.
    return  float4(color.rgb, 1) ; 

     

    다음 예제는 내부 함수로부터 반환된 값과 리터럴값으로 생성한 float4를 반환한다.
    float4 func(float2 a: POSITION): COLOR
    {
        return float4(sin(length(a) * 100.0) * 0.5 + 0.5, sin(a.y * 50.0), 0, 1);

     

    다음 예제는 하나 이상의 멤버를 가진 구조체를 반환한다. 
    float4x4 WorldViewProj;

    struct VS_OUTPUT
    {
        float4 Pos  : POSITION;
    };

    VS_OUTPUT VertexShader_Tutorial_1(float4 inPos : POSITION )
    {
        VS_OUTPUT out;
        out.Pos = mul(inPos, WorldViewProj );
        return out;
    }; 

     

    2.2.2 흐름제어 문(Flow-Control Statements)
    흐름제어 문은 다음 번에 실행된 문장이 어느 것인지를 결정 것으로, if, do for, while등이 있다.

     

    2.2.2.1 if문
    if문은 비교의 결과값에 근거해서 다음 번에 실행될 문장을 정한다. 
    if ( (Normal dot LightDirection) > 0 ) 
    {
        // 면에 조명처리, 확산광 색깔을 더할 것
        ...  

     

    문장 블럭은 하나 이상의 문장으로 이루어 졌으며 {}으로 감싼 영역을 말하는데, 셰이더 명령슬롯의 크기를 넘지만 않는다면 얼마든지 커도 상관없다.

     

    if문은 선택적으로 else블럭을 가질 수 있는데, if식이 참이라면 if문과 연계되어 있는 블럭이 처리되고, 그렇지 않다면 else에 연계된 블럭이 처리된다.

     

    2.2.2.2 do 문
    do문은 문장블럭을 실행 한 뒤 상태식(conditional expression)을 평가해서 문장블럭을 다시 실행할지 여부를 결정한다. 이 과정은 상태식의 값이 false가 될 때까지 반복된다. 
    do  
    {
      // one or more statements
      color /= 2;
    } while ( color.a > 0.33f ) 

     

    앞의 코드는 색깔 성분들을 2로 나누고나서 알파성분은 검사해서 반복할지 여부를 결정한다. 알파성분이 0.33f보다 크다면 색깔값의 성분들을 계속 반으로 나누는 과정을 반복하다가 알파성분이 0.33f보다 작아지면 비교식의 값이 거짓이 되어, 프로그램은 다음 번 명령으로 진행하게 된다.

     

    이 코드는 한 줄짜리 문장블럭을 사용했지만, 다음과 같이 여러 줄짜리 문장 블럭을 사용해도 된다. 
    do  {
        color.r /= 2;
        color.g /= 4;
        color.b /= 8;
        ....  // Other statements
        color.a /= 8; 
    }  while ( color.a > 0.33f ) 

     

    이 코드에서 알파값을 변화시키는 것을 잊으면 안된다. 만약 그걸 까먹었다가는 이 코드가 무한루프에 빠질 것이고, 셰이더 실행에 막대한 지장을 줄 것이다.

     

    2.2.2.3 for문
    for문은 정해진 횟수만큼 문장블럭을 반복하는 정적인 제어를 제공해 준다.

     

    for문에는 초기식, 비교식, 증가식이 포함되며 문장블럭이 뒤따른다. 
    for ( int i = 0; i < 2; i++)
    {
       // 하나 이상의 문장블럭
       ...
       문장 n;
    }; 

     

    다음 예제는 4개의 다른 텍스처 좌표에서 텍스처를 읽어들이기 위해서 for반복문을 사용 하였다. 
    sampler RenderTarget;
    float4 textureCoordinates[4]; 
    float4 outColor[4];

    for(int i = 0; i < 3; i++)
    {
        outColor[i] = tex2D(RenderTarget, textureCoordinates[i]);

     

    문장 블럭은 비교식이 true일 때마다 반복해서 실행된다.

     

    2.2.2.4 while문
    while문은 식을 평가하여 문장블럭을 실행할지 여부를 판단해서 반복한다.
    while ( color.a > 0.33f )
    {
        color /= 2; 
     
    이 코드는 알파성분이 0.33f보다 큰지 확인한 다음, 참이면 절반으로 나눈다. 알파값이 0.33f보다 작아지면 비교식이 거짓이 되어 프로그램은 다음 문장을 실행하게 된다.

     

    다음 코드는 한줄짜리 문장이며, 한줄짜리 문장은 {}을 사용할지 여부를 선택할 수 있다. 
    while ( color.a > 0.33f )
        color /= 2;  

     

    문장 블럭은 n개로 확장할 수 있다. 
    while ( color.a > 0.33f )
    {
        color.r /= 2;
        color.g /= 4;
        color.b /= 8;
        ....  // Other statements
        color.a /= 8; 
     
    알파값을 바꾸는 것을 잊지 말자. 무한루프에 빠질 수 있다.

     

    3절 셰이더 의미자(shader semantics)부터는 다음에....


    3절 셰이더 의미자(shader semantics)
    3절에서는 다음과 같은 내용들을 살펴 볼 것이다.

    • 정점셰이더 의미자(Vertex Shader Semantics)
    • 픽셀셰이더 의미자(Pixel Shader Semantics)
    • 명시적 레지스터 결합(Explicit Register Binding)

    3.1 정점 셰이더 의미자(Vertex Shader Semantics)
    HLSL 의미자의 역할은 셰이더 데이터의 입력과 출력을 식별하는 것이다. 의미자는 3가지 경우에 나타난다.

    • 구조체 멤버 뒤에
    • 함수의 입력 전달인자 리스트들 중의 전달인자 뒤에
    • 함수의 입력 전달인자 리스트 뒤에

    다음 예제는 하나이상의 셰이더 입력을 위해서 구조체를 사용한다. 그리고, 셰이더 출력을 위해서 다른 구조체를 사용하는데, 이들 구조체 멤버들이 의미자를 사용하고 있다.


    vector vClr;

    struct VS_INPUT
    {
        float4 vPosition : POSITION;
        float3 vNormal : NORMAL;
        float4 vBlendWeights : BLENDWEIGHT;
    };

    struct VS_OUTPUT
    {
        float4  vPosition : POSITION;
        float4  vDiffuse : COLOR;

    };

    float4x4 mWld1;
    float4x4 mWld2;
    float4x4 mWld3;
    float4x4 mWld4;

    float Len;
    float4 vLight;

    float4x4 mTot;

    VS_OUTPUT VS_Skinning_Example(const VS_INPUT v, uniform float len=100)
    {
        VS_OUTPUT out;

        // 스킨 위치(월드 공간으로)
        float3 vPosition = 
            mul(v.vPosition, (float4x3) mWld1) * v.vBlendWeights.x +
            mul(v.vPosition, (float4x3) mWld2) * v.vBlendWeights.y +
            mul(v.vPosition, (float4x3) mWld3) * v.vBlendWeights.z +
            mul(v.vPosition, (float4x3) mWld4) * v.vBlendWeights.w;


        // 스킨 법선(월드 공간으로)
        float3 vNormal =
            mul(v.vNormal, (float3x3) mWld1) * v.vBlendWeights.x + 
            mul(v.vNormal, (float3x3) mWld2) * v.vBlendWeights.y + 
            mul(v.vNormal, (float3x3) mWld3) * v.vBlendWeights.z + 
            mul(v.vNormal, (float3x3) mWld4) * v.vBlendWeights.w;
        
        // 출력할 것들
        out.vPosition    = mul(float4(vPosition + vNormal * Len, 1), mTot);
        out.vDiffuse  = dot(vLight,vNormal);

        return out;

    입력 구조체는 정점버퍼로부터 셰이더 입력이 될 데이터를 식별한다.
    앞서 소개한 코드의 셰이더는 정점버퍼로부터 들어오는 입력을 POSITION, NORMAL, BLENDWEIGHT요소로 구별해서 셰이더 레지스터에 맵핑(연결)한다.

    HLSL의 입력 데이터는 정점선언 데이터형과 완벽하게 일치할 필요는 없는데, 데이터형이 맞지 않을 경우, HLSL에서 지정된 형으로 자동변환 되기 때문이다. 예를 들자면, 프로그램에서 UINT로 정의된 NORMAL형은 셰이더에서 읽혀질때는 float3형으로 변환된다는 것이다.

    정점 스트림의 데이터가 셰이더에 선언된 데이터형보다 적을 경우, 없는 성분들은 0으로 초기화된다. 단 w성분은 예외적으로 1로 초기화 된다.

    입력 의미자는 고정함수 파이프라인의 D3DDECLUSAGE 열거형 값과 비슷하다고 생각하면 된다.

    출력 구조체는 정점 셰이더 출력 인자를 식별한다. 이 예제의 경우에는 위치값과 색깔값을 사용한다. 이들 출력값은 삼각형 레스터라이즈에 사용될 것인데, POSITION이라고 표기된 것이 스크린 좌표계에서의 좌표이다. 스크린 좌표계에서는 -1과 +1이 뷰포트에서의 x,y좌표의 최소,최대 값이며, z값은 z버퍼 테스트에 사용된다. 그리고, 어떠한 정점셰이더도 반드시 POSITION값만큼은 출력해야 한다.

    출력 의미자도 고정함수 파이프라인의 D3DDECLUSAGE 열거형 값과 비슷하다고 생각하면 된다. 일반적으로 정점셰이더의 출력 구조체는 픽셀 셰이더의 입력 구조체로 사용된다.
    픽셀셰이더의 경우에는 POSITION, PSIZE, FOG으로 연결된 값들은 읽을 수가 없는데, 만약, 이들 값들이 필요할 경우에는 픽셀 셰이더의 다른 출력 레지스터에 복사하여 의미자를 다른 형식(TEXCOORDn등)으로 사용하면 된다.


    3.1.1 정점 셰이더 입력 의미자(Vertex Shader Input Semantics)

    다음과 같은 입력 의미자가 있다.
     
    POSITION[n] 위치(position)    
    BLENDWEIGHT[n] 결합가중치(blend weight)    
    BLENDINDICES[n] 결합인덱스(blend index)    
    NORMAL[n] 법선(normal)    
    PSIZE[n] 점크기(point size)    
    COLOR[n] 확산,반사광(diffuse specular color)    
    TEXCOORD[n] 텍스처 좌표(texture coordinate)    
    TANGENT[n] 접선(tangent)    
    BINORMAL[n] 종법선(binormal)    
    TESSFACTOR[n] 분할 인수(tessellation factor)  
    n은 0부터 시작하는 정수로서 지원되는 레지스터의 숫자이다. 예) PSIZE0, COLOR1, 등

    3.1.2 정점 셰이더 출력 의미자(Vertex Shader Output Semantics)
    다음과 같은 입력 의미자가 있다.


    POSITION 위치(position)    
    PSIZE 점크기(point size)    
    FOG 정점 안개(vertex fog)    
    COLOR[n] 색깔(Color) 예) COLOR0    
    TEXCOORD[n] 텍스처 좌표(texture coordinate) 예) TEXCOORD0  
    n은 0부터 시작하는 정수로서 지원되는 레지스터의 숫자이다. 예) TEXCOORD0, TEXCOORD1, 등

    3.2 픽셀 셰이더 의미자(Pixel Shader Semantics)
    HLSL 의미자의 역할은 셰이더 데이터의 입력과 출력을 식별하는 것이다. 의미자는 3가지 경우에 나타난다.

    • 구조체 멤버 뒤에
    • 함수의 입력 전달인자 리스트들 중의 전달인자 뒤에
    • 함수의 입력 전달인자 리스트 뒤에

    다음 예제는 정점셰이더 출력과 픽셀셰이더 입력으로 같은 구조체를 사용한다. 픽셀셰이더는 색깔값을 반환한다는 것을 나타내기 위해서, 함수의 전달인자의 뒤에 의미자를 사용하고 있다.
     
    struct VS_OUTPUT
    {
        float4 Position  : POSITION;
        float3 Diffuse : COLOR0;
        float3 Specular : COLOR1;               
        float3 HalfVector : TEXCOORD3;
        float3 Fresnel : TEXCOORD2;               
        float3 Reflection : TEXCOORD0;               
        float3 NoiseCoord : TEXCOORD1;               
    };

    float4 PixelShader_Sparkle(VS_OUTPUT In) : COLOR
    {   
        float4 Color = (float4)0;
        float4 Noise = tex3D(SparkleNoise, In.NoiseCoord);
        float3 Diffuse, Specular, Gloss, Sparkle;

        Diffuse   = In.Diffuse * Noise.a;
        Specular  = In.Specular;
        Specular *= Specular;
        Gloss     = texCUBE(Environment, In.Reflection) * saturate(In.Fresnel);
        Sparkle   = saturate(dot((saturate(In.HalfVector) - 0.5) * 2, 
      (Noise.rgb - 0.5) * 2));
        Sparkle  *= Sparkle;
        Sparkle  *= Sparkle;
        Sparkle  *= Sparkle * k_s;

        Color.rgb = Diffuse + Specular + Gloss + Sparkle;
        Color.w   = 1;

        return Color;

    VS_OUTPUT으로부터 들어오는 모든 입력은 의미자를 가지고 있다. 이들 값들은 정점셰이더로부터 반환된 값이므로, 픽셀셰이더 입력으로 읽혀진다. 다른 3개의 입력은 프로그램에서 설정된 전역 uniform변수들인 Environment, SparkleNoise, k_s값으로, Environment와 SparkleNoise는 모두 프로그램에 의해서 생성되고 설정되는 텍스처들이며, k_s도 프로그램에 의해서 설정된 상수 레지스터이다.

    전역 변수들은 컴파일러에 의해서 자동적으로 레지스터에 할당된다. 전역변수들은 uniform전달인자로도 불리우는데, 그것은 이들 변수의 값이 셰이더가 호출될 때 처리되는 픽셀들에 대해서 항상 일정(uniform)하기 때문이다.
    상수테이블에 있는 레지스터들은 ID3DXConstantTable를 통해서 접근 가능하다.

    픽셀 셰이더의 입력 의미자는 정점셰이더와 픽셀셰이더의 값을 특정한 하드웨어에 연결(mapping)하게 되는데, 레지스터마다 특별한 성질이 있다. 현재까지는 오직 TEXCOORD와 COLOR의미자만이 유효하기 때문에 전달하려는 값이 텍스처좌표가 아니라 하더라도 TEXCOORD 의미자를 사용해야 한다.

    정점셰이더의 출력 구조체에는 POSITION이 있지만, 픽셀셰이더 입력에서는 이 값이 없음을 유념하자. HLSL은 픽셀셰이더에서 유효하지 않은 값이라도 정점셰이더에서 유효한 출력값이면 이를 허용하는데, 결과적으로는 픽셀셰이더에서 참조 되지 않는다.

    입력 인자는 배열일 수도 있다. 이 경우 의미자는 각 배열요소마다 자동적으로 컴파일러에 의해서 증가된다. 예를 들어 다음과 같은 명시적 선언을 생각해보자.
     
    struct VS_OUTPUT
    {
        float4 Position  : POSITION;
        float3 Diffuse : COLOR0;
        float3 Specular : COLOR1;               
        float3 HalfVector : TEXCOORD3;
        float3 Fresnel : TEXCOORD2;               
        float3 Reflection : TEXCOORD0;               
        float3 NoiseCoord : TEXCOORD1;               
    };

    float4 Sparkle(VS_OUTPUT In) : COLOR 

    여기서 이루어진 명시적 선언은, 다음과 같이 선언할 경우 컴파일러에 의해서 의미자가 자동적으로 붙기 때문에 동일한 선언이 된다.
     
    float4 Sparkle(float4 Position : POSITION,
                             float3 Col[2] : COLOR0,
                             float3 Tex[4] : TEXCOORD0) : COLOR0
    {
       // shader statements
       ... 

    입력 의미자처럼, 출력 의미자도 픽셀셰이더 출력데이터를 구분해 주는데, 대부분의 픽셀 셰이더는 출력값을 그냥 COLOR0로 하게 된다.

    또한, 픽셀 셰이더는 DEPTH0값과 여러개(최대 4개)의 렌더타겟도 가질수 있다. 다음 셰이더 코드는 색깔값과 깊이값을 모두 출력하고 있다.
     
    struct PS_OUTPUT
    {
        float4 Color[4] : COLOR0;
        float  Depth  : DEPTH;
    };

    PS_OUTPUT main(void)
    {
        PS_OUTPUT out;

       // 셰이더 문
       ...

      // 4개의 출력색깔 값을 쓴다.
      out.Color[0] =  ...
      out.Color[1] =  ...
      out.Color[2] =  ...
      out.Color[3] =  ...

      // Write pixel depth 
      out.Depth =  ...

        return out;

    픽셀셰이더의 출력 색깔값은 float4형식만 가능하다. 여러가지 색깔값을 사용하고 싶다면, 연속적으로만 사용 가능한데, 이 말은 COLOR1을 사용하고 싶다면 먼저 COLOR0값을 출력해야만 한다는 뜻이다. 그리고, 픽셀셰이더에서 출력하는 깊이값의 경우에는 float1형만 가능하다.

    3.2.1 픽셀 셰이더 입력 의미자(Pixel Shader Input Semantics)
    다음과 같은 의미자를 지원한다.
     
    COLOR[n] 확산,반사광 색깔  예) COLOR0, COLOR1    
    TEXCOORD[n] 텍스처 좌표  예) TEXCOORD0  
    n은 0부터 시작하는 정수로서 지원되는 레지스터의 숫자이다. 예) TEXCOORD0, TEXCOORD1, 등

    3.2.2 픽셀 셰이더 출력 의미자(Pixel Shader Output Semantics)
    다음과 같은 의미자를 지원한다.
     
    COLOR[n] 색깔    예) COLOR0    
    TEXCOORD[n] 텍스처좌표   예) TEXCOORD0    
    DEPTH[n] 깊이값    예) DEPTH0  
    n은 0부터 시작하는 정수로서 지원되는 레지스터의 숫자이다. 예) COLOR0, TEXCOORD1, DEPTH0, 등

    3.3 명시적 레지스터 결합(Explicit Register Binding)
    우리는 이미 컴파일러가 전역변수에 레지스터를 자동적으로 할당한다는 것을 알고있다. 그러나, 변수에 특정한 레지스터를 결합하도록 명시하는 것도 가능하다.

    다음과 같은 3개의 전역변수가 있을 경우,
     
    sampler Environment;
    sampler SparkleNoise;
    float4 k_s; 

    컴파일러는 자동적으로 Environment에 샘플러 레지스터 s0를, SparkleNoise에 샘플러 레지스터 s1를 할당하고, k_s에 상수레지스터 c0를 할당할 것이다.(이전에 할당된 레지스터들이 없다고 가정하자)

    그러나, 컴파일러에게 특정한 레지스터를 할당하도록 하려면 register(...)구문을 사용하면 된다.
     
    sampler Environment : register(s1);
    sampler SparkleNoise : register(s0);
    float4 k_s : register(c12); 

    이 경우 Environment는 s1샘플러 레지스터에, SparkleNoise는 s0샘플러 레지스터에, 마지막으로 k_s는 c12상수 레지스터에 할당될 것이다.


    4절 흐름 제어(Flow Control)

    대부분의 정점,픽셀 셰이더 하드웨어는 명령어를 한번씩만 수행하도록 선형적으로 디자인되어있다. 그러나, HLSL은 정적 분기(static branching), 프레디케이션 명령(predicated instructions), 정적 반복(static looping), 동적 분기(dynamic branching), 동적 반복(dynamic looping) 등의 흐름 제어를 지원한다.

    전에는 HLSL에서 if문을 사용하면 어셈블리 셰이더 코드에서 if문의 참/거짓의 경우를 모두 수행하되, 한쪽의 결과값만 남도록 컴파일되었다.

    다음 코드를 vs_1_1로 컴파일 해보자.
     
    if (Value > 0)
        Position = Value1; 
    else
        Position = Value2; 

    다음 코드가 앞의 코드를 vs_1_1로 컴파일해서 만들어진 어셈블리 코드이다.
     
    // 선형보간값 r0.w를 계산
    mov r1.w, c2.x               
    slt r0.w, c3.x, r1.w         
    // value1과 value2사이를 선형보간
    mov r7, -c1                      
    add r2, r7, c0                   
    mad oPos, r0.w, r2, c1 

    그러나, 어떤 하드웨어는 정적이나 동적 반복 중 하나만 지원하며, 선형적인 수행을 필요로 한다. 반복을 지원하지 않는 모델에서는 반복할 내용을 반복 회수만큼 전개(unroll)해서 컴파일 하게 된다. 이러한 경우의 좋은 예는 DirectX 9.0 SDK의 DepthOfField예제인데, 여기서는 ps_1_1에서도 작동하도록 전개(unroll)를 사용해서 컴파일 하고 있다.

    HLSL은 이제 다음과 같은 형태의 흐름제어도 포함되어 있다.

    • 정적 분기(static branching)
    • 프레디케이션 명령(predicated instructions)
    • 정적 반복(static looping)
    • 동적 분기(dynamic branching)
    • 동적 반복(dynamic looping)

    요즘 하드웨어에서 지원하는 대부분의 분기지원은 정적 분기이다. 정적분기는 불린 셰이더 상수에 의해서 특정 코드블럭이 실행될지 안될지를 결정하는 것이다. 이것은 현재 렌더링 되려는 오브젝트의 형태에 따라서 코드를 허용하거나 금지시킬 때 편리한 방법이다. 화면에 그려질 때, 현재 셰이더의 특정 기능을 사용할지를 결정 할 수 있는 것이다. HLSL에서는 분기를 허용하기 때문에서 불린 상수에 의해서 금지된 코드는 실행되지 않고 건너뛰게 된다.

    개발자에게 가장 친숙한 분기는 동적분기이다. 동적분기에서는 비교해야 할 상태가 변수에 들어있어서, 비교가 컴파일 시(compile time)에 일어나는 것이 아니라 실행 시(runtime)에 일어나게 된다. 수행능력은 "분기에 필요한 비용 + 분기에 의해서 실행된 명령에 따른 비용"으로 결정된다. 동적분기는 현재 정점셰이더에서 사용 가능하다.


    5절 함수(Functions)

    • 함수 선언(Function Declaration)
    • 함수 반환 형(Function Return Types)
    • 함수 명(Function Name)
    • 전달인자 리스트(Argument Lists)
    • 함수 몸체(Function Body)
    • 사용자 정의 함수(User-Defined Functions)

    함수는 큰 작업을 작은 작업들로 분할하며, 작은 작업은 디버깅과 재사용이 훨씬 용이하다. 또한, 함수는 함수자체의 자세한 내용을 숨기기 위해서도 사용되며, 프로그램은 이러한 함수들의 조합으로 이루어져있다.

    HLSL함수는 몇가지 면에서 C의 함수와 비슷하다. 일단, 함수는 '함수정의'와 '함수몸체'를 포함하고 있어야 하며, '반환형'과 '전달인자 리스트'를 선언해야 한다. C함수와 마찬가지로 HLSL도 셰이더 컴파일시에 문법의 적법성을 검사하기 위해 전달인자, 전달인자 형, 반환값을 체크한다.

    그러나, C함수와는 다르게, HLSL의 진입함수는 함수 전달인자와 셰이더 입출력값을 결합하기 위해서 의미자를 사용하는데, 결과적으로 '버퍼 데이터를 셰이더와 결합하는 것'과 '셰이더 출력값을 셰이더 입력값과 결합하는 것'을 쉽게해준다.(내부적으로 호출되는 HLSL함수는 의미자를 무시한다.)



    5.1 함수 선언(Function Declaration)
    함수는 선언부와 몸체로 이루어져 있는데, 선언부는 몸체보다 먼저 나와야 한다.
     
    float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION
    {
        return mul(inPos, WorldViewProj );

    함수 선언부에는 {}앞에 모든 것이 나와있어야 한다.
     
    float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION 

    함수 선언부에는 다음과 같은 것들이 포함되어 있다.
    • 반환 값 
    • 함수 명
    • 전달인자 리스트(선택적)
    • 출력 의미자(선택적)
    • 설명문(annotation)(선택적)

    5.2 함수 반환형(Function Return Types)
    반환형은 float4처럼 HLSL의 어떠한 기본 자료형도 가능하다.


    float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION
    {
       ...

    반환형은 미리 정의된 자료구조도 가능하다.
     
    struct VS_OUTPUT
    {
        float4  vPosition        : POSITION;
        float4  vDiffuse         : COLOR;
    };

    VS_OUTPUT VertexShader_Tutorial_1(float4 inPos : POSITION )
    {
       ...

    함수가 반환값이 없으면, 반환형을 void로 하면된다.
     
    void VertexShader_Tutorial_1(float4 inPos : POSITION )
    {
       ...

    반환형은 항상 함수 선언의 앞부분에 나타나야 한다.



    5.3 함수 명(Function Name)
    함수명은 함수의 반환형 다음에 나타나는 식별자이다.


    float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION 

    함수명은 VertexShader_Tutorial_1처럼 이해하기 쉽게 짓는 것이 좋다.



    5.4 전달인자 리스트(Argument Lists)
    전달인자 리스트 함수에게 입력 인자와 반환될 값을 선언할 수 있다. 어떤 인자를 입력과 출력 모두에 사용될 수도 있다.

    다음 예제는 4개의 입력 인자를 가진 셰이더이다.
     
    float4 Light(float3 LightDir : TEXCOORD1, 
                 uniform float4 LightColor,  
                 float2 texcrd : TEXCOORD0, 
                 uniform sampler samp) : COLOR 
    {
        float3 Normal = tex2D(samp,texcrd);

        return dot((Normal*2 - 1), LightDir)*LightColor;


    이 함수는 샘플링된 텍스처와 조명색깔을 혼합한 최종 색깔을 반환한다. 이 함수는 4개의 입력값이 있고, 그 중 2개는 의미자가 있는데, LightDir는 TEXCOORD1, texcrd는 TEXCOORD0이다. 이것은 이 변수들이 정점버퍼로부터 입력 된다는 뜻이다.
    LightDir가 TEXCOORD1의 의미자를 갖고 있지만, 이 전달인자는 아마도 텍스처 좌표가 아닐 것이다. TEXCOORDn의미자는 주로 미리 정의되어 있지 않은 자료형일 경우에 자주 사용되는데, 이 경우에도 정점셰이더 의미자에는 광원방향에 대한 것이 없으므로 TEXCOORD1을 사용한 것으로 이해할 수 있을 것이다.


    다른 2개의 입력인 LightColor와 samp는 uniform명령어가 있다. 그 얘기는 화면에 그려지는 중에 이 변수값이 바꾸지 않는 다는 뜻이다. 대부분 이런 전달인자는 셰이더 전역 변수로부터 전달된다.

    전달인자는 입력값의 경우 in명령어, 출력값일 경우 out명령어와 함께 사용할 수 있다. 전달인자는 참조에 의해서 전달될 수 없으나, inout명령어를 사용하면 입출력값으로 사용될 수 있다. inout명령어로 선언되어 함수로 전달되는 전달인자는 함수 반환전까지는 원본의 복사본으로 간주되다가, 되돌아 갈때 다시 복사된다.

    inout명령어를 사용한 예를 보도록 하자.
     
    void Increment_ByVal(inout float A, inout float B) 
    { 
        A++; B++;

    이 함수는 A와 B값을 증가시키고, 그 값을 반환한다.



    5.5 함수 몸체(Function Body)
    함수 몸체란 함수 선언다음에 나오는 모든 코드를 말하는 것으로, {}으로 둘러싸인 문장으로 구성되며, 변수, 리터럴, 식, 문장을 사용해서 구현한다.
     
    float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION
    {
        return mul(inPos, WorldViewProj );

    이 셰이더 몸체는 2가지 일을 하는데, 행렬곱셈과 float4를 반환하는 것이다. 여기서, 행렬곱셈은 mul함수를 사용해서 이루어진다. mul은 HLSL라이브러리 함수에 이미 구현되어져 있기때문에 내부함수(intrinsic function)라 부른다.

    이 코드의 행렬 곱셈은 입력벡터 Pos와 합성행렬인 WorldViewProj의 조합으로 이루어져있다. 결과적으로 위치값은 화면좌표계로 변환된다. 이것이 우리가 할수 있는 최소한의 정점셰이더 처리인 것이다. 만약 정점셰이더가 아닌 고정함수 파이프라인을 사용한다면 이 변환뒤에 정점데이터가 그려질 것이다.

    함수몸체의 제일 마지막 문장은 반환문으로, C와 마찬가지로 return문은 제어권을 이 함수를 호출한 곳으로 되돌려 준다.



    5.6 반환 형(Return Types)
    함수 반환형은 HLSL에 정의된 bool, int, half, float, double등의 어떠한 자료형도 가능하다. 또한 복합 자료형인 vector, matrix도 가능하지만, pixelshader, vertexshader, texture, and sampler같은 객체형은 반환형이 될 수 없다.

    구조체를 반환하는 자료형의 예를 보도록 하자.


    float4x4 WorldViewProj : WORLDVIEWPROJ;

    struct VS_OUTPUT
    {
        float4 Pos  : POSITION;
    };

    VS_OUTPUT VS_HLL_Example(float4 inPos : POSITION )
    {
        VS_OUTPUT Out;

        Out.Pos = mul(inPos,  WorldViewProj );

        return Out;



    5.7 사용자 정의 함수(User-Defined Functions)
    함수 정의는 C와 매우 비슷하다.


    [static inline target] [const] type id ( [parameter_list] ) ;
    [static inline target] [const] type id ( [parameter_list] ) { [statements] } ; 

    타겟이란 선택적인 식별자로 함수가 사용될 플랫폼을 지정한다.
    parameter_list는 하나 이상의 전달인자 선언들을 콤마(,)로 구분하여 나열한 것이다.


    [uniform in out inout] type id [: semantic] [= default] 

    전달인자 값은 항상 값에 의해서 전달된다.

    • in명령어가 사용된 전달인자는 사용하기 전에 먼저 복사되어야 한다는 것이다.
    • out명령어가 사용된 전달인자는 전달인자의 최종값이 복사되어, 함수를 호출한 곳으로 전달되어야 한다는 것이다.
    • inout명령어가 사용된 전달인자는 in과 out을 동시에 지정하는 것이다.
    • uniform명령어가 사용된 전달인자는 in명령어의 특별한 형태로, 최상위 함수일 경우 상수데이터가 전달인자로 전달된다는 것이다. 
    • 최상위 함수가 아닐 경우에는 in과 동일한 역할을 한다.
    • in,out,inout,uniform중 어떤것도 지정되지 않으면 in으로 간주한다.

    함수몸체가 없는 함수는 함수형태만 선언한 것으로 간주되며, 뒤에 몸체와 함께 반드시 재정의 되어야 한다. 만약 함수 몸체가 정의되지 않은 함수가 참조될 경우에는 오류가 발생한다.

    함수는 다중정의(overloading)될 수 있으며, 함수명, 전달인자 형, 타겟 플랫폼에 의해서 식별될 수 있다. 그러나, 함수 다중정의는 아직 구현되어 있지 않다.

    현재 모든 함수는 인라인(inline)이므로, 되부름은 지원되지 않는다.

    6절 내부함수(Intrinsic functions)
    내부함수란 HLSL컴파일러에서 지원하는 라이브러리 함수이다. 이미 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 으로 동일하지 않은 경우에서도, 원점 이


    외의 각 점에 대해서 잘 정의(well-defined)되어 있다.     


    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 의 쌍곡코사인(hyperbolic cosine)을 돌려준다.     


    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 를 라디안 단위로부터 도(degree)로 변환한다.     


    determinant determinant(m) 정방 행렬 m 의 행렬식을 돌려준다.     


    distance distance(a, b) 2 개의 점 a 와 b 간의 거리를 돌려준다.     


    dot dot(a, b) 2 개의 벡터 a 와 b 의 내적을 돌려준다.     


    exp exp(x) e 를 밑으로 하는 지수     


    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 / b 의 부동 소수 나머지(잉여) f를 돌려준다.

    단, i는 정수, f 는 x 와 부호가 같고, f의 절대값은 b 의 절대값보다 작아야 한다.    


    frac frac(x) 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) 밑이 e인 x 의 자연대수를 돌려준다. x가 음수(-)일 경우, 무한을 돌려준다. x 가 0일 경우, +INF를 돌려준다.     


    log10 log10(x) 밑이 10인 x 의 자연대수를 돌려준다. x가 음수(-)일 경우, 무한을 돌려준다. x 가 0일 경우, +INF를 돌려준다.     


    log2 log2(x) 밑이 2인 x 의 자연대수를 돌려준다. x가 음수(-)일 경우, 무한을 돌려준다. x 가 0일 경우, +INF를 돌려준다.     


    max max(a, b) a와 b중 큰 쪽을 선택한다.     


    min min(a, b) a와 b중 작은쪽을 선택한다.     


    modf modf(x, out ip) 값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 n 열의 경우, 결과차원은 n열x m행이다. 

    반응형
    반응형


    http://goo.gl/0BzAS

     DirectX Graphics  레퍼런스  이펙트 레퍼런스  인터페이스  ID3DXBaseEffect   [목차열람] [주소복사] [슬롯비우기]
    Microsoft DirectX 9.0

    ID3DXBaseEffect 인터페이스


    정수, 함수, 셰이더, 테크닉 등, 이펙트 파라미터의 취득 및 설정을 위한 메서드를 제공한다.

    ID3DXBaseEffect 멤버

    GetAnnotation어노테이션의 핸들을 얻어온다.
    GetAnnotationByName이름으로 참조해, 어노테이션의 핸들을 얻어온다.
    GetBool불리언 값을 얻어온다.
    GetBoolArray불리언 값의 배열을 얻어온다.
    GetDesc이펙트의 기술을 얻어온다.
    GetFloat부동 소수점값을 얻어온다.
    GetFloatArray부동 소수점값의 배열을 얻어온다.
    GetFunction함수의 핸들을 얻어온다.
    GetFunctionByName이름으로 참조해, 함수의 핸들을 얻어온다.
    GetFunctionDesc함수의 기술을 얻어온다.
    GetInt정수를 얻어온다.
    GetIntArray정수의 배열을 얻어온다.
    GetMatrix비전치행렬을 얻어온다.
    GetMatrixArray비전치행렬의 배열을 얻어온다.
    GetMatrixPointerArray비전치행렬의 포인터 배열을 설정한다.
    GetMatrixTranspose전치행렬을 얻어온다.
    GetMatrixTransposeArray전치행렬의 배열을 얻어온다.
    GetMatrixTransposePointerArray전치행렬의 포인터 배열을 설정한다.
    GetParameter최상정도 파라미터 또는 구조체 멤버 파라미터의 핸들을 얻어온다.
    GetParameterByName이름으로 참조해, 최상정도 파라미터 또는 구조체 멤버 파라미터의 핸들을 얻어온다.
    GetParameterBySemantic의미들로 참조해, 최상정도 파라미터 또는 구조체 멤버 파라미터의 핸들을 얻어온다.
    GetParameterDesc파라미터 또는 어노테이션의 기술을 얻어온다.
    GetParameterElement배열 요소 파라미터의 핸들을 얻어온다.
    GetPass패스의 핸들을 얻어온다.
    GetPassByName이름으로 참조해, 패스의 핸들을 얻어온다.
    GetPassDesc패스의 기술을 얻어온다.
    GetPixelShader픽셀 셰이더를 얻어온다.
    GetString캐릭터 라인을 얻어온다.
    GetTechnique테크닉의 핸들을 얻어온다.
    GetTechniqueByName이름으로 참조해, 테크닉의 핸들을 얻어온다.
    GetTechniqueDesc테크닉의 기술을 얻어온다.
    GetTexture텍스처를 얻어온다.
    GetValue임의의 파라미터 또는 어노테이션의 값을 얻어온다. 이것에는, 단순형, 구조체, 배열, 캐릭터 라인, 셰이더, 텍스처가 포함된다. ID3DXBaseEffect 의 Getxxx 형식의 거의 모든 호출의 대신으로서 이 메서드를 사용할 수 있다.
    GetVector벡터를 얻어온다.
    GetVectorArray벡터의 배열을 얻어온다.
    GetVertexShader정점 셰이더를 얻어온다.
    SetBool불리언 값을 설정한다.
    SetBoolArray불리언 값의 배열을 설정한다.
    SetFloat부동 소수점값을 설정한다.
    SetFloatArray부동 소수점값의 배열을 설정한다.
    SetInt정수를 설정한다.
    SetIntArray정수의 배열을 설정한다.
    SetMatrix비전치행렬을 설정한다.
    SetMatrixArray비전치행렬의 배열을 설정한다.
    SetMatrixPointerArray비전치행렬의 포인터 배열을 설정한다.
    SetMatrixTranspose전치행렬을 설정한다.
    SetMatrixTransposeArray전치행렬의 배열을 설정한다.
    SetMatrixTransposePointerArray전치행렬의 포인터 배열을 설정한다.
    SetPixelShader픽셀 셰이더를 설정한다.
    SetString캐릭터 라인을 설정한다.
    SetTexture텍스처를 설정한다.
    SetValue임의의 파라미터 또는 어노테이션의 값을 얻어온다. 이것에는, 단순형, 구조체, 배열, 캐릭터 라인, 셰이더, 텍스처가 포함된다. ID3DXBaseEffect 의 Setxxx 형식의 거의 모든 호출의 대신으로서 이 메서드를 사용할 수 있다.
    SetVector벡터를 설정한다.
    SetVectorArray벡터의 배열을 설정한다.
    SetVertexShader정점 셰이더를 설정한다.

    인터페이스의 정보

    상속받은곳IUnknown
    헤더d3dx9effect.h
    임포트 라이브러리d3dx9.lib
    최저한의 operating systemWindows 98

    참조

    D3DXCreateEffect

    반응형
    반응형




    HatchTest_060123_parkch.zip




    http://blog.naver.com/pch413/10001173790



    < 자작 해칭 예제 스크린샷 >

     

     

    아주~아주~아주~ 오래전에 제가 회사에서 세미나하고 올린

    해칭 관련 예제 포스트(http://blog.naver.com/pch413/7330265)를 보시고

    세부 구현이나 관련 자료를 문의하시는 분들이 가끔~가끔~ 계셨습니다.

    (사실 REAL-TIME RENDERING 2nd Edition 책 보면 다 나옵니다.)

     

    사실 자작 구현이라기 보다는 책에 있는 내용을 공부삼아 HLSL로 옮겨본 것 밖에 없고,

    귀찮기도 하고 소스가 별로 깔끔하지도 못하고 엉망이라서 공개를 꺼렸는데...

    이래저래 문의하시는 분들이 나중에도 생기면 더 귀찮아질 수도 있으므로

    예제 실행 파일과 함께 HLSL 소스까지 공개합니다.

     

    첨부된 HatchTest_060123_parkch.zip 압축파일을 받아서 적당한 곳에 풀어놓으신 후,

    HatchTest 폴더 안에 있는 HatchTest.exe를 실행시키시면 예제를 돌려보실 수 있습니다.

    HLSL 소스는 HatchTest.fx 파일을 참조하시면 됩니다.

     

    예제를 실행하실려면 DirectX 9.0c 런타임 최신 버전이 깔려있어야 합니다.

    (http://www.microsoft.com/downloads/details.aspx?displaylang=ko&FamilyID=3F2828FA-0E3C-4837-AFC0-6C67DCAA54BF)

    그리고 Vertex Shader 1.1 이상, Pixel Shader 1.4 이상을 지원하는

    그래픽 카드(ATI RADEON 8500급 이상)가 있어야 합니다.

     

    실행시키시면 가상 광원의 위치를 나타내는 노란색의 구(sphere)가 물체 주위를 빙글빙글 돌아가는 것을 보실 수 있습니다. 그리고 그 광원 위치에 따라 해칭 패턴이 변화하게 됩니다. 마우스로 물체를 회전시킬 수도 있으니 조작해보시길 바랍니다. 그리고 우측에 있는 콤보 박스를 이용하면 표시되는 물체(구, 원뿔, 주전자, 호랑이)를 바꿀 수 있습니다.

     

    < 해칭의 개념 >

     

    해칭(Hatching) : 펜/잉크 소묘에서 가늘고 세밀한 평행선이나 교차선을 사용하여 객체의 음영 차이 및 요철을 표현하는 기법에 대한 일반적인 용어.

     

    (개념 : 인간이 마땅히 알아야 할 상식과 도리로서 이를 알지 못하면 '개'가 된다고 하여 개념이라 함.)

     

    이 예제에서 보여주는 해칭(Hatching)은 비실사 렌더링(Non-Photorealistic Rendering;NPR) 방식 중의 하나로서 해치 패턴의 밀도를 통해 그 지점의 표면이 얼마나 많은 빛을 반사하는지를 표현해주는 기법입니다. 여기서 사용하는 해칭 기법은 매우 드문드문한(빛을 많이 받는) 영역으로부터 매우 조밀한(빛을 받지 않는) 영역에 이르는 해치 패턴 맵을 이용하게 됩니다. 각 정점 별로 빛에 대한 가중치를 계산한 다음, 각 픽셀 별로 보간된 가중치에 따라 해치 패턴 맵을 적절히 혼합하여 표현해주는 방법을 사용하고 있습니다.


    < Tonal Art Maps >

     

    어떤 분께서 E-Mail로 Tonal Art Map(TAM)을 이용해서 구현했는지 물어보셨는데, 맞습니다.

    앞서 언급한 해칭 패턴 맵이 바로 Tonal Art Map으로서 여기서는 6단계의 텍스처를 사용하게 됩니다.

    하지만, 팔레트가 6개-즉, 텍스처를 6장이나 쓰게 되면 그만큼 처리 비용이 엄청나겠죠?

    그래서 위의 그림과 같이 각 단계의 맵이 단색(Grayscale)임을 이용하여

    텍스처의 각 색상 채널에 하나씩 할당해서 2장의 텍스처로 줄여 쓸 수 있게 합니다.

    (단색 텍스처는 0~255 값을 가지는 하나의 채널로 표현할 수 있으므로

    R, G, B 각각의 채널에 하나씩 집어넣을 수 있습니다.)

    즉, 1,2,3 단계의 맵은 첫번째 텍스처, 4,5,6 단계의 맵은 두번째 텍스처에 할당됩니다.

    (첨부된 압축 파일 안의 Hatch123.dds, Hatch456.dds가 이것입니다.)

     

    이 내용은 "REAL-TIME RENDERING 2nd Edition" 책에 소개된 기법으로서

    2001년 시그라프(SIGGRAPH) 논문에 있는 내용입니다.

     

    Emil Praun, Hugues Hoppe, Matthew Webb, and Adam Finkelstein, "Real-time Hatching," (SIGGRAPH Proceedings, 2001), pp. 581-586.

     

    아래 링크를 클릭해보시면 논문을 직접 보실 수 있습니다.

    http://research.microsoft.com/~hoppe/hatching.pdf

    (다소 영어의 압박을 느낄 수도 있습니다.)

     

    < Vertex program source >

     

    위의 소스는 정점 프로그램으로서 각 정점 별로 빛에 대한 가중치를 계산합니다.

    워낙 코딩 실력이 딸려서 소스가 복잡하고 다소 더럽지만 그렇다고 나무라지는 마시길 바랍니다.

    clamp, abs 함수들을 좀 복잡하게 써 놓긴 했지만, 내용은 단순합니다.

    (abs는 절대값, clamp는 min~max 밖의 값을 잘라냄; max보다 크면 max로, min보다 작으면 min으로 함.)

     

    쉽게 설명하면, 정점에 대한 노말 벡터와 광원 방향 벡터의

    내적을 구해서 빛을 받는 정도를 [-1, 1] 구간 사이로 구한 다음, [0, 6] 구간으로 바꿉니다.

    그리고나서 가장 적합한 단계들에게 가중치를 부여합니다.

     

    예를 들어, 'n dot l'의 계산 결과가 0.5가 나왔다고 합시다. 어중간하게 좀 밝은 쪽이 되겠죠?

    그러면, 1단계와 2단계 텍스처가 중간 정도 혼합된 형태가 되어야 할 것입니다.

    위의 복잡한 식대로 계산하면 oTexWeight123과 oTexWeight456은 다음 값들을 가지게 됩니다.

     

    oTexWeight123 == (0.5, 0.5, 0.0)

    oTexWeight456 == (0.0, 0.0, 0.0)

     

    의도한 대로 1단계와 2단계에 0.5씩 가중치가 들어갔죠?

    한마디로, abs랑 clamp를 써서 적절한 단계에 가중치가 부여될 수 있도록 했습니다.

    만일, 이 글을 보시는 다른 분들께서 조금 더 간단하게 쓸 수 있다고 생각하시면 알려주세요.

    아직 HLSL를 많이 써보지 못하다보니 코드가 좀 엉망입니다.

    (지금 생각해보니 clamp(..., 0.0, 1.0)이 아니라 saturate(...)를 쓰면 되었을 것을... 왜 저랬을까? 이런 바보같은 짓을... ㅡㅡ;)


    < Fragment program source >

     

    마지막으로 위의 소스는 프래그먼트 프로그램으로서

    각 픽셀 별로 보간된 가중치에 따라 TAM을 혼합하게 됩니다.

     

    앞 쪽에서 보여드린 TAM 그림을 다시 보시면 스트로크(stroke) 패턴들이 검정색,

    바탕은 흰색으로 된 것을 알 수 있습니다.

    사실 우리가 해야 할 일은 스트로크 패턴들만 뽑아내서 잘 혼합하는 것입니다.

     

    그래서 이 프래그먼트 프로그램에서는 별도로 구현된

    GetInverseColor 함수를 이용해서 색상을 뒤집습니다.

    즉, 바탕이 검정(0.0)으로 바뀌고, 스트로크가 흰색으로 바뀌므로

    스트로크 요소만 따로 혼합될 수 있습니다.

     

    보간된 가중치를 각 채널에 들어있는 Grayscale 값과 곱해서 더하고,

    이를 다시 GetInverseColor 함수로 뒤집어주면 우리가 원하던 최종 색상을 얻어낼 수 있게 됩니다.

     

    < 참고 자료 >

     

     

    1) Real-Time Rendering(2nd Edition)/Tomas Akenine-Moller, Eric Haines/A.K.Peters Limited

     

    해칭 관련 내용은 이 책 보고 쁼(feel) 받고

    실제로 돌려보고 싶다는 생각이 들어서 HLSL로 구현해봤던 겁니다.

    현존하는 실시간 렌더링 관련 서적 중에서는 최고의 참고서입니다.

    7장의 Non-Photorealistic Rendering 파트에 해칭 관련 내용이 소개되어 있습니다.

     


    2) Direct3D ShaderX:Vertex and Pixel Shader Tips and Tricks/Wolfgang F. Engel/Wordware Publishing

     

    사실 제 소스는 이 책에 있는 어셈블리로 작성된 해칭 소스를 거의 베끼다시피해서 HLSL로 옮긴 것에 불과합니다. 그리고 TAM 텍스처도 부록 CD 안에 들어 있는 것을 가져와서 쓴 것입니다. 원본 소스를 보고 싶으시다면 참조하십시오.


    < 마침 >

     

    간만에 긴 글을 포스팅해봤습니다. HLSL 써본지도 꽤 오래된 지라 가물가물하기도 하고, 글도 생각처럼 잘 써지지도 않는군요. 그리고 나름대로 머리 좀 굴려봤다고 자부했었는데... 오히려 글을 쓰고 보니 부족했던 점이 더욱 눈에 띕니다. 제가 참여하고 있는 프로젝트도 셰이더를 맘껏 쓸 수 있는 환경이 되면 좋겠지만... 렌더링과는 점점 더 멀어지는 일만 하고 있네요. 좀 더 재미있는 것을 할 수 있다면 좋으련만...

     

    오뎅장수 P군

    블로그 : http://blog.naver.com/pch413/

    반응형
    반응형



     InteliShade.msi




    http://blog.naver.com/sorkelf/40147650136




    cpp 파일이나 h 파일 작성시 비쥬얼 적으로 깔끔하게 해주는 컬러링과 

    관련 함수등을 쉽게 찾아내게 도와주는 VS의 대표적인 기능인 Intelisense가 있다.


    하지만 아직은 VS는 HLSL 편집할때의 이러한 기능을 지원하지 않는다..


    NotePad++ 이나 UltraEdit등으로 편집해도 되긴 하지만..


    VS에서 같이 쓰면 편한건 누구나 아는 사실이다..


    분명히 누군가가 이런거 불편해서 만들어놨을텐데라고 해서 구글링 시작...


    InteliShade 라는 놈을 찾아 냈다..


    Step 1 : 위에 첨부한 파일을 다운 받아 설치한다





    Step 2 : 설치된 폴더를 확인한다






    Step 3 : VS가 깔린 폴더의 Addin 폴더에 해당 dll을 Ctrl +C, V 한다

               (일반적인 경로라면 : C:\Program Files\Microsoft Visual Studio\COMMON\MSDev98\AddIns )






    Step 4 VS를 열고 도구 -> 옵션 -> 글꼴 및 색 에서 컬러링 지정을 한다 (굳이 안해도 될 듯)


    적용되는 옵션들 

    (Comments, DataTypes, Error, Intrinsic, Matrix, Operation, RenderState, Register, Statements Semantics, Variable, Void)





    Step 5  : fx 파일을 편집한다






    Utility이긴 하지만 Shader 관련이기 때문에 카테고리를 이곳으로 하기로 했다-.- 어차피 검색하면 나오겠지만..


    쓰고 있는지는 프로젝트 시작하기 전부터였으나.. 올리기가 귀찮아서...


    쓸데없는 부분에서 자동완성기능이 활성화 된다거나.. 처음부터 치지않으면 자동완성이 나오지 않는점..


    변수명을 줄려고 한건데 자동완성기능으로 인해 시멘틱등으로 자동완성 되버리는 점등..


    VS의 Intelisense 기능처럼 완벽하지는 않으나


    이정도가 어디임... -_- 2010은 지원해 줄라나...

    반응형

    '그래픽스(Graphics) > Shader' 카테고리의 다른 글

    셰이더[Effect] 함수들 ID3DXBaseEffect 인터페이스  (0) 2013.01.08
    [소스] 해칭(Hatching) HLSL 소스  (0) 2013.01.05
    쉐이더 기법  (0) 2012.11.02
    셰이더 내장함수  (0) 2012.11.02
    smoothstep  (0) 2012.11.02
    반응형



    http://blog.naver.com/kimachine/130104998068

    지금도 주말마다 개발하고 있는 게임이 있는데 쉐이더 공부를 하면서 하니까 오래 걸린다.

    그림이 점점 좋아 지고는 있으나 직업으로 하지 못하니 시간을 내기 힘들고 주말에만 잠깐씩 할 수 있다.

    공부해서 알게된 쉐이더 기법을 하나씩 적용해 나갔다.

     

    1. HDR Lighting 기법

    노말매핑을 적용한 물체에 HDR(High Dynamic Range) Lighting 을 적용했다. 

    반사되는 부분이 빛이 옆으로 번지는 기법 (Blooming)이 사용되었다.

    HDR을 하기 위해서는 먼저 16비트 렌더링 타겟에 물체를 렌더링한다.  즉, A16R16G16B16 포멧을 갖는 텍스쳐를 만들어 이것을 렌더 타겟으로 설정한다.  이후 쏘이는 빛의 세기를 1.0을 초과하는 큰 값을 가지게 하여 렌더링한다.  그러면 각 물체의 표면이 어두운 곳은 변함이 없지만 밝은 곳은 1.0을 넘는 밝기를 가지게 된다.  16비트 텍스쳐는 각 픽셀의 밝기값을 훨씬 크게 저장할 수 있다.  하지만 마지막 보여지는 화면은 각 픽셀의 밝기가 1.0까지만 되므로 1.0을 초과하는 밝기를 다른 형태로 표현해 줘야 한다.  그러기 위해 Blooming효과를 준다.  Blooming 효과 말고도 tone mapping이나 다른 방식이 있으나 번쩍이는 효과를 주기로 했다.  이렇게 얻어진 16비트 텍스쳐는 필터링을 거쳐서 밝은 부분만 걸러내는 작업을 통과하게 된다.  그러면 어두운 화면에 밝은 부분이 군데 군데 남게 된다.  이렇게 얻어진 화면을 Gaussian Blur를 한다.  Blur는 번지게 하는 기법을 말하는데 간단히 말하면 주위 근처의 픽셀 밝기값들을 현재의 픽셀값에 더한후 나누어 주는 것을 말한다.  그러면 어두운 화면에 번져있는 부분이 있는 텍스쳐가 얻어지는데 이것을 원래 그림과 합해서 렌더링해주면 된다.

     





     


    2. Environment Mapping

      환경매핑(Environment Mapping)은 일반적으로 매끈한 금속성 표면에 주위가 비춰지는 효과를 나타내 주는 기법을 말하나 나는 바다의 반짝이는 효과를 주기 위해 사용되었다.  바다는 전에 골프장에서 썼던 반사맵을 이용해서 그리면 바다 처럼 보이지가 않았다.  그래서 반짝거리는 효과가 주로 나타내는 데 집중했다.  일반적으로 물을 그릴때는 물의 표면의 움직임(파도)을 나타내는 normal map(법선맵)을 가지고 environment map(환경맵)의 좌표값을 알아낸후 텍스쳐로 사용한다.

      일반적인 노말매핑은 노말맵에 들어 있는 값 (예, -0.7, 0.0, 0.7)을 물체에서 그 좌표에 해당하는 표면이 바라보는 방향으로 간주한다.  즉 어떤 물체의 표면에 매핑된 노말맵의 좌표값이 (0.1, 0.2) 라면 이 좌표값에 있는 노말맵의 색깔 (RGB) 값을 3차원 벡터로 바꿔주고 그 벡터가 가리키는 방향이 그 표면이 보고 있는 방향이라고 간주하는 것이다.  만약 그 표면이 광원을 향하고 있으면 밝게 그려주고 그렇지 않으면 어둡게 그려주므로써 표면을 픽셀단위로 굴곡을 줄 수 있다.  이것은 결국 로우폴리곤 모델로 하이폴리곤 모델을 나타내 주는 방법이다.  그러나 환경매핑은 같은 노말값을 이용해서 광원에 따라 밝기를 계산하는게 아니고 그 노말값이 가리키는 방향에 있는 환경 (즉, 주변물체)의 표면을 그려준다.  하늘을 예로 들면 노말값이 수직이면 하늘의 정 중앙의 이미지를 텍스쳐링하는 식이다.  그러면 결국 울퉁불퉁한 표면이 주위의 환경을 반사하는 물체가 그려진다.  이때 반사되는 물체들을 일일히 찾아서 그려주는 것이 아니고 환경맵이라는 주변을 찍은 텍스쳐를 이용해서 그려준다.  이때 사용하는 텍스쳐는 큐브맵(Cube map) 인데 박스형태의 이미지로 6개의 면으로 되어 있다.  즉, 상하, 좌우, 앞뒤를 가운데 에서 찍은 이미지로 사방의 환경을 나타낸 것이다. 

      물은 이 방법에서 나아가 표면의 노말값이 시간에 따라 바뀌도록 (즉, 돌려주거나 움직이거나 등) 해야 한다.  그러면 겉에 반사된 이미지가 물결에 따라 출렁거리게 보인다.  아래의 스크린 샷에서 반짝이는 하얀 점들은 환경맵에 있는 광원이 번진 것이다.  광원을 크게 그리면 너무 밝게 보여서 멀리 있는 물처럼 보이지를 않아서 작은 광원이 모여있는 방식으로 그렸다.


    [출처] 쉐이더 기법 1|작성자 바람소리


    반응형

    '그래픽스(Graphics) > Shader' 카테고리의 다른 글

    [소스] 해칭(Hatching) HLSL 소스  (0) 2013.01.05
    VS에서 HLSL 편집하기 InteliShade  (0) 2013.01.05
    셰이더 내장함수  (0) 2012.11.02
    smoothstep  (0) 2012.11.02
    NVIDIA계열 그래픽카드 오버클럭 하기  (0) 2012.10.31
    반응형

    http://blog.naver.com/inushaha?Redirect=Log&logNo=10020452097

     

    내장 함수 설명
    abs(x) 절대값을 구한다(x의 각 성분에 대해).
    acos(x)
    x의 모든 성분의 아크코사인 값을 구한다.
    각 성분들의 값은 [ 1, 1] 사이에 있어야 한다.
    all(x) x의 모든 성분들이 0이 아닌지를 테스트한다.
    any(x) x의 성분 중 0이 아닌 것이 있는지 테스트한다.
    as in(x)
    x의 각 성분의 아크사인 값을 구한다.
    각 성분들의 값은 [ p/2, p/2] 이내에 있어야 한다.
    atan(x)
    x의 아크탄젠트 값을 구한다.
    반환되는 값은 [ p/2, p/2]의 범위 안에 있다.
    atan2(y, x)
    y/x의 아크탄젠트 값을 구한다. y와 x의 부호는 [-p, p] 범위 안에 있는
    반환 값들의 사분면을 결정하는 데 쓰인다. atan2는 원점을 제외한 모든
    다른 점들에 대해서도 잘 정의되어 있는데, 심지어 x가 0이고 y가 0이
    아닌 상황에서도 잘 동작한다.
    ceil(x) x보다 크거나 같은 정수 값을 반환한다.
    clamp(x, min, max) [min, max] 범위를 벗어나지 않게 값을 자른다.
    Clip(x)
    x의 어떤 성분이라도 0부다 작으면 현재 픽셀 값을 버린다. 이것을
    이용하여 x의 각 원소가 평면으로부터의 거리를 나타내게 하면 평면
    클립도 구현할 수가 있다. 이것은 texkill 같은 명령어를 만들고자 할 때
    사용할 수 있는 내장 함수이다.
    cos(x) x의 코사인을 구한다.
    cosh(x) x의 하이퍼볼릭 코사인(hyperbolic cos ine)을 구한다.
    cross(a , b) a , b 벡터의 외적을 구한다.
    D3DCOLORtoUBYTE4(x)
    4D 벡터 형태인 x의 각 성분을 몇몇 하드웨어에서 지원하는 UBYTE4
    스트림 형태로 바꾼다.
    ddx(x) 스크린 공간 기준인 x 좌표의 편 미분계수를 구한다.
    ddy(x) 스크린 공간 기준인 y 좌표의 편 미분계수를 구한다.
    degrees(x) 라디안 값을 각도 값(0∼360도 사이)으로 바꾼다.
    determinant(m) m행렬의 행렬식을 구한다.
    distance(a , b) a , b 사이의 거리를 구한다.
    dot(a , b) a , b 벡터의 내적을 구한다.
    exp(x) e의 x 제곱 값을 구한다.
    exp2(a) 2의 x 제곱 값이다(각 원소마다).
    faceforward(n, i, ng) n * s ign(dot(i, ng)) 값을 구한다.
    floor(x) x보다 같거나 작은 것 중 가장 큰 정수 값을 구한다.

    fmod(a , b)
    a/b의 부동 소수점형(실수형) 나머지를 구한다. a = i * b + f에서 I는
    정수형이고, f는 x와 같은 부호를 가지고 있고, f의 절대값은 b보다는 작다.
    frac(x) X의 소숫점 부분을 구한다
    frexp(x, out exp)
    x의 가수와 지수를 계산한다. frexp는 exp의 결과 매개변수에 저장된
    가수와 지수를 계산한다. 만일 x가 0일 때는 가수와 지수의 계산 결과를
    0으로 계산한다.
    fwidth(x) abs(ddx(x))+abs(ddy(x))를 반환한다.
    isfinite(x) x가 유한이면 true, 그렇지 않으면 false를 반환한다.
    is inf(x) x가 +INF이거나 INF이면 true를, 그렇지 않으면 false를 반환한다.
    isnan(x)
    x가 NAN이거나 QNAN이면 true를, 그렇지 않으면 false를 반환한다.
    CNAN:Net a numbor-잘못된 실수 값
    ldexp(x, exp) * 2ex p 값을 구한다.
    len(v) 벡터 길이
    length(v) 벡터 v의 길이를 구한다.
    lerp(a , b, s)
    s가 0이면 a를, 1이면 b를 반환하는 a와 b의 선형 보간 값인 a + s(b
    a)의 결과 값을 계산한다.
    log(x)
    지수가 e 인 x의 로그 값을 구한다. x가 음수면 indefinite 값을 반환한다.
    x가 0이면 +INF를 반환한다.
    log10(x)
    지수가 10인 X의 로그 값을 구한다. x가 음수이면 indefinite를, 0이면
    +INF를 돌려준다.
    log2(x)
    지수가 2인 x의 로그 값을 구한다. x가 음수이면 indefinite를, 0이면
    +INF를 돌려준다.
    max(a, b) a와 b 중 큰 것을 선택한다.
    min(a, b) a와 b 중 작은 것을 선택한다.
    modf(x, out ip)
    x 값을 x와 같은 부호를 같은 소수부와 정수부로 나눈다. 부호가 있는
    소수 부분은 반환되고, 정수부는 out ip 값에 저장된다.
    mul(a, b)
    a와 b의 행렬 곱셈 연산을 수행한다. a가 벡터이면 행 벡터로 인식된다.
    b가 벡터면 열 벡터로 인식된다. a의 열과 b의 행은 반드시 같아야 한다.
    결과 값은 a행×b열인 행렬이다.
    normalize(v)
    v/ length(v)로 정규화된 벡터 v를 구한다.
    v의 길이가 0이면 결과는 정의되지 않는다..
    pow(x, y) xy 값을 반환한다.
    radians(x) x를 각도에서 호도(radian) 값으로 바꾼다.
    reflect(i, n)
    반사 벡터 v를 돌려준다. I는 입사 광선, n은 표면의 법선 벡터이라면 결과
    값은 v = i 2 * dot(i, n) * n이다.

    refract(i, n, eta)
    굴절 벡터 v를 구한다(입사 광선은 I이고, 표면의 법선은 n이고, eta는 굴절
    색인). eta로 주어진 n과 I 사이의 각도가 너무 크다면 refract는 (0, 0,
    0)을 반환한다.
    round(x) x에 가장 가까운 정수를 찾는다.
    rsqrt(x) 1 / sqrt(x)를 구한다.
    saturate(x) x의 값을 [0, 1] 사이가 되게 자른다.
    sign(x)
    x의 부호를 구한다. x < 0이면 -1을 돌려주고, x=0이면 0을, x > 0이면
    1을 돌려준다.
    sin(x) x의 사인 값을 구한다.
    sincos(x, out s , out c)
    x의 사인과 코사인 값을 구한다. sin(x)는 출력 매개변수 s에 저장되고,
    cos(x)는 출력 매개변수 c에 저장된다.
    sinh(x) x의 하이퍼볼릭 사인을 구한다.
    smoothstep(min, max, x)
    x < min이라면 0을 반환하고, x > max이면 1을 반환한다. [min, max]
    사이에서는 0과 1사이의 smooth Hermite 보간 값을 반환한다.
    sqrt(x) 제곱근(성분별).
    step(a , x) (x = a) ? 1 : 0 값을 반환한다.
    tan(x) x의 탄젠트 값을 구한다.
    tanh(x) x에 대한 하이퍼볼릭 탄젠트 값을 구한다.
    transpose(m)
    행렬 m의 전치 행렬을 구한다. 입력 값이 mro w s × mc o lu m n s 차원이면
    출력은 mc o lu m n s ×mrow s 차원의 행렬이 될 것이다.

    반응형

    '그래픽스(Graphics) > Shader' 카테고리의 다른 글

    [소스] 해칭(Hatching) HLSL 소스  (0) 2013.01.05
    VS에서 HLSL 편집하기 InteliShade  (0) 2013.01.05
    쉐이더 기법  (0) 2012.11.02
    smoothstep  (0) 2012.11.02
    NVIDIA계열 그래픽카드 오버클럭 하기  (0) 2012.10.31
    반응형

    Smoothstep 함수 사용법 by 김윤정

    smoothstep(min,max,x) : x가 [min, max] 사이의 값인 경우에 대해서 [0, 1] 사이에서 부드럽게 변하는 
    Hermite 보간법을 리턴한다. x가 min보다 작다면 0을 리턴하고, max보다 크다면 1을 리턴한다.



    ..라고 하는데 뭐야 저게... 
    라고 해서 알아보니. 

    (그림1)

    일단 0에서 1로 가는 Liner 한 그래프가 있다고 치죠. 저걸 거꾸로 하면 단순무식한 렘버트 출력값과 같겠죠 (...) 


    (그림2)
    그리고 min과 max 값을 정합니다. 뭐 이건 0과 1로 정하면 원래 그림인 (그림 1)과 같지만 여기서는 0.2와 0.8로 정했다고 치죠. 

    (그림3)

    X 값이 min 값보다 작으면 최종 결과는 0 값이 나오고, X 값이 max보다 크면 1값이 나오는건 쉽지요.

    (그림4)

    근데 만약 X 값이 min과 max 사이에 있는 값이면? 
    비율을 다시 계산해서 값을 내놓는다는 것입니다. 그냥 Liner 하게 계산하면 그림과 같겠지요. 
    근데 여기서 Hermite Interpolation (보간법)을 사용한다고 합니다. http://en.wikipedia.org/wiki/Hermite_interpolation
    ... 이거 뭐야 몰라 무서워... 그래픽쟁이한테 이런 수학 공식 들이밀지 마... (이럴때만 그래픽인척 하는 초파리이자 박쥐) 

    뭐 그래서 더 생각 안하고, 그림으로 만들어 봤습니다. -_- 
    하프 렘버트 출력물을 
    밝기에 따라 3단계로 적용. smoothstep 사용. Liner 하지 않다는 것이 보입니다. 
    Warp 대용으로 사용한 최종 결과물. 텍스쳐 셈플러 하나 줄였습니다. 만세. 근데 이게 더 무거우면 어쩌지 ㄷㄷㄷ 



    즉 아마도 Smoothstep의 보간법은 이런 결과물을 내놓는 듯 합니다. 

    (그림5)

    반응형

    '그래픽스(Graphics) > Shader' 카테고리의 다른 글

    [소스] 해칭(Hatching) HLSL 소스  (0) 2013.01.05
    VS에서 HLSL 편집하기 InteliShade  (0) 2013.01.05
    쉐이더 기법  (0) 2012.11.02
    셰이더 내장함수  (0) 2012.11.02
    NVIDIA계열 그래픽카드 오버클럭 하기  (0) 2012.10.31
    반응형

    http://blog.naver.com/humanist23/90027831935




    오버클럭은 말 그대로 CPU나 램 등 반도체의 작동 속도를 강제로 빠르게 하는 것으로 돈을 들이지 않고 성능 높이는 하나의 방법이다.

    그래픽카드는 PC게임이 3D그래픽(영화, 기타등등)으로 나오는 요즘. PC전체의 성능을 좌우하는 중요한 부품이 그래픽카드 이다.

     

    그래픽카드 오버클럭 방법은 두가지가 있으며, 다음과 같다.

    1.GPU(그래픽 프로세서)클럭의 오버클럭

    2.메모리 클럭의 오버클럭

     

    이 두 부품의 클럭이 그래픽카드의 성능을 대변하므로 이를 높여 성능을 향상시킬 수 있다.

    출시되는 대부분의 그래픽카드는 GPU와 메모리 클럭이 다르다.

     

    메모리는 대부분 DDR SD램을 사용하고 있어 실제 표시 클럭의 2배로 작동한다.

    이건 그래픽카드 구조상 GPU보다는 메모리쪽에 더 많은 부하가 걸리기 때문이다.

    오버클럭 효과를 높이기 위해서는 GPU보다는 메모리클럭을 올리는 것이 더 확실하다.

     

    그래픽카드 오버클럭이 가능한 것은 프로세서와 마찬가지로 GPU와 메모리에 마진율이 적용되기 때문이다.

    반도체를 설계할 때는 목표동작 주파수(클럭)를 정하고 설계한다.

    그러나 실제 제조 공정을 거쳐 실리콘 웨어퍼 상에 칩을 만들면 정확히 목표한 클럭대로 칩이 만들어지는 것은 아니다.

    따라서 그래픽카드 제조사에서는 이 칩을 테스트해 일정한 마진을 두고 안정적으로 동작하는 클럭으로 마킹해 출시한다.

    이 과정에서 발생하는 마진으로 인해 오버클럭이 가능한 것이다.

     

    여기서 설명하는 엔디비아 그래픽카드의 오버클럭 방법은 엔디비아에서 제공하는 nTune 라는 유틸리티를 사용하는 것으로, ForceWare 1xx.xx 대 버전의  그래픽카드 드라이버에서 할 수 있는 방법이다.

     

    참고로 nTune 는 엔디비아 메인보드용 유틸리티로서 엔디비아 메인보드를 사용하고 있다면, 시스템에 대한 좀더 폭넓은 정보(메인보드 온도 등등) 및 CPU 오버클럭 그리고 안정성 검사 등을 할수 있는 유틸리티이다.

     

     

    아래 링크를 클릭하여 nTune 를 다운받는다.

      2007년 9월 19일 NVIDIA 홈페이지에 업데이트된 NVIDIA nTune v5.05.54.00 정식 버전

      WinXP/WinXP 64Bit/WinVista/WinVista 64Bit OS용

      다운로드   (파일서버가 미국에 있는것이라 다운받는데 시간이 좀 걸린다.)

     

     

    프로그램을 설치하고 바탕화면 -> 마우스 오른클릭 -> NVIDIA 제어판 선택

    혹은 시작 -> 제어판 ->  NVIDIA 제어판 

     

     

    유틸리티가 정상적으로 설치 되었다면 아래의 이미지와 같이 성능 이라는 탭이 생성된다.

     

     

    왼쪽창에서 "최종 사용자 라이센스 계약 동의"를 선택하고 오른쪽 창의 "동의" 버튼을 클릭한다.

     

     

    성능 탭아래 안보이던 메뉴가 몇개 추가된 것을 확인할 수 있으며, GPU 설정 조정 메뉴를 선택한다.

    엔디비아 메인보드를 사용하고 있다면, GPU 설정 조정 아래 나열되어 있는 메뉴에서 시스템 정보를 얻거나 시스템 설정, 시스템 안정성 테스트 등을 할 수 있다.

    오른쪽 창의 GPU 클럭 설정 에서 오버클럭을 할 수 있으며, 그 아래 GPU 팬 설정이 보인다. 

    아쉽게도 필자의 그래픽카드에는 GPU 팬 컨트롤러가 없는 제품이다. 따라서 GPU 팬 설정 항목이 활성화 되지 않는다.

    (GPU팬 전원이 2핀이라면 GPU 팬 컨트롤러가 없다고 보면 된다.)

     

     

     

    오른쪽 창에서 사용자 정의 클럭 주파수 선택

     

     

    마우스로 Core클럭바와 Memory클럭바를 오른쪽으로 이동시켜 오버클럭을 설정하며, 정밀조정은 키보드의 왼쪽 혹은 오른쪽 방향키 를 사용한다.

    (참고. 안정적인 오버클럭율은 기본클럭의 10% ~ 20% 이며, 과도한 오버클럭은 오히려 시스템성능을 저하 시키기도 한다.)

     

     

    오버클럭값을 정했다면, 테스트 버튼 클릭


     

     

    테스트가 끝날때 까지 기다리자.

     

     

    테스트를 통과 했다면 다음과 같은 메시지 창을 볼수가 있다.

    확인 하고 시스템에 적용 시키자.

     

     

     

    오버클럭후 평소에 자주하던 3D 게임을 최소 3~5번 정도 실행해 보고 이상이 없어야 한다.

     

     




    반응형

    '그래픽스(Graphics) > Shader' 카테고리의 다른 글

    [소스] 해칭(Hatching) HLSL 소스  (0) 2013.01.05
    VS에서 HLSL 편집하기 InteliShade  (0) 2013.01.05
    쉐이더 기법  (0) 2012.11.02
    셰이더 내장함수  (0) 2012.11.02
    smoothstep  (0) 2012.11.02

    + Recent posts