--- id: wiki-2026-0508-pbr title: PBR (Physically Based Rendering) category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Physically Based Rendering, PBR shading, metallic-roughness] duplicate_of: none source_trust_level: A confidence_score: 0.95 verification_status: applied tags: [graphics, shading, rendering, materials] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: hlsl framework: unreal-engine --- # PBR (Physically Based Rendering) ## 매 한 줄 > **"매 빛-표면 상호작용을 물리법칙(에너지 보존, microfacet, Fresnel)에 따라 모델링"**. 매 2014 Disney/Burley BRDF + Unreal/Frostbite 의 industry standardization → 매 2026 의 모든 real-time 엔진의 default. 매 metallic-roughness workflow 의 universal. ## 매 핵심 ### 매 핵심 원칙 1. **에너지 보존**: 매 reflected + transmitted ≤ incoming. 2. **Fresnel**: 매 grazing angle에서 reflectance ↑. 3. **Microfacet**: 매 표면을 미세 mirror 의 분포로 모델 (NDF + G + F). 4. **Linear-space lighting**: 매 sRGB → linear → tonemap. ### 매 BRDF 구조 (Cook-Torrance) ``` f = kd * Lambert + ks * (D * G * F) / (4 * NoL * NoV) ``` - **D**: Normal Distribution Function — Trowbridge-Reitz GGX (default). - **G**: Geometry / Shadowing — Smith / Schlick-GGX. - **F**: Fresnel — Schlick approx. - **kd**: diffuse weight = (1 - F) * (1 - metallic). ### 매 workflow - **Metallic-Roughness** (Unreal, glTF, Substance default): albedo + metallic + roughness + normal + ao. - **Specular-Glossiness** (legacy): 매 deprecated. - **Clearcoat / Sheen / Anisotropy / Subsurface** = 매 extensions (glTF KHR\_\*). ### 매 IBL (Image-Based Lighting) - 매 environment HDR → 매 prefiltered specular cubemap (split-sum approximation) + 매 diffuse irradiance. - 매 BRDF LUT (NoV × roughness) precompute. ## 💻 패턴 ### Core PBR PS (HLSL, UE-style) ```hlsl float3 F_Schlick(float3 F0, float VoH) { return F0 + (1 - F0) * pow(1 - VoH, 5); } float D_GGX(float NoH, float a) { float a2 = a * a; float d = NoH * NoH * (a2 - 1) + 1; return a2 / (PI * d * d); } float V_SmithGGX(float NoV, float NoL, float a) { float a2 = a * a; float gv = NoL * sqrt(NoV * NoV * (1 - a2) + a2); float gl = NoV * sqrt(NoL * NoL * (1 - a2) + a2); return 0.5 / (gv + gl + 1e-5); } float3 PBR(float3 albedo, float metallic, float roughness, float3 N, float3 V, float3 L, float3 lightColor) { float3 H = normalize(V + L); float NoL = saturate(dot(N, L)); float NoV = saturate(dot(N, V)); float NoH = saturate(dot(N, H)); float VoH = saturate(dot(V, H)); float a = roughness * roughness; float3 F0 = lerp(0.04, albedo, metallic); float D = D_GGX(NoH, a); float V_ = V_SmithGGX(NoV, NoL, a); float3 F = F_Schlick(F0, VoH); float3 spec = D * V_ * F; float3 kd = (1 - F) * (1 - metallic); float3 diff = kd * albedo / PI; return (diff + spec) * lightColor * NoL; } ``` ### IBL (split-sum) ```hlsl float3 IBL(float3 N, float3 V, float roughness, float3 albedo, float metallic, TextureCube prefiltered, Texture2D brdfLUT, TextureCube irradiance) { float NoV = saturate(dot(N, V)); float3 R = reflect(-V, N); float3 F0 = lerp(0.04, albedo, metallic); float3 F = F_Schlick(F0, NoV); float mip = roughness * 6.0; // 매 mip count - 1 float3 specIBL = prefiltered.SampleLevel(s, R, mip).rgb; float2 brdf = brdfLUT.Sample(s, float2(NoV, roughness)).rg; float3 spec = specIBL * (F * brdf.x + brdf.y); float3 kd = (1 - F) * (1 - metallic); float3 diff = kd * albedo * irradiance.Sample(s, N).rgb; return diff + spec; } ``` ### glTF PBR material loader (Three.js / WebGPU) ```ts const mat = new THREE.MeshPhysicalMaterial({ map: albedo, // sRGB metalnessMap: metalRough, normalMap: normal, roughnessMap: metalRough, // packed: G=rough, B=metal (glTF spec) aoMap: ao, clearcoat: 0.5, clearcoatRoughness: 0.1, // KHR_materials_clearcoat sheen: 0.3, sheenColor: new THREE.Color(0xff8888), // KHR_materials_sheen }); ``` ### Disney "Principled" BSDF param mapping ```python # Blender Principled BSDF — 매 artist-friendly mat.inputs["Base Color"] = albedo mat.inputs["Metallic"] = metallic # 0 dielectric, 1 metal mat.inputs["Roughness"] = roughness # 0 mirror, 1 chalk mat.inputs["IOR"] = 1.5 # dielectric F0 = ((ior-1)/(ior+1))^2 mat.inputs["Specular IOR Level"] = 0.5 # 매 dielectric strength mat.inputs["Coat Weight"] = 0.0 mat.inputs["Sheen Weight"] = 0.0 mat.inputs["Subsurface Weight"] = 0.0 ``` ### Texture authoring rules ``` albedo: sRGB, no lighting baked, dielectric ≥ 30 sRGB, metal ≥ 200 sRGB metallic: linear, binary 가까이 (0 or 1) — 매 dielectric/metal 의 mix X roughness: linear, 0.04 floor (perfect mirror 회피) normal: linear, OpenGL or DirectX convention 통일 ao: linear, multiplied with diffuse only ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | Real-time game | Metallic-Roughness GGX + IBL | | Skin / wax | + Subsurface scattering | | Car paint / lacquer | + Clearcoat | | Cloth / velvet | + Sheen | | Brushed metal / hair | + Anisotropy | | Path-traced offline | Disney Principled (super-set) | **기본값**: 매 GGX + Smith + Schlick + metallic-roughness + IBL split-sum. ## 🔗 Graph ## 🤖 LLM 활용 **언제**: 매 photoreal real-time/offline rendering. 매 cross-engine asset (glTF). 매 artist 의 physical intuition. **언제 X**: 매 stylized non-photoreal (NPR/toon) — 매 PBR 위에 override 또는 별도 model. ## ❌ 안티패턴 - **gamma 안 맞춤**: 매 albedo 의 linear 처리 → 매 wash-out / overbright. - **Metallic 0.5**: 매 binary 가까이가 정답. 0.5 = 매 무의미 (전이 영역 없음). - **Roughness 0.0**: 매 NaN / fireflies. floor 0.04 + clamp. - **AO multiplied with specular**: 매 specular AO 별도 계산 필요. - **F0=0.04 for everything**: 매 metal은 albedo 색이 F0. ## 🧪 검증 / 중복 - Verified (Disney 2012 Burley paper, UE5 docs, Filament PBR pipeline doc, glTF 2.0 spec, Real-Time Rendering 4th ed). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — Cook-Torrance + IBL + glTF workflow |