From 3bfc9c04b443675b67f478ff22bc408cdac60244 Mon Sep 17 00:00:00 2001 From: hhhhkrx <155431265+hhhhkrx@users.noreply.github.com> Date: Fri, 13 Dec 2024 02:05:19 +0800 Subject: [PATCH] pbr sheen (#312) * feat: pbr support sheen --- .../thinfilm/IridescenceIndirectLight.glsl | 2 +- packages/shaderlab/src/shaders/PBR.gs | 31 +++----- .../src/shaders/shadingPBR/BRDF.glsl | 73 +++++++++++++++++++ .../src/shaders/shadingPBR/FragmentPBR.glsl | 29 ++++++++ .../shaders/shadingPBR/LightDirectPBR.glsl | 5 ++ ...Probe.glsl => LightIndirectFunctions.glsl} | 26 +------ .../shaders/shadingPBR/LightIndirectPBR.glsl | 21 +++++- .../shaders/shadingPBR/ReflectionLobe.glsl | 13 +++- .../shaderlab/src/shaders/shadingPBR/index.ts | 4 +- 9 files changed, 151 insertions(+), 53 deletions(-) rename packages/shaderlab/src/shaders/shadingPBR/{LightProbe.glsl => LightIndirectFunctions.glsl} (72%) diff --git a/packages/custom-material/src/advanced-shader/thinfilm/IridescenceIndirectLight.glsl b/packages/custom-material/src/advanced-shader/thinfilm/IridescenceIndirectLight.glsl index 34610ca9..919da1b4 100644 --- a/packages/custom-material/src/advanced-shader/thinfilm/IridescenceIndirectLight.glsl +++ b/packages/custom-material/src/advanced-shader/thinfilm/IridescenceIndirectLight.glsl @@ -1,7 +1,7 @@ #define FUNCTION_SPECULAR_IBL evaluateSpecularIBLIridescence #include "BRDF.glsl" #include "./IridescenceFunction.glsl" -#include "LightProbe.glsl" +#include "LightIndirectFunctions.glsl" void evaluateSpecularIBLIridescence(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, float radianceAttenuation, inout vec3 specularColor){ diff --git a/packages/shaderlab/src/shaders/PBR.gs b/packages/shaderlab/src/shaders/PBR.gs index 7bb7c4c8..8bea8bb2 100644 --- a/packages/shaderlab/src/shaders/PBR.gs +++ b/packages/shaderlab/src/shaders/PBR.gs @@ -47,33 +47,20 @@ Shader "PBR.gs" { material_IridescenceTexture("IridescenceTexture", Texture2D); } + Header("Sheen"){ + ui_SheenIntensity("Intensity", Range(0, 1, 0.01)) = 0; + ui_SheenColor("Color", Color ) = (0, 0, 0, 0); + material_SheenRoughness("Roughness", Range(0, 1, 0.01)) = 0; + material_SheenTexture("ColorTexture", Texture2D); + material_SheenRoughnessTexture("RoughnessTexture", Texture2D); + } + Header("Common") { material_AlphaCutoff( "AlphaCutoff", Range(0, 1, 0.01) ) = 0; material_TilingOffset("TilingOffset", Vector4) = (1, 1, 0, 0); } } - - EditorMacros { - Header("Conditional Macors") { - MATERIAL_HAS_BASETEXTURE("HAS_BASETEXTURE"); - MATERIAL_HAS_ROUGHNESS_METALLIC_TEXTURE("HAS_ROUGHNESS_METALLIC_TEXTURE"); - MATERIAL_ENABLE_ANISOTROPY("ENABLE_ANISOTROPY"); - MATERIAL_HAS_ANISOTROPY_TEXTURE("HAS_ANISOTROPY_TEXTURE") - MATERIAL_HAS_NORMALTEXTURE("HAS_NORMALTEXTURE"); - MATERIAL_HAS_EMISSIVETEXTURE("HAS_EMISSIVETEXTURE"); - MATERIAL_HAS_OCCLUSION_TEXTURE("HAS_OCCLUSION_TEXTURE"); - MATERIAL_ENABLE_CLEAR_COAT("ENABLE_CLEAR_COAT"); - MATERIAL_HAS_CLEAR_COAT_TEXTURE("HAS_CLEAR_COAT_TEXTURE"); - MATERIAL_HAS_CLEAR_COAT_ROUGHNESS_TEXTURE("HAS_CLEAR_COAT_ROUGHNESS_TEXTURE"); - MATERIAL_HAS_CLEAR_COAT_NORMAL_TEXTURE("HAS_CLEAR_COAT_NORMAL_TEXTURE"); - MATERIAL_ENABLE_IRIDESCENCE("ENABLE_IRIDESCENCE"); - MATERIAL_HAS_IRIDESCENCE_THICKNESS_TEXTURE("HAS_IRIDESCENCE_THICKNESS_TEXTURE"); - MATERIAL_HAS_IRIDESCENCE_TEXTURE("HAS_IRIDESCENCE_TEXTURE"); - MATERIAL_IS_TRANSPARENT("IS_TRANSPARENT"); - MATERIAL_IS_ALPHA_CUTOFF("IS_ALPHA_CUTOFF"); - } - } - + SubShader "Default" { UsePass "pbr/Default/ShadowCaster" diff --git a/packages/shaderlab/src/shaders/shadingPBR/BRDF.glsl b/packages/shaderlab/src/shaders/shadingPBR/BRDF.glsl index 92680b99..c8eb6461 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/BRDF.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/BRDF.glsl @@ -9,6 +9,9 @@ #define NEED_TANGENT #endif +#ifdef MATERIAL_ENABLE_SHEEN + sampler2D scene_prefilteredLUT; +#endif struct SurfaceData{ // common @@ -56,6 +59,11 @@ struct SurfaceData{ float iridescenceThickness; #endif + #ifdef MATERIAL_ENABLE_SHEEN + float sheenRoughness; + vec3 sheenColor; + #endif + }; @@ -72,6 +80,13 @@ struct BRDFData{ #ifdef MATERIAL_ENABLE_IRIDESCENCE vec3 iridescenceSpecularColor; #endif + + #ifdef MATERIAL_ENABLE_SHEEN + float sheenRoughness; + float sheenScaling; + float approxIBLSheenDG; + #endif + }; @@ -305,6 +320,58 @@ vec3 BRDF_Diffuse_Lambert(vec3 diffuseColor) { } #endif +#ifdef MATERIAL_ENABLE_SHEEN + // http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf + float D_Charlie(float roughness, float dotNH) { + float invAlpha = 1.0 / roughness; + float cos2h = dotNH * dotNH; + float sin2h = max(1.0 - cos2h, 0.0078125); // 2^(-14/2), so sin2h^2 > 0 in fp16 + return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI); + } + + // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886". + float V_Neubelt(float NoV, float NoL) { + return saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV))); + } + + vec3 sheenBRDF(vec3 incidentDirection, SurfaceData surfaceData, vec3 sheenColor, float sheenRoughness) { + vec3 halfDir = normalize(incidentDirection + surfaceData.viewDir); + float dotNL = saturate(dot(surfaceData.normal, incidentDirection)); + float dotNH = saturate(dot(surfaceData.normal, halfDir)); + float D = D_Charlie(sheenRoughness, dotNH); + float V = V_Neubelt(surfaceData.dotNV, dotNL); + vec3 F = sheenColor; + return D * V * F; + } + + float prefilteredSheenDFG(float dotNV, float sheenRoughness) { + #ifdef HAS_TEX_LOD + return texture2DLodEXT(scene_prefilteredLUT, vec2(dotNV, sheenRoughness), 0.0).b; + #else + return texture2D(scene_prefilteredLUT, vec2(dotNV, sheenRoughness),0.0).b; + #endif + } +#endif + +// ------------------------Indirect Specular------------------------ +// ref: https://www.unrealengine.com/blog/physically-based-shading-on-mobile - environmentBRDF for GGX on mobile +vec3 envBRDFApprox(vec3 specularColor, float roughness, float dotNV ) { + + const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); + + const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); + + vec4 r = roughness * c0 + c1; + + float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; + + vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; + + return specularColor * AB.x + AB.y; + +} + + void initBRDFData(SurfaceData surfaceData, out BRDFData brdfData){ vec3 albedoColor = surfaceData.albedoColor; vec3 specularColor = surfaceData.specularColor; @@ -332,6 +399,12 @@ void initBRDFData(SurfaceData surfaceData, out BRDFData brdfData){ float topIOR = 1.0; brdfData.iridescenceSpecularColor = evalIridescenceSpecular(topIOR, surfaceData.dotNV, surfaceData.iridesceceIOR, brdfData.specularColor, surfaceData.iridescenceThickness); #endif + + #ifdef MATERIAL_ENABLE_SHEEN + brdfData.sheenRoughness = max(MIN_PERCEPTUAL_ROUGHNESS, min(surfaceData.sheenRoughness + getAARoughnessFactor(surfaceData.normal), 1.0)); + brdfData.approxIBLSheenDG = prefilteredSheenDFG(surfaceData.dotNV, brdfData.sheenRoughness); + brdfData.sheenScaling = 1.0 - brdfData.approxIBLSheenDG * max(max(surfaceData.sheenColor.r, surfaceData.sheenColor.g), surfaceData.sheenColor.b); + #endif } #endif \ No newline at end of file diff --git a/packages/shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl b/packages/shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl index 22c1b34e..0ec9afe4 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl @@ -50,6 +50,18 @@ float material_OcclusionTextureCoord; #endif #endif +#ifdef MATERIAL_ENABLE_SHEEN + float material_SheenRoughness; + vec3 material_SheenColor; + #ifdef MATERIAL_HAS_SHEEN_TEXTURE + sampler2D material_SheenTexture; + #endif + + #ifdef MATERIAL_HAS_SHEEN_ROUGHNESS_TEXTURE + sampler2D material_SheenRoughnessTexture; + #endif +#endif + // Texture #ifdef MATERIAL_HAS_BASETEXTURE sampler2D material_BaseTexture; @@ -263,6 +275,23 @@ SurfaceData getSurfaceData(Varyings v, vec2 aoUV, bool isFrontFacing){ #endif #endif + #ifdef MATERIAL_ENABLE_SHEEN + vec3 sheenColor = material_SheenColor; + #ifdef MATERIAL_HAS_SHEEN_TEXTURE + vec4 sheenTextureColor = texture2D(material_SheenTexture, uv); + #ifndef ENGINE_IS_COLORSPACE_GAMMA + sheenTextureColor = gammaToLinear(sheenTextureColor); + #endif + sheenColor *= sheenTextureColor.rgb; + #endif + surfaceData.sheenColor = sheenColor; + + surfaceData.sheenRoughness = material_SheenRoughness; + #ifdef MATERIAL_HAS_SHEEN_ROUGHNESS_TEXTURE + surfaceData.sheenRoughness *= texture2D(material_SheenRoughnessTexture, uv).a; + #endif + #endif + // AO float diffuseAO = 1.0; float specularAO = 1.0; diff --git a/packages/shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl b/packages/shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl index 1da64831..f8ad6e77 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl @@ -14,6 +14,9 @@ #ifndef FUNCTION_CLEAR_COAT_LOBE #define FUNCTION_CLEAR_COAT_LOBE clearCoatLobe #endif +#ifndef FUNCTION_SHEEN_LOBE + #define FUNCTION_SHEEN_LOBE sheenLobe +#endif #include "BRDF.glsl" #include "Light.glsl" @@ -34,6 +37,8 @@ void surfaceShading(Varyings varyings, SurfaceData surfaceData, BRDFData brdfDat FUNCTION_DIFFUSE_LOBE(varyings, surfaceData, brdfData, attenuationIrradiance, diffuseColor); // Specular Lobe FUNCTION_SPECULAR_LOBE(varyings, surfaceData, brdfData, incidentDirection, attenuationIrradiance, specularColor); + // Sheen Lobe + FUNCTION_SHEEN_LOBE(varyings, surfaceData, brdfData, incidentDirection, attenuationIrradiance, diffuseColor, specularColor); color += diffuseColor + specularColor; diff --git a/packages/shaderlab/src/shaders/shadingPBR/LightProbe.glsl b/packages/shaderlab/src/shaders/shadingPBR/LightIndirectFunctions.glsl similarity index 72% rename from packages/shaderlab/src/shaders/shadingPBR/LightProbe.glsl rename to packages/shaderlab/src/shaders/shadingPBR/LightIndirectFunctions.glsl index f45ece00..a6588349 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/LightProbe.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/LightIndirectFunctions.glsl @@ -1,25 +1,6 @@ -#ifndef LIGHT_PROBE -#define LIGHT_PROBE - -// ------------------------Specular------------------------ - -// ref: https://www.unrealengine.com/blog/physically-based-shading-on-mobile - environmentBRDF for GGX on mobile -vec3 envBRDFApprox(vec3 specularColor, float roughness, float dotNV ) { - - const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 ); - - const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 ); - - vec4 r = roughness * c0 + c1; - - float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y; - - vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; - - return specularColor * AB.x + AB.y; - -} - +#ifndef LIGHT_INDIRECT_FUNCTIONS_INCLUDED +#define LIGHT_INDIRECT_FUNCTIONS_INCLUDED +#include "Light.glsl" vec3 getReflectedVector(SurfaceData surfaceData, vec3 n) { #ifdef MATERIAL_ENABLE_ANISOTROPY @@ -67,5 +48,4 @@ vec3 getLightProbeRadiance(SurfaceData surfaceData, vec3 normal, float roughness #endif } - #endif \ No newline at end of file diff --git a/packages/shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl b/packages/shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl index 7a48795a..e0f5780b 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl @@ -11,10 +11,12 @@ #ifndef FUNCTION_CLEAR_COAT_IBL #define FUNCTION_CLEAR_COAT_IBL evaluateClearCoatIBL #endif - +#ifndef FUNCTION_SHEEN_IBL + #define FUNCTION_SHEEN_IBL evaluateSheenIBL +#endif #include "BRDF.glsl" #include "Light.glsl" -#include "LightProbe.glsl" +#include "LightIndirectFunctions.glsl" // ------------------------Diffuse------------------------ @@ -77,6 +79,15 @@ void evaluateSpecularIBL(Varyings varyings, SurfaceData surfaceData, BRDFData br outSpecularColor += surfaceData.specularAO * radianceAttenuation * radiance * envBRDFApprox(speculaColor, brdfData.roughness, surfaceData.dotNV); } +void evaluateSheenIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, float radianceAttenuation, inout vec3 diffuseColor, inout vec3 specularColor){ + #ifdef MATERIAL_ENABLE_SHEEN + diffuseColor *= brdfData.sheenScaling; + specularColor *= brdfData.sheenScaling; + + vec3 reflectance = surfaceData.specularAO * radianceAttenuation * brdfData.approxIBLSheenDG * surfaceData.sheenColor; + specularColor += reflectance; + #endif +} void evaluateIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, inout vec3 color){ vec3 diffuseColor = vec3(0); @@ -90,8 +101,12 @@ void evaluateIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, // IBL specular FUNCTION_SPECULAR_IBL(varyings, surfaceData, brdfData, radianceAttenuation, specularColor); - + + // IBL sheen + FUNCTION_SHEEN_IBL(varyings, surfaceData, brdfData, radianceAttenuation, diffuseColor, specularColor); + color += diffuseColor + specularColor; + } diff --git a/packages/shaderlab/src/shaders/shadingPBR/ReflectionLobe.glsl b/packages/shaderlab/src/shaders/shadingPBR/ReflectionLobe.glsl index ee2ab891..e13d6062 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/ReflectionLobe.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/ReflectionLobe.glsl @@ -6,7 +6,16 @@ void diffuseLobe(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, } void specularLobe(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, vec3 incidentDirection, vec3 attenuationIrradiance, inout vec3 specularColor){ - specularColor += attenuationIrradiance * BRDF_Specular_GGX( incidentDirection, surfaceData, brdfData, surfaceData.normal, brdfData.specularColor, brdfData.roughness); + specularColor += attenuationIrradiance * BRDF_Specular_GGX( incidentDirection, surfaceData, brdfData, surfaceData.normal, brdfData.specularColor, brdfData.roughness); +} + +void sheenLobe(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, vec3 incidentDirection, vec3 attenuationIrradiance, inout vec3 diffuseColor, inout vec3 specularColor){ + #ifdef MATERIAL_ENABLE_SHEEN + diffuseColor *= brdfData.sheenScaling; + specularColor *= brdfData.sheenScaling; + + specularColor += attenuationIrradiance * sheenBRDF(incidentDirection, surfaceData, surfaceData.sheenColor, brdfData.sheenRoughness); + #endif } float clearCoatLobe(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, vec3 incidentDirection, vec3 color, inout vec3 specularColor){ @@ -16,7 +25,7 @@ float clearCoatLobe(Varyings varyings, SurfaceData surfaceData, BRDFData brdfDat float clearCoatDotNL = saturate( dot( surfaceData.clearCoatNormal, incidentDirection ) ); vec3 clearCoatIrradiance = clearCoatDotNL * color; - specularColor += surfaceData.clearCoat * clearCoatIrradiance * BRDF_Specular_GGX( incidentDirection, surfaceData, surfaceData.clearCoatNormal, brdfData.clearCoatSpecularColor, brdfData.clearCoatRoughness ); + specularColor += surfaceData.clearCoat * clearCoatIrradiance * BRDF_Specular_GGX( incidentDirection, surfaceData, brdfData, surfaceData.clearCoatNormal, brdfData.clearCoatSpecularColor, brdfData.clearCoatRoughness ); attenuation -= surfaceData.clearCoat * F_Schlick(0.04, surfaceData.clearCoatDotNV); #endif diff --git a/packages/shaderlab/src/shaders/shadingPBR/index.ts b/packages/shaderlab/src/shaders/shadingPBR/index.ts index 2c6bf9c6..a1995f0b 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/index.ts +++ b/packages/shaderlab/src/shaders/shadingPBR/index.ts @@ -1,6 +1,6 @@ import AttributesPBR from "./AttributesPBR.glsl"; import BRDF from "./BRDF.glsl"; -import LightProbe from "./LightProbe.glsl"; +import LightIndirectFunctions from "./LightIndirectFunctions.glsl"; import ForwardPassPBR from "./ForwardPassPBR.glsl"; import FragmentPBR from "./FragmentPBR.glsl"; import LightDirectPBR from "./LightDirectPBR.glsl"; @@ -18,6 +18,6 @@ export default [ { source: LightIndirectPBR, includeKey: "LightIndirectPBR.glsl" }, { source: VertexPBR, includeKey: "VertexPBR.glsl" }, { source: BRDF, includeKey: "BRDF.glsl" }, - { source: LightProbe, includeKey: "LightProbe.glsl" }, + { source: LightIndirectFunctions, includeKey: "LightIndirectFunctions.glsl" }, { source: ReflectionLobe, includeKey: "ReflectionLobe.glsl" } ];