본문으로 바로가기

Bent Normal 이란?

category Technical Report/Graphics Tech Reports 2020. 3. 13. 00:52
반응형

BentNormal은 원래 노멀을 수정해 만들어진 벡터값으로, 다른 오브젝트(또는 지오메트리, 복셀)에 의해 막혀있지 않는 현재 픽셀의 평균 방향, 즉 들어오는 빛의 주 방향의 평균값을 가리킨다. Ambient Occlusion과 관련이 있음을 알 수 있다. 계산공식은 아래와 같다.

 

 

그중 에서 w 방향으로 현재 점 x 의 가시성, 즉 막혔는지 여부를 판단하게 된다. 이 공식은 기본적으로 일반적인 AO 계산 공식과 동일. 몬테카를로 적분[각주:1]을 사용하여 이산법을 사용하면 제한된 샘플링에서 실행 가능한 벤트 정규 계산 공식을 얻을 수 있다.[각주:2]

 

 

Sampling diagram of Bent Normal (It can be seen from the figure that compared with the original Normal(blue), Bent Normal (yellow) has been trimmed to the right in consideration of the surrounding geometric voxel distribution)

 

What's the use of Bent Normal?

벤트 노멀은 일반적으로 두 가지 목적으로 사용될 수 있다.

원래 노멀을 변경하고 라이트의 주요 통과 방향을 기록하는데 사용되므로 환경맵을 샘플링 할 때 샘플링 된 라이트를 보다 최적화 된 방향에서 계산할 수 있다. 노멀 셰이딩을 수행하기 위해 일반적인 노멀을 대체하는데 사용되므로 노멀임에도 불구하고 앰비언트 오클루전과 비슷한 부드러운 그림자 효과를 어느 정도 얻을 수 있다.

 

How to calculate Bent Normal?

일반적으로 벤트 노멀을 계산하는 방법은 두 가지가 있다.


첫 번째 방법은 전통적인 오프라인 샘플링 방법이다. 여기에서의 작업은 기존 오프라인 앰비언트 오클루젼 계산 방법과 동일한데, 씬의 각 표면에서 샘플을 채취하고 위의 몬테 카를로 적분은 구부러진 노멀을 scene의 오클루젼을 고려하여 평균적으로 얻을 수 있다.

 

두 번째 방법은 현재 널리 사용되는 다양한 그래픽 알고리즘의 화면 공간(screen space) 기반 방법이다. 장점은 빠르며 단점은 정확하지 않다. 게임에서 실시간 AO 계산을 얻기 위해 화면 공간을 기반으로 한 많은 SSAO 알고리즘이 제안되었으며 AO 계산은 Bent Normal 계산과 관련이 있으므로 일반 SSAO 포스트 프로세싱 패스를 사용하여 Bent를 전달할 수 있으며, 추가 비용으로 빠르게 계산하게 된다(그러나 노멀 버퍼의 샘플이 하나 더 있게 되므로 적절한 방법으로 이를 피하는게 좋다). 특정 작업은 각 화면 샘플링 지점에서 해당 AO 값이 Normal의 오프셋으로 변환된다는 점을 제외하고 AO 계산과 유사하다.


다음은 원본 Normal과 Bent Normal의 비교 차트이다 (Bent Normal은 오프라인 몬테카를로 알고리즘을 사용하여 계산되며 최적화 된 샘플링 방법이 있으므로 추측된 노이즈가 있다) :

 

위의 노멀 비교 이미지에서 볼 수 있듯 노멀 곡률이 0보다 크거나 같은 표면의 경우 벤트노멀은 원래 노멀과 다르게 된다. 이미지에서 다른 오브젝트는 복셀에 의해 차폐되지 않았으며, 이러한 표면 홈의 경우 벤트 노멀은 그에 따라 원래 노멀을 수정하며 SSAO 작업을 수행 할 때, 이 영역에서도 해당 AO가 생성되게 된다.

 

아래 이미지는 Normal과 Bent Normal을 사용해 렌더링된 이미지의 비교.

 

 

 

Unity HDRP에서 사용된 BentNormal 적용(좌)와 적용하지 않은 경우(우) This image compared applied Bent normal and not applied(right)

 

Reflecion Occlusion compared applied and not.

 

 

Unity에서 Bent Normal Baker를 구현한 포스팅이 있다.

 

중국어 포스팅 참고 :
https://blog.uwa4d.com/archives/USparkle_Bent_Normal.html

 

Unity-based Bent Normal Baker

Substaince Designer는 직접 구울 수 있지만 (SSAO 포스트 프로세싱을 사용할 수도 있지만 당분간은 욕심이 없습니다...)하지만 재미를 위해 Unity에서 베이킹을 구현하려고 합니다.

 

 

GPU Gems mentions:

This method is based on a view-independent preprocess that computes occlusion information with a ray tracer and then uses this information at runtime to compute a fast approximation to diffuse shading in the environment.

 

이 방법은 레이 트레이서를 사용해 오클루젼 정보를 계산한 다음 런타임에 이 정보를 사용해 환경에서 확산 음영에 대한 빠른 근사치를 계산하는 뷰 독립적인 전처리를 기반으로합니다.


나는 의아해했다 : 가시성 계산을 Unity의 ShadowMap으로 바꿀 수 있지 않을까?

 

1. Spherical parallel light distribution을 생성합니다 (실제로 베이킹 할 때 이보다 훨씬 밀도가 높습니다)

 

2. 다른 각도에서 오브젝트를 렌더링 할 때마다 Shadow map을 사용하여 각 픽셀의 가시성을 얻을 수 있습니다. 몇 가지 주의할 사항이 있습니다.


2UV로 출력(이 트릭은 유니티 모델 에디터의 blog extention을 참조할 수 있습니다), Cull / ZTest 등을 끄는 것을 잊지 마십시오.

라이트에 하드 섀도우를 사용하고, 필요한 경우 바이어스 및 기타 매개 변수를 조정하십시오.

스크린 스페이스 쉐도우를 사용하지 마십시오.

섀도 맵의 활용을 극대화하려면 카메라 위치와 모델 크기에 주의하십시오.

 

 


PS. 처음에는 Graphics.DrawMeshNow를 사용하여 RenderTexture에 직접 그렸습니다. 나중에 많은 엔진들이 자동으로 통과하지 못한다는 것을 알았습니다. 마지막으로 방법을 변경하고 Camera.targetTexture를 직접 설정했습니다. 그러면 Camera.Render는 더 걱정할 필요가 없습니다.


float으로 새 RenderTexture를 만든 다음 Blend One One의 경우 모든 각도에서 다시 렌더링합니다 : 그림자가 없다면, RGB output light direction, A output 1 count ; 마지막으로 RGB / A 에 저장할 수 있습니다.

 

최종 결과는 에지 샘플링 문제를 해결하기 위해 여러 번 Dilation해야합니다.

 

Bake results comparison

 

Substance Design의 사진과 비교할 때 방향에 문제가 없다고 말할 수는 있지만 여전히 매우 많은 다른 디테일이 있습니다.

 

좌 : Unity baked / 우 : Substance designer 처리효과

 

 

약간의 이상한 노이즈는 심한 언더 샘플링에서 비롯됩니다.

Substaince Designer가 생성한 BentNormal 때문에 일부 이미지 공간을 조작했다고 심각하게 의심했습니다.

어떤 부분은 2UV에 해당하는 정보가 없지만 가치가 있습니다. 이는 매우 흥미롭습니다.

Substaince Designer는 ZB 하이 모드를 사용하므로 이방식으로 Unity로 가져오기가 번거롭습니다.

 


그러나 다음과 같은 이점도 있습니다. Substaince Designer는 표준화 된 Bent Normal을 내보냅니다. 직접 생성했을 때 AO 강도를 저장하는데 사용된 B 채널을 메시 AO로 사용할수도 있습니다. 다른 하나는 Unity에서 베이킹하는 것이 정말 간단하고 빠르게 반복된다는 것입니다. 자신의 프로젝트에 넣고 백라이트가 있을때 Diffuse IBL 부분의 효과를 비교하십시오 (당분간 스카이 라이트만 사용됨). 노멀이 훨씬 매끄럽기 때문에 빛샘이 훨씬 좋습니다.

 

 

Left: no bent normal effect / Right: bent normal effect


 

향후 작업


현재 결론은 모바일 게임에서 사용할 수 있다는 것입니다. 어쨌든 키워드가 낮고 꺼져 있습니다.

다음으로 에너지가 있다면 반복적으로 전처리하고 구워야합니다. Unity의 베이커 솔루션에는 2차 개발을 위한 공간이 많이 있습니다. 예를 들어 AO, Normal 등은 사용자 정의 베이킹을 완벽하게 구현할 수 있습니다 (물론 다른 방법은 Baker를위한 C++ Ray Tracing을 작성하는 것입니다).

SD / SP / Max와 같은 고급 DCC 도구의 가장 큰 문제는 공용 API가 많지 않다는 것입니다. output을 사용자가 커스텀하려면 매우 편리하지 않습니다. 최종 결과만 처리하기 쉬운 경우 중간 결과를 얻으려면 매우 힘들 수 있습니다. 이것이 Unity의 Baker에서 가장 중요한 것입니다.

그건 그렇고, 아래 이미지에서 표면에 3가지 라이트 소스가 적용된
Vray 3DS Max에서 베이킹과 Unity의 Enlighten 베이킹 사이의 두 이미지의 비교는 매우 흥미롭습니다. (왼쪽 모델 신발 / 벨트의 오류).


 


이것의 가장 큰 장점은 커스터마이징과 원활한 워크 플로우 통합입니다.
Jihu Technology의 433 번째 기사입니다. 그의 기여에 대한 저자 Qian Kanglai에게 감사합니다

  1. 임의로 생성된 난수를 이용해 함수의 값을 샘플링하고, 이를 평균화하여 적분값(면적값)을 근사하는 방식으로, 복잡한 다차원 함수의 적분을 계산할 때 유용하며, 구분구적법의 경우 차원이 증가함에 따라 오차가 발생하기 때문에 이 방식을 사용한다.

    또한, glsl 의 경우 rand 함수를 통해 랜덤값을 임의로 생성하기 쉽지만 hlsl의 경우 아래 코드를 활용해 랜덤 값을 생성한다. 자세한 내용은 github 링크 참고


    float rand()
    {
        float result = frac(sin(_Seed / 100.0f * dot(_Pixel, float2(12.9898f, 78.233f))) * 43758.5453f);
        _Seed += 1.0f;
        return result;
    }

    Reference link
    https://iquilezles.org/articles/simplepathtracing/

    https://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/monte-carlo-methods-in-practice/monte-carlo-methods.html

    https://github.com/aceyan/Unity3D_Path_Tracer/tree/master

    [본문으로]



  2. // Pseudo-code for Monte Carlo Integration in GLSL (Fragment Shader)
    uniform sampler2D environmentMap;
    uniform int numSamples; // Number of Monte Carlo samples

    vec3 MonteCarloIntegration(vec3 normal) {
        vec3 result = vec3(0.0);
        
        for (int i = 0; i < numSamples; ++i) {
            // Generate a random direction vector (importance sampling)
            vec3 sampleDir = RandomDirection(normal);
            
            // Sample the environment map based on the random direction
            vec3 sampleColor = texture(environmentMap, sampleDir).rgb;
            
            // Accumulate the sampled color
            result += sampleColor;
        }
        
        // Average the result based on the number of samples
        result /= float(numSamples);
        
        return result;
    }

    void main() {
        vec3 normal = normalize(vNormal); // Surface normal
        vec3 color = MonteCarloIntegration(normal);
        
        // Output the final color
        gl_FragColor = vec4(color, 1.0);
    }

     

    • numSamples : 샘플 수가 많을수록 결과가 더 정확해지지만, 계산 비용이 증가한다.
    • RandomDirection(normal) : 주어진 표면 노멀을 기준으로 무작위 방향 벡터를 생성.
    • texture(environmentMap, sampleDir) : 생성된 방향에서 환경 맵을 샘플링하여 조명을 계산.

    [본문으로]

반응형