From d1620dc77c892f45b5f22da4cf2d83a8504d66e4 Mon Sep 17 00:00:00 2001 From: hhhhkrx Date: Mon, 16 Dec 2024 15:51:55 +0800 Subject: [PATCH] feat: refraction --- packages/shaderlab/src/shaders/Common.glsl | 52 +++++++++++++++++ packages/shaderlab/src/shaders/PBR.gs | 9 ++- .../src/shaders/shadingPBR/BRDF.glsl | 14 +++++ .../src/shaders/shadingPBR/BTDF.glsl | 53 +++++++++++++++++ .../src/shaders/shadingPBR/FragmentPBR.glsl | 58 ++++++++++++++++++- .../shaders/shadingPBR/LightDirectPBR.glsl | 5 ++ .../shaders/shadingPBR/LightIndirectPBR.glsl | 13 ++++- .../src/shaders/shadingPBR/Refraction.glsl | 56 ++++++++++++++++++ .../shaderlab/src/shaders/shadingPBR/index.ts | 6 +- 9 files changed, 262 insertions(+), 4 deletions(-) create mode 100644 packages/shaderlab/src/shaders/shadingPBR/BTDF.glsl create mode 100644 packages/shaderlab/src/shaders/shadingPBR/Refraction.glsl diff --git a/packages/shaderlab/src/shaders/Common.glsl b/packages/shaderlab/src/shaders/Common.glsl index 46ac04ad..f6560807 100644 --- a/packages/shaderlab/src/shaders/Common.glsl +++ b/packages/shaderlab/src/shaders/Common.glsl @@ -97,5 +97,57 @@ float remapDepthBufferLinear01(float z){ #define INVERSE_MAT(mat) inverseMat(mat) #endif +vec4 sampleTexture(sampler2D tex, vec2 uv){ + vec4 color = texture2D(tex, uv); + #ifndef ENGINE_IS_COLORSPACE_GAMMA + color = gammaToLinear(color); + #endif + + return color; +} + +vec2 bSpline3MiddleLeft(vec2 x){ + return 0.16666667 + x * (0.5 + x * (0.5 - x * 0.5)); +} + +vec2 bSpline3MiddleRight(vec2 x){ + return 0.66666667 + x * (-1.0 + 0.5 * x) * x; +} + +vec2 bSpline3Rightmost(vec2 x){ + return 0.16666667 + x * (-0.5 + x * (0.5 - x * 0.16666667)); +} + +// Compute weights & offsets for 4x bilinear taps for the bicubic B-Spline filter. +// The fractional coordinate should be in the [0, 1] range (centered on 0.5). +// Inspired by: http://vec3.ca/bicubic-filtering-in-fewer-taps/ +void bicubicFilter(vec2 fracCoord, out vec2 weights[2], out vec2 offsets[2]){ + vec2 r = bSpline3Rightmost(fracCoord); + vec2 mr = bSpline3MiddleRight(fracCoord); + vec2 ml = bSpline3MiddleLeft(fracCoord); + vec2 l = 1.0 - mr - ml - r; + + weights[0] = r + mr; + weights[1] = ml + l; + offsets[0] = -1.0 + mr / weights[0]; + offsets[1] = 1.0 + l / weights[1]; +} + + +// texSize: (1/width, 1/height, width, height) +vec4 sampleTexture2DBicubic(sampler2D tex, vec2 coord, vec4 texSize){ + vec2 xy = coord * texSize.zw + 0.5; + vec2 ic = floor(xy); + vec2 fc = fract(xy); + + vec2 weights[2]; + vec2 offsets[2]; + bicubicFilter(fc, weights, offsets); + + return weights[0].y * (weights[0].x * sampleTexture(tex, (ic + vec2(offsets[0].x, offsets[0].y) - 0.5) * texSize.xy) + + weights[1].x * sampleTexture(tex, (ic + vec2(offsets[1].x, offsets[0].y) - 0.5) * texSize.xy)) + + weights[1].y * (weights[0].x * sampleTexture(tex, (ic + vec2(offsets[0].x, offsets[1].y) - 0.5) * texSize.xy) + + weights[1].x * sampleTexture(tex, (ic + vec2(offsets[1].x, offsets[1].y) - 0.5) * texSize.xy)); +} #endif \ No newline at end of file diff --git a/packages/shaderlab/src/shaders/PBR.gs b/packages/shaderlab/src/shaders/PBR.gs index 8bea8bb2..06e5d12f 100644 --- a/packages/shaderlab/src/shaders/PBR.gs +++ b/packages/shaderlab/src/shaders/PBR.gs @@ -54,7 +54,14 @@ Shader "PBR.gs" { material_SheenTexture("ColorTexture", Texture2D); material_SheenRoughnessTexture("RoughnessTexture", Texture2D); } - + Header("Refraction"){ + material_attenuationColor("AttenuationColor", Color ) = (0, 0, 0, 1); + material_attenuationDistance("AttenuationDistance", Range(0, 1, 0.01)) = 0; + material_transmission("Transmission", Range(0, 1, 0.01)) = 0; + material_Thickness("Thickness", Float) = 0; + material_TransmissionTexture("TransmissionTexture", Texture2D); + material_ThicknessTexture("ThicknessTexture", Texture2D); + } Header("Common") { material_AlphaCutoff( "AlphaCutoff", Range(0, 1, 0.01) ) = 0; material_TilingOffset("TilingOffset", Vector4) = (1, 1, 0, 0); diff --git a/packages/shaderlab/src/shaders/shadingPBR/BRDF.glsl b/packages/shaderlab/src/shaders/shadingPBR/BRDF.glsl index c8eb6461..6886b740 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/BRDF.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/BRDF.glsl @@ -24,6 +24,7 @@ struct SurfaceData{ float specularAO; float f0; float opacity; + float IOR; // geometry vec3 position; @@ -64,6 +65,19 @@ struct SurfaceData{ vec3 sheenColor; #endif + #ifdef MATERIAL_ENABLE_SS_REFRACTION + vec3 attenuationColor; + float attenuationDistance; + + // #ifdef MATERIAL_HAS_TRANSMISSION + float transmission; + // #endif + + // #ifdef MATERIAL_ENABLE_THICKNESS + float thickness; + // #endif + #endif + }; diff --git a/packages/shaderlab/src/shaders/shadingPBR/BTDF.glsl b/packages/shaderlab/src/shaders/shadingPBR/BTDF.glsl new file mode 100644 index 00000000..5e646454 --- /dev/null +++ b/packages/shaderlab/src/shaders/shadingPBR/BTDF.glsl @@ -0,0 +1,53 @@ +#ifndef BTDF_INCLUDED +#define BTDF_INCLUDED + +#include "Refraction.glsl" + +vec4 renderer_texelSize; // x: 1/width, y: 1/height, z: width, w: height +sampler2D camera_OpaqueTexture; + +#ifdef MATERIAL_ENABLE_SS_REFRACTION + vec3 evaluateRefraction(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, vec3 speculaColor) { + RefractionModel ray; + #if REFRACTION_SPHERE + RefractionModelSphere(surfaceData.viewDir, varyings.positionWS, varyings.normalWS, surfaceData.IOR, surfaceData.thickness, ray); + #elif REFRACTION_PLANE + RefractionModelBox(surfaceData.viewDir, varyings.positionWS, varyings.normalWS, surfaceData.IOR, surfaceData.thickness, ray); + #elif REFRACTION_THIN + RefractionModelBox(surfaceData.viewDir, varyings.positionWS, varyings.normalWS, surfaceData.IOR, surfaceData.thickness, ray); + #endif + + vec3 refractedRayExit = ray.position; //ray.position + ray.direction; + + // Project refracted vector on the framebuffer, while mapping to normalized device coordinates. + vec4 ndcPos = camera_ProjMat * camera_ViewMat * vec4( refractedRayExit, 1.0 ); + vec2 refractionCoords = ndcPos.xy / ndcPos.w; + refractionCoords *= 0.5; + refractionCoords += 0.5; + + // compute transmission + vec3 absorptionCoefficient = max(vec3(0), -log(surfaceData.attenuationColor)/surfaceData.attenuationDistance); + #ifdef MATERIAL_HAS_ABSORPTION + vec3 transmittance = min(vec3(1.0), exp(-absorptionCoefficient * ray.dist)); + #else + vec3 transmittance = 1.0 - absorptionCoefficient; + #endif + + // vec4 transmittedLight = texture2D(camera_OpaqueTexture, varyings.uv); + vec4 transmittedLight = sampleTexture2DBicubic(camera_OpaqueTexture, refractionCoords, renderer_texelSize); + // vec3 transmittanceColor = brdfData.diffuseColor * transmittance; + vec3 E = envBRDFApprox(speculaColor, brdfData.roughness, surfaceData.dotNV); + + vec3 ft = transmittedLight.rgb; + ft *= brdfData.diffuseColor; + ft *= 1.0 - E; + + #ifdef MATERIAL_HAS_ABSORPTION + ft *= transmittance; + #endif + + return ft; + } +#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 0ec9afe4..3d95f17c 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl @@ -3,6 +3,12 @@ #include "Normal.glsl" +// #if MATERIAL_ENABLE_SS_REFRACTION + // #define REFRACTION_SPHERE + // #define REFRACTION_PLANE + // #define REFRACTION_THIN +// #endif + float material_AlphaCutoff; vec4 material_BaseColor; float material_Metal; @@ -62,6 +68,28 @@ float material_OcclusionTextureCoord; #endif #endif +#ifdef MATERIAL_ENABLE_SS_REFRACTION + #define REFRACTION_SPHERE + #define REFRACTION_PLANE + #define REFRACTION_THIN + + vec3 material_attenuationColor; + float material_attenuationDistance; + + // #ifdef MATERIAL_ENABLE_SS_REFRACTION + float material_transmission; + // #ifdef MATERIAL_HAS_TRANSMISSION_TEXTURE + // sampler2D material_TransmissionTexture; + // #endif + // #endif + + // #ifdef MATERIAL_ENABLE_THICKNESS + float material_thickness; + // #ifdef MATERIAL_HAS_THICKNESS_TEXTURE + // sampler2D material_ThicknessTexture; + // #endif + // #endif +#endif // Texture #ifdef MATERIAL_HAS_BASETEXTURE sampler2D material_BaseTexture; @@ -166,7 +194,7 @@ SurfaceData getSurfaceData(Varyings v, vec2 aoUV, bool isFrontFacing){ surfaceData.emissiveColor = emissiveRadiance; surfaceData.metallic = metallic; surfaceData.roughness = roughness; - surfaceData.f0 = f0; + surfaceData.IOR = material_IOR; #ifdef MATERIAL_IS_TRANSPARENT surfaceData.opacity = baseColor.a; @@ -292,6 +320,34 @@ SurfaceData getSurfaceData(Varyings v, vec2 aoUV, bool isFrontFacing){ #endif #endif + + #ifdef MATERIAL_ENABLE_SS_REFRACTION + surfaceData.attenuationColor = material_attenuationColor; + + // #if REFRACTION_THIN + // #define REFRACTION_THIN_DISTANCE 0.005 + // surfaceData.attenuationDistance = REFRACTION_THIN_DISTANCE; + // #else + surfaceData.attenuationDistance = material_attenuationDistance; + // #endif + + // #ifdef MATERIAL_HAS_TRANSMISSION + surfaceData.transmission = material_transmission; + // #ifdef MATERIAL_HAS_TRANSMISSION_TEXTURE + // surfaceData.transmission *= texture2D( material_TransmissionTexture, uv).r; + // #endif + // #else + // surfaceData.transmission = 1.0; + // #endif + + // #ifdef MATERIAL_ENABLE_THICKNESS + surfaceData.thickness = max(0.0, material_thickness); + // #ifdef MATERIAL_HAS_THICKNESS_TEXTURE + // surfaceData.thickness *= texture2D( material_ThicknessTexture, uv).g; + // #endif + // #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 f8ad6e77..4b497806 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl @@ -37,6 +37,11 @@ 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); + + // #ifdef MATERIAL_ENABLE_SS_REFRACTION + // diffuseColor *= (1.0 - surfaceData.transmission); + // #endif + // Sheen Lobe FUNCTION_SHEEN_LOBE(varyings, surfaceData, brdfData, incidentDirection, attenuationIrradiance, diffuseColor, specularColor); diff --git a/packages/shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl b/packages/shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl index e0f5780b..22f24599 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl @@ -15,6 +15,7 @@ #define FUNCTION_SHEEN_IBL evaluateSheenIBL #endif #include "BRDF.glsl" +#include "BTDF.glsl" #include "Light.glsl" #include "LightIndirectFunctions.glsl" @@ -92,7 +93,7 @@ void evaluateSheenIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfD void evaluateIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, inout vec3 color){ vec3 diffuseColor = vec3(0); vec3 specularColor = vec3(0); - + vec3 transmissioncolor = vec3(0); // IBL diffuse FUNCTION_DIFFUSE_IBL(varyings, surfaceData, brdfData, diffuseColor); @@ -105,8 +106,18 @@ void evaluateIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, // IBL sheen FUNCTION_SHEEN_IBL(varyings, surfaceData, brdfData, radianceAttenuation, diffuseColor, specularColor); + +// #ifdef MATERIAL_ENABLE_SS_REFRACTION + // vec3 Ft = evaluateRefraction(varyings, surfaceData, brdfData, specularColor); + // Ft *= surfaceData.transmission; + // diffuseColor *= (1.0 - surfaceData.transmission); + // #endif + color += diffuseColor + specularColor; + // #ifdef MATERIAL_ENABLE_SS_REFRACTION + // color.rgb += Ft; + // #endif } diff --git a/packages/shaderlab/src/shaders/shadingPBR/Refraction.glsl b/packages/shaderlab/src/shaders/shadingPBR/Refraction.glsl new file mode 100644 index 00000000..5790d34c --- /dev/null +++ b/packages/shaderlab/src/shaders/shadingPBR/Refraction.glsl @@ -0,0 +1,56 @@ +#ifndef REFRACTION_INCLUDED +#define REFRACTION_INCLUDED + +#ifdef MATERIAL_ENABLE_SS_REFRACTION + +struct RefractionModel{ + float dist; // length of the transmission during refraction through the shape + vec3 position; // out ray position + vec3 direction; // out ray direction +}; + + void RefractionModelSphere(vec3 V, vec3 positionWS, vec3 normalWS, float ior, float thickness, out RefractionModel ray){ + // Sphere shape model: + // We approximate locally the shape of the object as sphere, that is tangent to the shape. + // The sphere has a diameter of {thickness} + // The center of the sphere is at {positionWS} - {normalWS} * {thickness} * 0.5 + // So the light is refracted twice: in and out of the tangent sphere + + // First refraction (tangent sphere in) + // 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 NoR1 = dot(normalWS, R1); + // Optical depth within the sphere + float dist = -NoR1 * thickness; + // Out hit point in the tangent sphere + vec3 P1 = positionWS + R1 * dist; + // Out normal + vec3 N1 = normalize(C - P1); + // Out refracted ray + vec3 R2 = refract(R1, N1, ior); + + ray.dist = dist; + ray.position = P1; + ray.direction = R2; +} + +void RefractionModelBox(vec3 V, vec3 positionWS, vec3 normalWS, float ior, float thickness, out RefractionModel ray){ + // Plane shape model: + // We approximate locally the shape of the object as a plane with normal {normalWS} at {positionWS} + // with a thickness {thickness} + // Refracted ray + vec3 R = refract(V, normalWS, 1.0 / ior); + + // Optical depth within the thin plane + float dist = thickness / max(dot(R, -normalWS), 0.001); + + ray.dist = dist; + ray.position = positionWS + R * dist; + ray.direction = V; +} +#endif +#endif \ No newline at end of file diff --git a/packages/shaderlab/src/shaders/shadingPBR/index.ts b/packages/shaderlab/src/shaders/shadingPBR/index.ts index a1995f0b..7f878e88 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/index.ts +++ b/packages/shaderlab/src/shaders/shadingPBR/index.ts @@ -8,6 +8,8 @@ import LightIndirectPBR from "./LightIndirectPBR.glsl"; import ReflectionLobe from "./ReflectionLobe.glsl"; import VaryingsPBR from "./VaryingsPBR.glsl"; import VertexPBR from "./VertexPBR.glsl"; +import Refraction from "./Refraction.glsl"; +import BTDF from "./BTDF.glsl"; export default [ { source: ForwardPassPBR, includeKey: "ForwardPassPBR.glsl" }, @@ -19,5 +21,7 @@ export default [ { source: VertexPBR, includeKey: "VertexPBR.glsl" }, { source: BRDF, includeKey: "BRDF.glsl" }, { source: LightIndirectFunctions, includeKey: "LightIndirectFunctions.glsl" }, - { source: ReflectionLobe, includeKey: "ReflectionLobe.glsl" } + { source: ReflectionLobe, includeKey: "ReflectionLobe.glsl" }, + { source: Refraction, includeKey: "Refraction.glsl" }, + { source: BTDF, includeKey: "BTDF.glsl" } ];