Skip to content

Commit

Permalink
shaderlab pbr support refraction (#2470)
Browse files Browse the repository at this point in the history
* feat: pbr support refraction
  • Loading branch information
hhhhkrx authored Dec 27, 2024
1 parent ab48451 commit 01deebc
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 64 deletions.
36 changes: 2 additions & 34 deletions packages/core/src/material/PBRMaterial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export class PBRMaterial extends PBRBaseMaterial {

private static _transmissionMacro: ShaderMacro = ShaderMacro.getByName("MATERIAL_ENABLE_TRANSMISSION");
private static _thicknessMacro: ShaderMacro = ShaderMacro.getByName("MATERIAL_HAS_THICKNESS");
private static _absorptionMacro: ShaderMacro = ShaderMacro.getByName("MATERIAL_HAS_ABSORPTION");
private static _thicknessTextureMacro: ShaderMacro = ShaderMacro.getByName("MATERIAL_HAS_THICKNESS_TEXTURE");
private static _transmissionTextureMacro: ShaderMacro = ShaderMacro.getByName("MATERIAL_HAS_TRANSMISSION_TEXTURE");
private static _transmissionProp = ShaderProperty.getByName("material_Transmission");
Expand All @@ -46,7 +45,6 @@ export class PBRMaterial extends PBRBaseMaterial {
private _anisotropyRotation: number = 0;
private _iridescenceRange = new Vector2(100, 400);
private _sheenEnabled = false;
private _absorptionEnabled = true;

/**
* Index Of Refraction.
Expand Down Expand Up @@ -317,7 +315,7 @@ export class PBRMaterial extends PBRBaseMaterial {
set refractionMode(value: RefractionMode) {
if (value !== this._refractionMode) {
this._refractionMode = value;
this._setRefractionMode(value);
this.shaderData.enableMacro("REFRACTION_MODE", value.toString());
}
}

Expand Down Expand Up @@ -468,20 +466,17 @@ export class PBRMaterial extends PBRBaseMaterial {
shaderData.setVector4(PBRMaterial._iridescenceInfoProp, new Vector4(0, 1.3, 100, 400));
const sheenColor = new Color(0, 0, 0);
shaderData.setColor(PBRMaterial._sheenColorProp, sheenColor);
this.refractionMode = RefractionMode.Plane;
this.refractionMode = RefractionMode.Planar;
shaderData.setFloat(PBRMaterial._transmissionProp, 0);
shaderData.setFloat(PBRMaterial._thicknessProp, 0);
shaderData.setFloat(PBRMaterial._attenuationDistanceProp, Infinity);
const attenuationColor = new Color(1, 1, 1);
shaderData.setColor(PBRMaterial._attenuationColorProp, attenuationColor);
shaderData.enableMacro(PBRMaterial._absorptionMacro);

// @ts-ignore
this._iridescenceRange._onValueChanged = this._onIridescenceRangeChanged.bind(this);
// @ts-ignore
sheenColor._onValueChanged = this._onSheenColorChanged.bind(this);
// @ts-ignore
attenuationColor._onValueChanged = this._attenuationColorChanged.bind(this);
}

/**
Expand Down Expand Up @@ -511,31 +506,4 @@ export class PBRMaterial extends PBRBaseMaterial {
}
}
}

private _attenuationColorChanged(): void {
const attenuationColor = this.attenuationColor;
const enableAbsorption = attenuationColor.r + attenuationColor.g + attenuationColor.b > 0;
if (enableAbsorption !== this._absorptionEnabled) {
this._absorptionEnabled = enableAbsorption;
if (enableAbsorption) {
this.shaderData.enableMacro(PBRMaterial._absorptionMacro);
} else {
this.shaderData.disableMacro(PBRMaterial._absorptionMacro);
}
}
}

private _setRefractionMode(refractionMode: RefractionMode): void {
switch (refractionMode) {
case RefractionMode.Sphere:
this.shaderData.enableMacro("REFRACTION_MODE", "SPHERE");
break;
case RefractionMode.Plane:
this.shaderData.enableMacro("REFRACTION_MODE", "PLANE");
break;
case RefractionMode.Thin:
this.shaderData.enableMacro("REFRACTION_MODE", "THIN");
break;
}
}
}
10 changes: 4 additions & 6 deletions packages/core/src/material/enums/Refraction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
* Refraction mode.
*/
export enum RefractionMode {
/** Refraction shape is sphere. */
Sphere,
/** Refraction shape is plane. */
Plane,
/** Refraction shape is thin. */
Thin
/** Use the sphere refraction model when light passes through the surface. */
Sphere = 0,
/** Use the planar refraction model when light passes through the surface. */
Planar = 1
}
12 changes: 6 additions & 6 deletions packages/shader-shaderlab/src/shaders/Common.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#define EPSILON 1e-6
#define LOG2 1.442695
#define HALF_MIN 6.103515625e-5 // 2^-14, the same value for 10, 11 and 16-bit: https://www.khronos.org/opengl/wiki/Small_Float_Formats
// #define HALF_EPS 4.8828125e-4 // 2^-11, machine epsilon: 1 + EPS = 1 (half of the ULP for 1.0f)
#define HALF_EPS 4.8828125e-4 // 2^-11, machine epsilon: 1 + EPS = 1 (half of the ULP for 1.0f)

#define saturate( a ) clamp( a, 0.0, 1.0 )

Expand Down Expand Up @@ -99,10 +99,10 @@ float remapDepthBufferLinear01(float z){
#define INVERSE_MAT(mat) inverseMat(mat)
#endif

// vec3 safeNormalize(vec3 inVec)
// {
// float dp3 = max(float(HALF_MIN), dot(inVec, inVec));
// return inVec * inversesqrt(dp3);
// }
vec3 safeNormalize(vec3 inVec)
{
float dp3 = max(float(HALF_MIN), dot(inVec, inVec));
return inVec * inversesqrt(dp3);
}

#endif
4 changes: 4 additions & 0 deletions packages/shader-shaderlab/src/shaders/PBR.gs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ Shader "PBR.gs" {
Header("Transmission"){
material_Transmission("Transmission", Range(0, 1, 0.01)) = 0;
material_TransmissionTexture("TransmissionTexture", Texture2D);
material_AttenuationColor("AttenuationColor", Color ) = (1, 1, 1, 1);
material_AttenuationDistance("AttenuationDistance", Range(0, 1, 0.01)) = 0;
material_Thickness("Thickness", Range(0, 5, 0.01)) = 0;
material_ThicknessTexture("ThicknessTexture", Texture2D);
}

Header("Common") {
Expand Down
23 changes: 15 additions & 8 deletions packages/shader-shaderlab/src/shaders/shadingPBR/BTDF.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,35 @@

#ifdef MATERIAL_ENABLE_TRANSMISSION
sampler2D camera_OpaqueTexture;
vec3 evaluateRefraction(SurfaceData surfaceData, BRDFData brdfData) {
vec3 evaluateTransmission(SurfaceData surfaceData, BRDFData brdfData) {
RefractionModelResult ray;
#ifdef REFRACTION_MODE
refractionModelBox(-surfaceData.viewDir, surfaceData.position, surfaceData.normal, surfaceData.IOR, 0.005, ray);
#if REFRACTION_MODE == 0
// RefractionMode.Sphere
refractionModelSphere(-surfaceData.viewDir, surfaceData.position, surfaceData.normal, surfaceData.IOR, surfaceData.thickness, ray);
#elif REFRACTION_MODE == 1
// RefractionMode.Planar
refractionModelPlanar(-surfaceData.viewDir, surfaceData.position, surfaceData.normal, surfaceData.IOR, surfaceData.thickness, ray);
#endif
//TODO: support cubemap refraction.

vec3 refractedRayExit = ray.positionExit;

// We calculate the screen space position of the refracted point
vec4 samplingPositionNDC = camera_ProjMat * camera_ViewMat * vec4( refractedRayExit, 1.0 );
vec2 refractionCoords = (samplingPositionNDC.xy / samplingPositionNDC.w) * 0.5 + 0.5;

// Sample the opaque texture to get the transmitted light
vec4 getTransmission = texture2D(camera_OpaqueTexture, refractionCoords);
vec3 refractionTransmitted = getTransmission.rgb;
vec3 refractionTransmitted = texture2D(camera_OpaqueTexture, refractionCoords).rgb;
refractionTransmitted *= brdfData.diffuseColor;

// Use specularFGD as an approximation of the fresnel effect
// https://blog.selfshadow.com/publications/s2017-shading-course/imageworks/s2017_pbs_imageworks_slides_v2.pdf
vec3 specularDFG = brdfData.envSpecularDFG;
refractionTransmitted *= (1.0 - brdfData.envSpecularDFG);

refractionTransmitted *= (1.0 - specularDFG);
#ifdef MATERIAL_HAS_THICKNESS
// Absorption coefficient from Disney: http://blog.selfshadow.com/publications/s2015-shading-course/burley/s2015_pbs_disney_bsdf_notes.pdf
vec3 transmittance = min(vec3(1.0), exp(-surfaceData.absorptionCoefficient * ray.transmissionLength));
refractionTransmitted *= transmittance;
#endif

return refractionTransmitted;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ void PBRFragment(Varyings varyings) {
evaluateIBL(varyings, surfaceData, brdfData, totalDiffuseColor, totalSpecularColor);

#ifdef MATERIAL_ENABLE_TRANSMISSION
vec3 refractionTransmitted = evaluateRefraction(surfaceData, brdfData);
vec3 refractionTransmitted = evaluateTransmission(surfaceData, brdfData);
totalDiffuseColor = mix(totalDiffuseColor, refractionTransmitted, surfaceData.transmission);
#endif

Expand Down
33 changes: 25 additions & 8 deletions packages/shader-shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,20 @@ float material_OcclusionTextureCoord;
#endif
#endif

#ifdef MATERIAL_ENABLE_TRANSMISSION
float material_Transmission;
#ifdef MATERIAL_HAS_TRANSMISSION_TEXTURE
sampler2D material_TransmissionTexture;
#ifdef MATERIAL_ENABLE_TRANSMISSION
float material_Transmission;
#ifdef MATERIAL_HAS_TRANSMISSION_TEXTURE
sampler2D material_TransmissionTexture;
#endif

#ifdef MATERIAL_HAS_THICKNESS
vec3 material_AttenuationColor;
float material_AttenuationDistance;
float material_Thickness;
#ifdef MATERIAL_HAS_THICKNESS_TEXTURE
sampler2D material_ThicknessTexture;
#endif
#endif
#endif

// Texture
Expand Down Expand Up @@ -302,10 +311,18 @@ SurfaceData getSurfaceData(Varyings v, vec2 aoUV, bool isFrontFacing){
#endif

#ifdef MATERIAL_ENABLE_TRANSMISSION
surfaceData.transmission = material_Transmission;
#ifdef MATERIAL_HAS_TRANSMISSION_TEXTURE
surfaceData.transmission *= texture2D(material_TransmissionTexture, uv).r;
#endif
surfaceData.transmission = material_Transmission;
#ifdef MATERIAL_HAS_TRANSMISSION_TEXTURE
surfaceData.transmission *= texture2D(material_TransmissionTexture, uv).r;
#endif

#ifdef MATERIAL_HAS_THICKNESS
surfaceData.absorptionCoefficient = -log(material_AttenuationColor + HALF_EPS) / max(HALF_EPS, material_AttenuationDistance);
surfaceData.thickness = max(material_Thickness, 0.0001);
#ifdef MATERIAL_HAS_THICKNESS_TEXTURE
surfaceData.thickness *= texture2D( material_ThicknessTexture, uv).g;
#endif
#endif
#endif

// AO
Expand Down
23 changes: 22 additions & 1 deletion packages/shader-shaderlab/src/shaders/shadingPBR/Refraction.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,28 @@ struct RefractionModelResult{
// vec3 directionExit; // out ray direction
};

void refractionModelBox(vec3 V, vec3 positionWS, vec3 normalWS, float ior, float thickness, out RefractionModelResult ray){
//https://docs.unity3d.com/Packages/[email protected]/manual/refraction-models.html
void refractionModelSphere(vec3 V, vec3 positionWS, vec3 normalWS, float ior, float thickness, out RefractionModelResult ray){
// Refracted ray
vec3 R1 = refract(V, normalWS, 1.0 / ior);
// Center of the tangent sphere
// vec3 C = positionWS - normalWS * thickness * 0.5;

// Second refraction (tangent sphere out)
float dist = dot(-normalWS, R1) * thickness;
// Out hit point in the tangent sphere
vec3 P1 = positionWS + R1 * dist;
// Out normal
// vec3 N1 = safeNormalize(C - P1);
// Out refracted ray
// vec3 R2 = refract(R1, N1, ior);

ray.transmissionLength = dist;
ray.positionExit = P1;
// ray.directionExit = R2;
}

void refractionModelPlanar(vec3 V, vec3 positionWS, vec3 normalWS, float ior, float thickness, out RefractionModelResult ray){
// Refracted ray
vec3 R = refract(V, normalWS, 1.0 / ior);
// Optical depth within the thin plane
Expand Down

0 comments on commit 01deebc

Please sign in to comment.