본문 바로가기

그래픽스

[그래픽스] 부드러운 그림자 - PCF

앞서 작성한 그림자 글대로 그림자를 구현하면 가장자리가 울퉁불퉁해 보인다.

이것을 해결하기 위해 함수를 조금 수정해보자.

 

        /*
        // 3. 쉐도우맵에서 값 가져오기
        float depth = shadowMap.Sample(shadowPointSampler, lightTexcoord).r;
        
        // 4. 가려져 있다면 그림자로 표시
        if (depth + 0.001 < lightScreen.z)
            shadowFactor = 0.0;
        */
        
        shadowMap.SampleCmpLevelZero(shadowCompareSampler, lightTexcoord.xy, lightScreen.z - 0.001).r;

 

3번과 4번 과정을 SampleCmpLevelZero라는 함수로 합칠 수 있다. shadowPointSampler 대신 shadowCompareSampler을 쓰고 인자로 그림자로 가려지는 기준이 되는 값을 넣어준다. (lightScreen.z - 0.001) 그리고 shadowFactor을 기존처럼 0이나 1 둘 중 하나로 정하는 것이 아니라 주변 픽셀 4개의 값에 따라 blend를 하기 때문에 부드럽게 보인다.

 

shadowCompareSampler은 SamplerState가 아닌 SamplerComparisonState이고 desc에서 Filter 설정할때도

 FILTER_COMPARISON_MIN_MAG...로 설정한다. ComparisonFunc 도 LESS_EQUAL로 설정해줘야 한다.

 

 

        uint width, height, numMips;
        shadowMap.GetDimensions(0, width, height, numMips);
        float dx = 5.0 / (float) width;
        float percentLit = 0.0;
        const float2 offsets[9] =
        {
            float2(-1, -1), float2(0, -1), float2(1, -1),
            float2(-1, 0), float2(0, 0), float2(1, 0),
            float2(-1, +1), float2(0, +1), float2(1, +1)
        };
        
        [unroll]
        for(int i=0;i<9;i++)
        {
             percentLit += shadowMap.SampleCmpLevelZero(shadowCompareSampler, 
             lightTexcoord.xy, + offsets[i] * dx, lightScreen.z - 0.001).r;
        }
        
        shadowFactor = percentLit / 9.0;

 

주위 9픽셀을 돌면서 9번 샘플링하면 더 결과가 부드러워진다. 이것을 Percentage Closer Filtering이라고 한다. 

index를 random으로 하거나 샘플을 더 늘리면 더 자연스러운 효과를 만들어낼 수 있다.