본문으로 바로가기
반응형

DX11 Graphics pipeline

 

MS hlsl guide  https://learn.microsoft.com/ko-kr/windows/win32/direct3dhlsl/dx-graphics-hlsl

hlsl의 정의

고급 셰이더 언어(High Level Shader Language)는 마이크로소프트의 다이렉트3D API에 사용되는 셰이딩 언어이다. 고급 셰이딩 언어(High Level Shading Language)라고도 불리며 줄여서 HLSL이라고도 불린다. 이 셰이딩 언어는 OpenGL API에서 표준으로 사용되는 셰이딩 언어인 GLSL 및 또한 엔비디아의 Cg 셰이딩 언어와도 매우 유사한데, 이는 서로 협력하여 개발하였기 때문이다.

 

특징

HLSL의 프로그램 형식에는 3가지가 있다. 버텍스 셰이더, 지오메트리 셰이더, 그리고 픽셀 셰이더이다. 다이렉트3D 10 인터페이스에서는 지오메트리 셰이더가 버텍스 셰이더와 픽셀 셰이더 사이의 파이프라인 가운데 새로 추가되었다. 버텍스 셰이더는 응용 프로그램이 제공하는 정점에 각각 붙어 실행되어 주로 이하의 처리를 담당한다. 객체 공간으로부터 시 공간에의 정점 변환이나 텍스처 좌표의 생성, 또 정점의 접선이나 종법선이나 법선 벡터와 같은 광선의 계 계산 등에 쓰인다. 버텍스 셰이더를 통해 정점의 그룹이 입력되었을 때, 출력 좌표는 그 영역내에서의 픽셀을 결정하기 위해서 보간 되게 된다. 이러한 작업은 레스터라이제이션으로 더 잘 알려져 있다. 이러한 픽셀이 각각이 픽셀 셰이더를 통과하는 것으로, 결과적으로 화면상의 색이 계산된다.

또, 다이렉트3D 10 인터페이스 또는 다이렉트3D 10 하드웨어를 사용하는 응용 프로그램은 지오메트리 셰이더를 직접 지정할 수도 있다. 이 셰이더는 삼각형의 3개의 정점을 입력받아 추가의 삼각형을 생성해 틈새 없게 채운 후, 레스터라이저에 보내게 된다.

shader는 하드웨어에서 지원하는 shader model에 따라 기능수준이 결정된다. 자세한 내용은 아래 링크 참고

주요 셰이더 모델

  • Shader Model 2.0: 기본적인 픽셀 및 버텍스 셰이더 지원.
  • Shader Model 3.0: 더 많은 명령어 세트와 동적 브랜칭 지원.
  • Shader Model 4.0 (DX10): 지오메트리 셰이더 도입.
  • Shader Model 5.0 (DX11): 컴퓨트 셰이더 도입, 더 복잡한 효과 구현 가능.
  • Shader Model 6.x (DX12): 최신 GPU 기능 지원 (예: 레이 트레이싱, FP16, 웨이브 셰이딩 등).

Unity Shaderlab shader model features : https://illu.tistory.com/1248
Mobile Graphics API 및 Shader Model feature : https://illu.tistory.com/1209

Vertex Shader

버텍스 셰이더(Vertex Shader)는 그래픽스 파이프라인에서 각 정점(Vertex)의 위치를 처리하는 GPU 프로그램으로, 주로 모델의 변환(예: 회전, 이동, 스케일)을 계산하고, 정점 정보를 변형하여 월드 좌표계에서 화면 좌표계로 변환하는 역할을 하게 된다.

  • 변환(Matrix Transformations) : 오브젝트의 위치, 회전, 스케일을 처리하여, 모델 좌표계에서 뷰, 클립 좌표계로 변환한다. 이는 주로 모델-뷰-프로젝션 행렬을 곱하는 과정에서 이루어진다.
  • 정점 속성 전달 : 각 정점의 위치뿐 아니라, 정점에 대한 추가적인 속성(예 : 색상, 텍스처 좌표, 노멀 벡터 등)을 픽셀 셰이더로 전달하게 된다. 이를 통해 픽셀 셰이더는 각 픽셀에 필요한 정보를 기반으로 최종 색을 계산할 수 있다.
  • 조명 계산 : 버텍스 셰이더에서 조명 계산을 일부 수행할 수 있다. 예를 들어, 고전적인 램버트 조명 모델이나 퐁 조명 모델 등 기본적인 조명 효과는 버텍스 셰이더에서 계산할 수 있다(다만, 버텍스 기반 계산이므로 버텍스가 충분히 많지 않을때 엣지가 두드러지게 보이는 증상이 있다)
  • 정점 변형 : GPU는 버텍스 셰이더를 사용해 정점들을 변형할 수 있는데 이를 활용해 정점의 움직임을 제어해 다양한 움직임을 만들수 있다.
  • 텍스처 좌표 계산 : 버텍스 셰이더는 텍스처 좌표를 계산하고, 이를 픽셀 셰이더로 전달한다. 예를 들어, 정점의 위치에 따라 텍스처 맵핑이 다르게 적용될 수 있다. 또한, 이를 활용해 텍스처 UV 정보값을 왜곡해 다양한 효과를 만들어 낼 수도 있다

Vertex Shader의 동작 과정

  • 입력(Input) : CPU에서 정점 데이터(예: 위치, 색상, 텍스처 좌표 등)를 GPU로 전달한다. 이 데이터는 버텍스 셰이더에서 변환되게 된다(Input Assembly)
  • 계산(Processing) : 정점의 변환, 조명, 텍스처 좌표 등의 계산을 수행.
  • 출력(Output) : 최종적으로 변환된 정점 데이터를 그래픽스 파이프라인의 다음 단계로 보간기(Interpolator)를 통해 전달한다(주로 픽셀 셰이더)

 

주요 함수 및 구성

  • float4 : 위치, 색상 등의 데이터형으로 주로 4D 벡터로 나타내는 자료형.
  • mul : 행렬 변환을 위해 많이 사용되는 함수로, 버텍스 위치에 변환 행렬을 곱하는 데 사용.

struct VertexInput 
 {
    float3 pos : POSITION;
    float4 color : COLOR;
 };

struct VertexOutput 
  {
    float4 pos : SV_POSITION;
    float4 color : COLOR;
  };

cbuffer MatrixBuffer 
  {
    matrix world;
    matrix view;
    matrix projection;
  };

VertexOutput main(VertexInput input) 
  {
    VertexOutput output;
    float4 worldPos = mul(float4(input.pos, 1.0f), world);
    float4 viewPos = mul(worldPos, view);
    output.pos = mul(viewPos, projection);
    output.color = input.color;
    return output;
  }

위 예시에서 VertexInput은 정점의 입력 데이터(위치, 색상 등)를 나타내며, MatrixBuffer는 변환을 위한 행렬들을 담고 있다. 이 행렬들을 이용해 정점의 위치를 변환하고, 결과값을 출력하게 된다.
버텍스 셰이더는 그래픽스 파이프라인의 첫 번째 단계에서 매우 중요한 역할을 하며, 복잡한 3D 장면에서 정점 데이터를 효율적으로 처리하는 데 사용된다.

 

Pixel Shader

픽셀 셰이더(Pixel Shader)는 그래픽스 파이프라인에서 각 픽셀의 색상, 명도, 텍스처 정보 등을 계산하는 GPU 프로그램으로, 주로 화면에 렌더링될 각 픽셀에 대한 최종 처리를 담당하게 된다.여기에 각 오브젝트의 픽셀당 조명 및 음영 계산(lighting & ambient color), 텍스처 맵핑, 포스트 프로세싱 등이 해당된다


struct PS_Input
  {
    float4 pos : SV_POSITION;
    float2 tex : TEXCOORD;
  };

sampler2D texSampler;
float4 main(PS_Input input) : SV_Target 
  {
    return tex2D(texSampler, input.tex);
  }

이 예시는 간단히 텍스처 좌표를 바탕으로 픽셀의 색상을 계산하는 기본적인 픽셀 셰이더로써, 복잡한 시각 효과(예: 반사, 그림자, 물리 기반 조명 등)를 구현할 수 있다.

포워드와 디퍼드의 셰이더 구조가 다른데 디퍼드의 경우 아래와 같이 해당 픽셀정보를 순차적으로 계산하는것이 아닌 Gbuffer에 저장하고 이를 최종 이미지에 사용하는 차이가 있다.

//First pass, 이 셰이더는 첫 번째 패스에서 각 픽셀의 알베도(색상), 노멀 벡터, 그리고 위치를 G-buffer에 저장

struct PS_Input 
  {
    float4 position : SV_POSITION;
    float3 normal : NORMAL;
    float2 uv : TEXCOORD0;
  };

struct GBufferOutput 
  {
    float4 albedo : SV_Target0;  // 색상 정보
    float4 normal : SV_Target1;  // 노멀 벡터 정보
    float4 position : SV_Target2; // 위치 정보
  };

GBufferOutput main(PS_Input input) {
    GBufferOutput output;
    output.albedo = float4(1.0, 0.0, 0.0, 1.0);  // 알베도 값 (빨간색)
    output.normal = float4(normalize(input.normal), 0.0);  // 노멀 벡터
    output.position = input.position;  // 월드 위치
    return output;
}

 

//second pass, 이 두 번째 패스의 픽셀 셰이더는 G-buffer에서 각 픽셀의 데이터를 가져와 조명 효과를 계산하고, 최종적으로 렌더링할 픽셀 값을 생성

struct PS_Input 
  {
    float2 uv : TEXCOORD0;
  };

sampler2D gAlbedo;   // G-buffer에서 가져온 알베도 값
sampler2D gNormal;   // G-buffer에서 가져온 노멀 값
sampler2D gPosition; // G-buffer에서 가져온 위치 값

float4 main(PS_Input input) : SV_Target0 {
    float4 albedo = tex2D(gAlbedo, input.uv);      // 알베도 값 가져오기
    float3 normal = normalize(tex2D(gNormal, input.uv).xyz);  // 노멀 값 가져오기
    float3 position = tex2D(gPosition, input.uv).xyz;  // 월드 공간의 위치

    // 간단한 조명 계산 (디렉셔널 라이트)
    float3 lightDir = normalize(float3(0.5, 0.5, -0.5));  // 라이트 방향
    float NdotL = max(dot(normal, lightDir), 0.0);  // 노멀과 라이트 벡터의 내적
    float3 lighting = NdotL * float3(1.0, 1.0, 1.0); // 디렉셔널 라이트 색상 (흰색)

    return float4(albedo.rgb * lighting, 1.0);  // 조명이 적용된 알베도 값 반환
}

 

Geometry Shader

그래픽스 파이프라인에서 버텍스 셰이더와 래스터라이저 사이에 위치하며, 3D 모델의 정점(버텍스) 데이터를 입력받아 추가적인 정점을 생성하거나, 정점의 데이터를 수정하는 역할을 한다. 즉, 입력된 정점 데이터를 이용해 새로운 도형을 생성하거나, 정점을 늘리고 줄이는 등의 다양한 조작이 가능하다.
https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-geometry-shader

 

Geometry-Shader Object - Win32 apps

A geometry-shader object processes entire primitives. Use the following syntax to declare a geometry-shader object.

learn.microsoft.com

지오메트리 셰이더는 Shader Model 4.0 이상 부터 지원하며, 모바일에서는 OpenGL es 3.1+ AEP(Android Extension Pack) 이상 부터 지원.(https://docs.unity3d.com/2021.3/Documentation/Manual/SL-ShaderCompileTargets.html)

//수정할 최대 버텍스 수를 정의
[maxvertexcount(3)]

// primitive type 정의 및 받아오는 data type 및 stream output 정의
void main(triangle InputType input[3], inout TriangleStream<OutputType> outputStream)
   {
      for (int i = 0; i < 3; ++i) {
        OutputType output;
        output.pos = input[i].pos;  // 정점 위치
        output.color = input[i].color;  // 정점 색상
        outputStream.Append(output);  // 출력 스트림에 추가
    }
}

stream output : https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-so-type

활용 사례

  • 그림자 볼륨 생성
  • 파티클 시스템에서 스프라이트 생성
  • 와이어프레임 렌더링에서 추가 선분 생성

장점

  • 추가적인 정점 생성으로 3D 모델의 디테일을 동적으로 조정 가능.
  • 한 번의 호출로 여러 정점이나 프리미티브를 생성할 수 있어 효율적.

단점

  • 연산 비용이 크기 때문에 성능에 영향을 미칠 수 있음.
  • 모든 GPU에서 효율적으로 지원되지 않을 수 있음.
 

Hull Shader

Tessellation 파이프라인에서 사용되는 셰이더로, 테셀레이션의 첫 단계에서 실행된다. 이 셰이더는 입력된 패치(정점 집합-삼각형, 사각형 또는 선)에 대한 세분화의 정도를 정의하고, 패치의 변형이나 추가 정보를 계산하게 된다. 패치당 한번 호출되며, 하위 표면을 정의하는 입력 제어점(Control point)을 패치를 만드는 제어점으로 변환(Patch Constant Data)하기도 한다. 또한 TS(Tessellation Stage) 및 DS(Domain Shader)에 데이터를 제공하기 위한 일부 패치별 계산도 수행하게 된다.


// 패치 상수 함수 정의
[patchconstantfunc("PatchConstantFunction")]
HS_CONSTANT_DATA_OUTPUT PatchConstantFunction(InputPatch<VertexInputType, 3> patch) 
   {
    HS_CONSTANT_DATA_OUTPUT output;
    output.edgeTessellationFactors[0] = 4.0f; // 엣지의 테셀레이션 레벨 설정
    output.insideTessellationFactor = 3.0f;   // 내부 테셀레이션 레벨 설정
    return output;
   }


// Hull Shader 함수 정의
[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(3)]
[patchconstantfunc("PatchConstantFunction")]

HS_OUTPUT main(InputPatch<VertexInputType, 3> patch, uint id : SV_OutputControlPointID) 
   {
    HS_OUTPUT output;
    output.position = patch[id].position;
    return output;
   }
  • PatchConstantFunction 함수는 패치의 세분화 수준을 설정.
  • domain("tri")는 삼각형 패치로 정의
  • partitioning("fractional_odd")는 세분화를 홀수 분할로 설정하여 부드러운 전환을 제공
  • outputcontrolpoints(3)는 출력 제어점의 개수를 3개로 설정
  • main 함수에서 각 제어점의 위치를 반환

 

Domain Shader

 

Hull Shader에서 정의된 세분화 수준에 따라 생성된 정점의 최종 위치를 계산하는 역할을 한다. 이 셰이더는 주로 Hull Shader와 함께 사용되며, 세분화된 패치의 각 정점 위치를 정의하여 최종적으로 세분화된 표면의 모양을 결정하게 된다


[domain("quad")]
OutputType domainShader(HullOutputType patchData, float2 uv : DOMAIN_LOCATION) 
   {
    OutputType output;

    // 세분화된 정점의 위치 계산
    float3 pos = lerp(
        lerp(patchData.controlPoints[0].position, patchData.controlPoints[1].position, uv.x),
        lerp(patchData.controlPoints[3].position, patchData.controlPoints[2].position, uv.x),
        uv.y);

    output.position = float4(pos, 1.0f);
    return output;
   }

 

 

https://blog.nullbus.net/83

반응형

'Technical Report > Unity Shader' 카테고리의 다른 글

Autodesk Media & Entertainment 2012  (4) 2012.07.23
lighting 관련 몇가지 용어  (0) 2012.07.15
Cg Shader Programming  (0) 2012.07.12
120710 아트 세미나 Photoshop Tool and Tip  (0) 2012.07.08
binormal, tangent normal  (5) 2012.05.24