🎨DirectX

[DX] Texture Sampler (Map.Sample 함수), SamplerState(Address/Filter), 선형보간

공대 컴린이 2023. 2. 4. 23:09
728x90

🎨 Texture Sampler

쉐이더를 이용하여 Texture를 출력할 때 픽셀 쉐이더 함수에서 Map.Sample(SamplerState, input.Uv) 함수를 수행하였다.

 

Sample은 uv의 비율에 따라 정점에 이미지를 잘라서 붙여주는, 발라주는 역할을 수행한다.
즉, Map.Sample 함수에 들어가는 uv 값이 (0,0) ~(1,1)이면 다 출력되고 (0,0) ~ (0.5,0.5)이면 절반만 출력될 것이다.

 

또한 원래 이미지를 0부터 1까지 출력해야 온전한 이미지가 출력되는데, 0부터 0.5까지만 출력하면 0.5크기에 맞춰서 이미지가 확대된다.

다른 예시로는 화면의 크기가 (100, 100)일때 이미지가 (50, 50)이라면 이미지가 100만큼 확대되어 붙어질 것이다. 반대로 이미지가 (200, 200)이라면 100만큼 축소되어 붙어질 것이다.

즉, 두번째 Sample의 역할은 확대/축소 기능을 구현하는 것이다.

 

또한 Sample의 기능은 매개변수로 들어가는 Sampler에 따라 달라질 수 있다.

📘 SamplerState

Sample 함수의 첫번째 매개변수에 들어가는 SamplerState에는 Filter 모드, Address 모드가 있다.

  • Address 모드
    Sample의 기능 중 하나인 확대/축소 역할을 수행할 때, 출력할 크기안에 이미지가 다 그려지지않고 남은 공간이 있을때 어떻게 빈공간을 채울것인가의 옵션을 설정하는 것이 Address 모드이다.
  • Filter 모드
    이미지가 확대될 때 픽셀의 중간 값들을 어떻게 채워줄지 결정하거나, 이미지가 축소되어 픽셀들이 사라질 때 어떤식으로 픽셀을 지울지 결정하는 것이 Filter 모드이다.

    (중간값을 채워주는 것을 "보간(Interpolation)" 이라고 한다)

💡 D3D11_FILTER 를 보는 방법

Shader Code 작성 예시

MIN_LINEAR_MAP_POINT_MIP_LINEAR 와 같은 명령어가 있다면
축소될때는 LINEAR 방식으로 채우고, MAP은 POINT 방식으로 채우고 다시 MIP이 일어날땐 RINEAR방식으로 채우라는 의미의 명령어다.
Linear랑 Point를 제외하고 Anistropic이라는 옵션도 있고, Msaa 라는 옵션도 있다.

💡 선형보간식 (Linear Interpolation)

(1 - t) * A + (t * B) 

t가 0이라면 결과는 A
t가 1이라면 결과는 B
t가 0.5라면 결과는 0.5A + 0.5B
이런식으로 비율에 맞게 결과값을 도출하는 것을 선형보간이라고 한다.

 

이러한 선형보간을 Filter 모드에 적용하여 출력하는 명령어가 LINEAR 방식이다.


📄 TextureSampler.fx (Shader Code)

matrix World;
matrix View;
matrix Projection;
Texture2D Map;

struct VertexInput
{
    float4 Position : Position;
    float2 Uv : Uv;
};

struct VertexOutput
{
    float4 Position : SV_Position;
    float2 Uv : Uv;
};

VertexOutput VS(VertexInput input)
{
    VertexOutput output;
    output.Position = mul(input.Position, World);
    output.Position = mul(output.Position, View);
    output.Position = mul(output.Position, Projection);
    
    output.Uv = input.Uv;
    
    return output;
}

SamplerState Samp;
float4 PS(VertexOutput input) : SV_Target
{
    // 두개의 이미지를 띄우고 싶을때 이런식으로 나타낼수도 있음
    //if (input.Uv.x < 0.5f)
    //    return float4(1, 0, 0, 1);
    
    return Map.Sample(Samp, input.Uv);
}


uint Filter;

SamplerState Sampler_Filter_Point
{
    Filter = MIN_MAG_MIP_POINT;
};

SamplerState Sampler_Filter_Linear
{
    Filter = MIN_MAG_MIP_LINEAR;
};

float4 PS_Filter(VertexOutput input) : SV_Target
{
    if (Filter == 0)
        return Map.Sample(Sampler_Filter_Point, input.Uv);
    
    if (Filter == 1)
        return Map.Sample(Sampler_Filter_Linear, input.Uv);
    
    return Map.Sample(Samp, input.Uv);
}

uint Address;

SamplerState Sampler_Address_Wrap
{
    AddressU = Wrap;
    AddressV = Wrap;
};

SamplerState Sampler_Address_Mirror
{
    AddressU = Mirror;
    AddressV = Mirror;
};

SamplerState Sampler_Address_Clamp
{
    AddressU = Clamp;
    AddressV = Clamp;
};

SamplerState Sampler_Address_Border
{
    AddressU = Border;
    AddressV = Border;

    BorderColor = float4(0, 0, 1, 1);
};

float4 PS_Address(VertexOutput input) : SV_Target
{
    if (Address == 0)
        return Map.Sample(Sampler_Address_Wrap, input.Uv);
    
    if (Address == 1)
        return Map.Sample(Sampler_Address_Mirror, input.Uv);
    
    if (Address == 2)
        return Map.Sample(Sampler_Address_Clamp, input.Uv);
    
    if (Address == 3)
        return Map.Sample(Sampler_Address_Border, input.Uv);
    
    return Map.Sample(Samp, input.Uv);
}

technique11 T0
{
    pass P0
    {
        SetVertexShader(CompileShader(vs_5_0, VS()));
        SetPixelShader(CompileShader(ps_5_0, PS()));
    }

    pass P1
    {
        SetVertexShader(CompileShader(vs_5_0, VS()));
        SetPixelShader(CompileShader(ps_5_0, PS_Filter()));
    }

    pass P2
    {
        SetVertexShader(CompileShader(vs_5_0, VS()));
        SetPixelShader(CompileShader(ps_5_0, PS_Address()));
    }
}

[ Filter 출력 결과 ]

Point 모드 (격자) Linear 모드

[ Address 출력 결과 ]

Wrap 모드 (반복) Mirror 모드 (뒤집어짐)
Clamp 모드 (도장찍고 쭉 늘린) Border 모드 (선으로 채움)

 

728x90