TECHARTNOMAD | TECHARTFLOWIO.COM

UNITY3D

Unity3D URPDual Lobe GGX works

jplee 2025. 2. 18. 01:03

2020년 봄 쯤 테스트.

구현 코드는 매우 단순하게 처리 된 형식.

Dual Lobe Debug view.

// Specular term
	half perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness);
	half roughness = PerceptualRoughnessToRoughness(perceptualRoughness);

	half a = roughness;
	half a2 = a*a;

	half d = nh * nh * (a2 - 1.h) + 1.00001h;
	half specularTerm = a2 / (max(0.1h, lh*lh) * (roughness + 0.5h) * (d * d) * 4);
	
	float Lobe0Roughness = max(saturate(specularTerm ), 0.002f);
	float Lobe1Roughness = clamp(specularTerm, 0.0, 100.0) *  _LobeWeight;
	
#if defined(DUAL_LOBE_GGX)
    float sAO = saturate(-0.3f + nv * nv);
    sAO =  lerp(pow(ao, 8.00f), 1.0f, sAO);
	half SpecularOcclusion = sAO;
	specularTerm = (Lobe0Roughness / 2 + Lobe1Roughness) * SpecularOcclusion ;
	return half4(specularTerm.xxx,1);
#else
	specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
#endif

 

2025년 구현 형식.

#ifndef DUAL_SPECULAR_LOBE_BRDF_INCLUDED
#define DUAL_SPECULAR_LOBE_BRDF_INCLUDED

float3 DualSpecularLobe(float3 N, float3 V, float3 L, float Roughness1, float Roughness2, float Weight1, float3 F0)
{
    // 뷰 벡터 정규화
    V = normalize(V);
    // 라이트 벡터 정규화
    L = normalize(L);

    // 뷰(V)와 라이트(L) 사이의 반각 벡터(H)
    float3 H = normalize(V + L);

    // 슬릭 근사화(Schlick Approximation)를 사용한 프레넬 항
    float3 F = F0 + (1.0 - F0) * pow(1.0 - saturate(dot(V, H)), 5.0);

    // 두 개의 GGX 배포 기능(NDF)
    float D1 = GGX_Distribution(N, H, Roughness1);
    float D2 = GGX_Distribution(N, H, Roughness2);

    // 기하학/가시성 항, Cook-Torrance 모델 기반
    float G = Geometry_Smith(N, V, L, Roughness1);

    // 두 로브를 결합
    float Lobe1 = Weight1 * D1 * G / (4.0 * max(0.001, dot(N, V)) * max(0.001, dot(N, L)));
    float Lobe2 = (1.0 - Weight1) * D2 * G / (4.0 * max(0.001, dot(N, V)) * max(0.001, dot(N, L)));

    // 두 로브의 가중합
    return F * max(0.0, dot(N, L)) * (Lobe1 + Lobe2);
}

// GGX 디스트리뷰션 펑션
float GGX_Distribution(float3 N, float3 H, float roughness)
{
    float alpha = roughness * roughness;
    float alpha2 = alpha * alpha;
    float NdotH = saturate(dot(N, H));
    float denom = NdotH * NdotH * (alpha2 - 1.0) + 1.0;
    return alpha2 / (3.14159265359 * denom * denom);
}

// 기하학 항 (Smith)
float Geometry_Smith(float3 N, float3 V, float3 L, float roughness)
{
    float k = (roughness + 1.0) * (roughness + 1.0) * 0.125;
    return Geometry_SchlickGGX(N, V, k) * Geometry_SchlickGGX(N, L, k);
}

float Geometry_SchlickGGX(float3 N, float3 V, float k)
{
    float NdotV = saturate(dot(N, V));
    return NdotV / (NdotV * (1.0 - k) + k);
}
#endif