TECHARTNOMAD | TECHARTFLOWIO.COM

GRAPHICS PROGRAMMING

VMF Diffuse: A Unified Rough Diffuse BRDF 간단 해석.

jplee 2025. 1. 17. 15:38

VMF Diffuse: A unified rough diffuse BRDF

참조.

 

VMF Diffuse: A Unified Rough Diffuse BRDF

 

diglib.eg.org

 

VMF Diffuse는 2024년 유진 디옹(Eugene d'Eon)과 안드레아 바이들리히(Andrea Weidlich)가 발표한 통합 러프 디퓨즈 BRDF(Bidirectional Reflectance Distribution Function) 모델입니다. 이 모델은 von Mises-Fischer(NDF)를 사용하여 다양한 거칠기를 가진 표면에서의 빛 산란을 정확하게 표현. 

주요 특징:

통합 표현: 매끄러운 램버시안(Lambertian) 표면부터 중간 정도의 거친 높이 필드, 그리고 매우 거칠거나 다공성인 재질까지 원활하게 표현된다. 


von Mises-Fischer NDF 사용: 폰 미제스-피셔(VMF) 정규 분포 함수(NDF)는 특히 표면 거칠기와 산란을 시뮬레이션하기 위한 BRDF와 같은 렌더링 목적으로 방향성 분포를 모델링하는 데 사용됩니다. 단위 구체에 대한 확률 밀도 함수.

최대 거칠기에서의 단순화: 최대 거칠기에서는 최근 제안된 Lambert-sphere BRDF로 단순화되어 일관성을 유지. 

모델 검증: 무작위로 배치된 램버시안 구로 구성된 기하학적 구조에서의 산란 시뮬레이션과 비교하여, 기존의 거친 Beckmann BRDF보다 높은 거칠기 수준에서 향상된 정확도. 


이 모델은 이전 BRDF의 한계를 극복하여 복잡한 거칠기 특성을 가진 표면의 렌더링에 있어 더욱 정확하고 다재다능한 접근 방식을 제공합니다. 이는 컴퓨터 그래픽스와 현실적인 재질 렌더링 분야에서 유용한 도구로 평가 됨. 

 

VMF Diffuse: A unified rough diffuse BRDF | Research

We present a practical analytic BRDF that approximates scattering from a generalized microfacet volume with a von Mises-Fischer NDF. Our BRDF seamlessly blends from smooth Lambertian, through moderately rough height fields with Beckmann-like statistics and

research.nvidia.com

스터디 케이스

유니티 엔진의 urp 셰이더로 구현

VMF Diffuse BRDF 공식 요약

VMF Diffuse는 von Mises-Fisher Normal Distribution Function (NDF)를 사용하여 다음과 같은 공식을 기반으로 작동

Diffuse BRDF Core

 

 

  • : 표면의 법선 벡터
  • : 빛의 방향 벡터
  • k: 러프니스 파라미터 (거칠기에 따라 조정)
  • 는 거칠기 α\alpha를 통해 조정 됨.
  • I0(k): 0차 수정된 베셀 함수
렌더링에서 VMF NDF는 확산 및 스페큘러 리플렉션의 러프니스 모델링을 위해 기존 NDF(예: Beckmann 또는 GGX)를 대체할 수 있다.

von Mises-Fisher (VMF) 분포는 주로 확률 분포 모델로 사용되며, 특정 벡터(예: 표면 법선 벡터)에 대한 빛의 산란을 모델링하는 데 유용. 

float vonMisesFisher(float3 n, float3 l, float roughness)
{
    float k = (1.0 - roughness) * 20.0; // 러프니스 값에 따른 샤프니스 맵핑
    return exp(k * dot(n, l)) / (4.0 * UNITY_PI * I0(k)); // 정규화된 분포 함수 계산
}

I0(k)는 수정된 베셀 함수의 제로차 항 (I₀) 을 나타내며, 이 함수는 VMF 분포에서 중요한 역할.

k 값이 커질수록, 즉 표면이 거칠어질수록, 빨리 증가하는 경향을 보인다.

VMF 분포에서 I0(k)는 다음과 같이 정의.

위 식은 k가 주어진 경우 정규화 상수 역할.

정확한 계산은 매우 복잡하므로, 일반적으로 I0(k)는 근사 계산으로 해결됩니다. 다음은 대표적인 근사 방법이다.

1. 저차 근사식

값이 작을 때 사용

 

2. 고차 근사식

k 값이 클 때 사용.

float I0(float k)
{
    if (k < 10.0) 
    {
        // 작은 k 값에 대한 저차 근사
        return 1.0 + (k * k) / 4.0 + (k * k * k * k) / 64.0;
    }
    else 
    {
        // 큰 k 값에 대한 고차 근사
        return exp(k) / sqrt(2.0 * UNITY_PI * k);
    }
}

이렇게 근사식을 사용하여 I0(k) 를 효율적으로 계산하고, 그것을 VMF Diffuse BRDF 모델에서 활용할 수 있습니다.

I0(k) 는 VMF 분포에서 빛의 강도를 계산할 때 핵심적인 역할을 하며, 정확한 조명 효과를 위해서는 이 함수를 세밀하게 계산하는 것이 중요합니다. VMF 기반의 Diffuse BRDF 모델에서는 이 값이 빛의 확산 정도를 결정하는 데 영향을 미칩니다.

필요에 따라 이 함수를 정확히 구현하거나 근사 계산을 사용할 수 있으며, 더 복잡한 계산이 필요한 경우 고급 수치 해석 기법을 사용할 수 있음.

스터디 케이스용 코드

Shader "VMFDiffuse"
{
    Properties
    {
        _MainTex("Albedo (RGB)", 2D) = "white" {}
        _Roughness("Roughness", Range(0.01, 1.0)) = 0.5
    }
    SubShader
    {
        Tags
        {
            "RenderType" = "Opaque"
            "RenderPipeline" = "UniversalPipeline"
            "UniversalMaterialType" = "Lit"
            "IgnoreProjector" = "True"
        }
        LOD 300

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata_t
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float3 worldNormal : TEXCOORD1;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            

            CBUFFER_START(UnityPerMaterial)
            float4 _MainTex_ST;
            float _Roughness;
            CBUFFER_END

            

            v2f vert (appdata_t v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex); // 클립 공간으로 변환
                o.worldNormal = UnityObjectToWorldNormal(v.normal); // 월드 공간으로 노멀 변환
                o.uv = TRANSFORM_TEX(v.uv, _MainTex); // UV 좌표 변환
                return o;
            }

            float I0(float k)
            {
                if (k < 10.0) 
                {
                    // 작은 k 값에 대한 저차 근사
                    return 1.0 + (k * k) / 4.0 + (k * k * k * k) / 64.0;
                }
                else 
                {
                    // 큰 k 값에 대한 고차 근사
                    return exp(k) / sqrt(2.0 * UNITY_PI * k);
                }
            }


            float vonMisesFisher(float3 n, float3 l, float roughness)
            {
                float k = (1.0 - roughness) * 20.0; // 러프니스 값에 따른 샤프니스 맵핑
                return exp(k * dot(n, l)) / (4.0 * UNITY_PI * I0(k)); // 정규화된 분포 함수 계산
            }
            

            float3 ApplyVMFDiffuse(float3 worldNormal, float3 lightDir, float roughness)
            {
                float NdotL = saturate(dot(worldNormal, lightDir)); // 노멀과 조명 방향의 내적
                return vonMisesFisher(worldNormal, lightDir, roughness) * NdotL; // vMF 확산 반사율 적용
            }

            float4 frag (v2f i) : SV_Target
            {
                
                float3 albedo = tex2D(_MainTex, i.uv).rgb; // 텍스처에서 알베도 색상 추출
                float3 normal = normalize(i.worldNormal); // 월드 노멀 정규화
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); // 월드 공간의 조명 방향 정규화
                float3 lightColor = float3(1,1,1); // 조명 색상 설정

                // vMF Diffuse BRDF 계산 적용
                float3 diffuse = ApplyVMFDiffuse(normal, lightDir, _Roughness);
                float3 finalColor = albedo * diffuse * lightColor; // 최종 색상 계산

                return float4(finalColor, 1.0); // 최종 RGBA 반환
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}