diff --git a/packages/core/src/material/BaseMaterial.ts b/packages/core/src/material/BaseMaterial.ts index cf1aa7c6b7..874c4d70f3 100644 --- a/packages/core/src/material/BaseMaterial.ts +++ b/packages/core/src/material/BaseMaterial.ts @@ -24,11 +24,11 @@ export class BaseMaterial extends Material { protected static _emissiveColorProp: ShaderProperty = ShaderProperty.getByName("material_EmissiveColor"); protected static _emissiveTextureProp: ShaderProperty = ShaderProperty.getByName("material_EmissiveTexture"); - private static _alphaCutoffProp: ShaderProperty = ShaderProperty.getByName("material_AlphaCutoff"); + protected static _alphaCutoffProp: ShaderProperty = ShaderProperty.getByName("material_AlphaCutoff"); private static _alphaCutoffMacro: ShaderMacro = ShaderMacro.getByName("MATERIAL_IS_ALPHA_CUTOFF"); private _renderFace: RenderFace = RenderFace.Front; - private _isTransparent: boolean = false; + protected _isTransparent: boolean = false; private _blendMode: BlendMode = BlendMode.Normal; /** @@ -73,24 +73,7 @@ export class BaseMaterial extends Material { } set isTransparent(value: boolean) { - if (value !== this._isTransparent) { - this.setIsTransparent(0, value); - - const { shaderData } = this; - if (value) { - // Use alpha test queue to simulate transparent shadow - shaderData.setFloat(BaseMaterial._shadowCasterRenderQueueProp, RenderQueueType.AlphaTest); - } else { - const alphaCutoff = shaderData.getFloat(BaseMaterial._alphaCutoffProp); - if (alphaCutoff) { - shaderData.setFloat(BaseMaterial._shadowCasterRenderQueueProp, RenderQueueType.AlphaTest); - } else { - shaderData.setFloat(BaseMaterial._shadowCasterRenderQueueProp, RenderQueueType.Opaque); - } - } - - this._isTransparent = value; - } + this._seIsTransparent(value); } /** @@ -119,35 +102,7 @@ export class BaseMaterial extends Material { } set alphaCutoff(value: number) { - const { shaderData } = this; - if (shaderData.getFloat(BaseMaterial._alphaCutoffProp) !== value) { - if (value) { - shaderData.enableMacro(BaseMaterial._alphaCutoffMacro); - shaderData.setFloat(BaseMaterial._shadowCasterRenderQueueProp, RenderQueueType.AlphaTest); - } else { - shaderData.disableMacro(BaseMaterial._alphaCutoffMacro); - if (this._isTransparent) { - shaderData.setFloat(BaseMaterial._shadowCasterRenderQueueProp, RenderQueueType.AlphaTest); - } else { - shaderData.setFloat(BaseMaterial._shadowCasterRenderQueueProp, RenderQueueType.Opaque); - } - } - - const { renderStates } = this; - for (let i = 0, n = renderStates.length; i < n; i++) { - const renderState = renderStates[i]; - if (value > 0) { - renderState.renderQueueType = renderState.blendState.targetBlendState.enabled - ? RenderQueueType.Transparent - : RenderQueueType.AlphaTest; - } else { - renderState.renderQueueType = renderState.blendState.targetBlendState.enabled - ? RenderQueueType.Transparent - : RenderQueueType.Opaque; - } - } - shaderData.setFloat(BaseMaterial._alphaCutoffProp, value); - } + this._setAlphaCutoff(value); } /** @@ -279,4 +234,57 @@ export class BaseMaterial extends Material { target._isTransparent = this._isTransparent; target._blendMode = this._blendMode; } + + protected _seIsTransparent(value: boolean): void { + if (value !== this._isTransparent) { + this.setIsTransparent(0, value); + + const { shaderData } = this; + if (value) { + // Use alpha test queue to simulate transparent shadow + shaderData.setFloat(BaseMaterial._shadowCasterRenderQueueProp, RenderQueueType.AlphaTest); + } else { + const alphaCutoff = shaderData.getFloat(BaseMaterial._alphaCutoffProp); + if (alphaCutoff) { + shaderData.setFloat(BaseMaterial._shadowCasterRenderQueueProp, RenderQueueType.AlphaTest); + } else { + shaderData.setFloat(BaseMaterial._shadowCasterRenderQueueProp, RenderQueueType.Opaque); + } + } + + this._isTransparent = value; + } + } + + protected _setAlphaCutoff(value: number): void { + const { shaderData } = this; + if (shaderData.getFloat(BaseMaterial._alphaCutoffProp) !== value) { + if (value) { + shaderData.enableMacro(BaseMaterial._alphaCutoffMacro); + shaderData.setFloat(BaseMaterial._shadowCasterRenderQueueProp, RenderQueueType.AlphaTest); + } else { + shaderData.disableMacro(BaseMaterial._alphaCutoffMacro); + if (this._isTransparent) { + shaderData.setFloat(BaseMaterial._shadowCasterRenderQueueProp, RenderQueueType.AlphaTest); + } else { + shaderData.setFloat(BaseMaterial._shadowCasterRenderQueueProp, RenderQueueType.Opaque); + } + } + + const { renderStates } = this; + for (let i = 0, n = renderStates.length; i < n; i++) { + const renderState = renderStates[i]; + if (value > 0) { + renderState.renderQueueType = renderState.blendState.targetBlendState.enabled + ? RenderQueueType.Transparent + : RenderQueueType.AlphaTest; + } else { + renderState.renderQueueType = renderState.blendState.targetBlendState.enabled + ? RenderQueueType.Transparent + : RenderQueueType.Opaque; + } + } + shaderData.setFloat(BaseMaterial._alphaCutoffProp, value); + } + } } diff --git a/packages/core/src/material/PBRMaterial.ts b/packages/core/src/material/PBRMaterial.ts index d711947aa9..888b1b8f14 100644 --- a/packages/core/src/material/PBRMaterial.ts +++ b/packages/core/src/material/PBRMaterial.ts @@ -1,9 +1,12 @@ -import { MathUtil, Vector2, Vector3, Vector4, Color } from "@galacean/engine-math"; +import { Color, MathUtil, Vector2, Vector3, Vector4 } from "@galacean/engine-math"; import { Engine } from "../Engine"; -import { ShaderProperty } from "../shader"; +import { ShaderMacro, ShaderProperty } from "../shader"; import { Shader } from "../shader/Shader"; +import { RenderQueueType } from "../shader/enums/RenderQueueType"; import { Texture2D } from "../texture/Texture2D"; +import { BaseMaterial } from "./BaseMaterial"; import { PBRBaseMaterial } from "./PBRBaseMaterial"; +import { RefractionMode } from "./enums/Refraction"; /** * PBR (Metallic-Roughness Workflow) Material. @@ -18,19 +21,33 @@ export class PBRMaterial extends PBRBaseMaterial { private static _anisotropyInfoProp = ShaderProperty.getByName("material_AnisotropyInfo"); private static _anisotropyTextureProp = ShaderProperty.getByName("material_AnisotropyTexture"); - private _anisotropyRotation: number = 0; - private static _iridescenceInfoProp = ShaderProperty.getByName("material_IridescenceInfo"); private static _iridescenceThicknessTextureProp = ShaderProperty.getByName("material_IridescenceThicknessTexture"); private static _iridescenceTextureProp = ShaderProperty.getByName("material_IridescenceTexture"); - private _iridescenceRange = new Vector2(100, 400); - private _sheenEnabled = false; private static _sheenColorProp = ShaderProperty.getByName("material_SheenColor"); private static _sheenRoughnessProp = ShaderProperty.getByName("material_SheenRoughness"); private static _sheenTextureProp = ShaderProperty.getByName("material_SheenTexture"); private static _sheenRoughnessTextureProp = ShaderProperty.getByName("material_SheenRoughnessTexture"); + 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"); + private static _transmissionTextureProp = ShaderProperty.getByName("material_TransmissionTexture"); + private static _attenuationColorProp = ShaderProperty.getByName("material_AttenuationColor"); + private static _attenuationDistanceProp = ShaderProperty.getByName("material_AttenuationDistance"); + private static _thicknessProp = ShaderProperty.getByName("material_Thickness"); + private static _thicknessTextureProp = ShaderProperty.getByName("material_ThicknessTexture"); + + private _refractionMode: RefractionMode; + private _anisotropyRotation: number = 0; + private _iridescenceRange = new Vector2(100, 400); + private _sheenEnabled = false; + private _absorptionEnabled = true; + /** * Index Of Refraction. * @defaultValue `1.5` @@ -289,6 +306,153 @@ export class PBRMaterial extends PBRBaseMaterial { } } + /** + * Refraction switch. + * @remarks Use refractionMode to set the refraction shape. + */ + get refractionMode(): RefractionMode { + return this._refractionMode; + } + + set refractionMode(value: RefractionMode) { + if (value !== this._refractionMode) { + this._refractionMode = value; + this._setRefractionMode(value); + } + } + + /** + * @inheritdoc + */ + override get isTransparent(): boolean { + return this._isTransparent; + } + + override set isTransparent(value: boolean) { + this._seIsTransparent(value); + if (this.transmission > 0) { + // If transmission enabled, always use transparent queue to ensure get correct opaque texture + this.renderState.renderQueueType = RenderQueueType.Transparent; + } + } + + /** + * @inheritdoc + */ + override get alphaCutoff(): number { + return this.shaderData.getFloat(BaseMaterial._alphaCutoffProp); + } + + override set alphaCutoff(value: number) { + this._setAlphaCutoff(value); + if (this.transmission > 0) { + // If transmission enabled, always use transparent queue to ensure get correct opaque texture + this.renderState.renderQueueType = RenderQueueType.Transparent; + } + } + + /** + * Transmission factor. + * @defaultValue `0.0` + */ + get transmission(): number { + return this.shaderData.getFloat(PBRMaterial._transmissionProp); + } + + set transmission(value: number) { + value = MathUtil.clamp(value, 0, 1); + if (!!this.shaderData.getFloat(PBRMaterial._transmissionProp) !== !!value) { + if (value > 0) { + this.shaderData.enableMacro(PBRMaterial._transmissionMacro); + this.renderState.renderQueueType = RenderQueueType.Transparent; + } else { + this.shaderData.disableMacro(PBRMaterial._transmissionMacro); + } + } + this.shaderData.setFloat(PBRMaterial._transmissionProp, value); + } + + /** + * Transmission texture. + * @remarks Use red channel, and multiply 'transmission'. + */ + get transmissionTexture(): Texture2D { + return this.shaderData.getTexture(PBRMaterial._transmissionTextureProp); + } + + set transmissionTexture(value: Texture2D) { + this.shaderData.setTexture(PBRMaterial._transmissionTextureProp, value); + if (value) { + this.shaderData.enableMacro(PBRMaterial._transmissionTextureMacro); + } else { + this.shaderData.disableMacro(PBRMaterial._transmissionTextureMacro); + } + } + + /** + * Attenuation color. + * @defaultValue `[1,1,1]` + */ + get attenuationColor(): Color { + return this.shaderData.getColor(PBRMaterial._attenuationColorProp); + } + + set attenuationColor(value: Color) { + const attenuationColor = this.shaderData.getColor(PBRMaterial._attenuationColorProp); + if (value !== attenuationColor) { + attenuationColor.copyFrom(value); + } + } + + /** + * Attenuation distance, greater than 0.0. + * @defaultValue `infinity` + */ + get attenuationDistance(): number { + return this.shaderData.getFloat(PBRMaterial._attenuationDistanceProp); + } + + set attenuationDistance(value: number) { + value = Math.max(0, value); + this.shaderData.setFloat(PBRMaterial._attenuationDistanceProp, value); + } + + /** + * Thickness, greater than or equal to 0.0. + * @defaultValue `0.0` + */ + get thickness(): number { + return this.shaderData.getFloat(PBRMaterial._thicknessProp); + } + + set thickness(value: number) { + value = Math.max(0, value); + if (!!this.shaderData.getFloat(PBRMaterial._thicknessProp) !== !!value) { + if (value > 0) { + this.shaderData.enableMacro(PBRMaterial._thicknessMacro); + } else { + this.shaderData.disableMacro(PBRMaterial._thicknessMacro); + } + } + this.shaderData.setFloat(PBRMaterial._thicknessProp, value); + } + + /** + * Thickness texture. + * @remarks Use green channel, and multiply 'thickness', range is 0.0 to 1.0. + */ + get thicknessTexture(): Texture2D { + return this.shaderData.getTexture(PBRMaterial._thicknessTextureProp); + } + + set thicknessTexture(value: Texture2D) { + this.shaderData.setTexture(PBRMaterial._thicknessTextureProp, value); + if (value) { + this.shaderData.enableMacro(PBRMaterial._thicknessTextureMacro); + } else { + this.shaderData.disableMacro(PBRMaterial._thicknessTextureMacro); + } + } /** * Create a pbr metallic-roughness workflow material instance. * @param engine - Engine to which the material belongs @@ -304,26 +468,20 @@ 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; + 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 = () => { - const enableSheen = sheenColor.r + sheenColor.g + sheenColor.b > 0; - if (enableSheen !== this._sheenEnabled) { - this._sheenEnabled = enableSheen; - if (enableSheen) { - this.shaderData.enableMacro("MATERIAL_ENABLE_SHEEN"); - } else { - this.shaderData.disableMacro("MATERIAL_ENABLE_SHEEN"); - } - } - }; - } - - private _onIridescenceRangeChanged(): void { - const iridescenceInfo = this.shaderData.getVector4(PBRMaterial._iridescenceInfoProp); - iridescenceInfo.z = this._iridescenceRange.x; - iridescenceInfo.w = this._iridescenceRange.y; + sheenColor._onValueChanged = this._onSheenColorChanged.bind(this); + // @ts-ignore + attenuationColor._onValueChanged = this._attenuationColorChanged.bind(this); } /** @@ -334,4 +492,50 @@ export class PBRMaterial extends PBRBaseMaterial { this.cloneTo(dest); return dest; } + + private _onIridescenceRangeChanged(): void { + const iridescenceInfo = this.shaderData.getVector4(PBRMaterial._iridescenceInfoProp); + iridescenceInfo.z = this._iridescenceRange.x; + iridescenceInfo.w = this._iridescenceRange.y; + } + + private _onSheenColorChanged(): void { + const sheenColor = this.sheenColor; + const enableSheen = sheenColor.r + sheenColor.g + sheenColor.b > 0; + if (enableSheen !== this._sheenEnabled) { + this._sheenEnabled = enableSheen; + if (enableSheen) { + this.shaderData.enableMacro("MATERIAL_ENABLE_SHEEN"); + } else { + this.shaderData.disableMacro("MATERIAL_ENABLE_SHEEN"); + } + } + } + + 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; + } + } } diff --git a/packages/core/src/material/enums/Refraction.ts b/packages/core/src/material/enums/Refraction.ts new file mode 100644 index 0000000000..e043ad6f49 --- /dev/null +++ b/packages/core/src/material/enums/Refraction.ts @@ -0,0 +1,11 @@ +/** + * Refraction mode. + */ +export enum RefractionMode { + /** Refraction shape is sphere. */ + Sphere, + /** Refraction shape is plane. */ + Plane, + /** Refraction shape is thin. */ + Thin +} diff --git a/packages/core/src/material/index.ts b/packages/core/src/material/index.ts index 2422f9867d..5fbf3c859b 100644 --- a/packages/core/src/material/index.ts +++ b/packages/core/src/material/index.ts @@ -8,3 +8,4 @@ export { PBRBaseMaterial } from "./PBRBaseMaterial"; export { PBRMaterial } from "./PBRMaterial"; export { PBRSpecularMaterial } from "./PBRSpecularMaterial"; export { UnlitMaterial } from "./UnlitMaterial"; +export { RefractionMode } from "./enums/Refraction"; diff --git a/packages/loader/src/gltf/extensions/GLTFExtensionSchema.ts b/packages/loader/src/gltf/extensions/GLTFExtensionSchema.ts index b0ee1c536a..5fd66d2d06 100644 --- a/packages/loader/src/gltf/extensions/GLTFExtensionSchema.ts +++ b/packages/loader/src/gltf/extensions/GLTFExtensionSchema.ts @@ -188,6 +188,16 @@ export interface IKHRMaterialsIridescence { iridescenceThicknessTexture?: ITextureInfo; } +/** + * Interfaces from the KHR_materials_volume extension + */ +export interface IKHRMaterialsVolume { + thicknessFactor?: number; + thicknessTexture?: ITextureInfo; + attenuationDistance?: number; + attenuationColor?: number[]; +} + export type GLTFExtensionSchema = | IKHRLightsPunctual_Light | IKHRMaterialsClearcoat @@ -207,4 +217,5 @@ export type GLTFExtensionSchema = | IKHRXmp_Node | IGalaceanAnimation | IKHRMaterialsIridescence + | IKHRMaterialsVolume | Object; diff --git a/packages/loader/src/gltf/extensions/KHR_materials_transmission.ts b/packages/loader/src/gltf/extensions/KHR_materials_transmission.ts index e69de29bb2..a1eeec8500 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_transmission.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_transmission.ts @@ -0,0 +1,21 @@ +import { PBRMaterial, Texture2D } from "@galacean/engine-core"; +import { GLTFMaterialParser } from "../parser/GLTFMaterialParser"; +import { registerGLTFExtension } from "../parser/GLTFParser"; +import { GLTFParserContext, GLTFParserType } from "../parser/GLTFParserContext"; +import { GLTFExtensionMode, GLTFExtensionParser } from "./GLTFExtensionParser"; +import { IKHRMaterialsTransmission } from "./GLTFExtensionSchema"; +@registerGLTFExtension("KHR_materials_transmission", GLTFExtensionMode.AdditiveParse) +class KHR_materials_transmission extends GLTFExtensionParser { + override additiveParse(context: GLTFParserContext, material: PBRMaterial, schema: IKHRMaterialsTransmission): void { + const { transmissionFactor = 0, transmissionTexture } = schema; + material.transmission = transmissionFactor; + + if (transmissionTexture) { + GLTFMaterialParser._checkOtherTextureTransform(transmissionTexture, "Transmission texture"); + + context.get(GLTFParserType.Texture, transmissionTexture.index).then((texture) => { + material.transmissionTexture = texture; + }); + } + } +} diff --git a/packages/loader/src/gltf/extensions/KHR_materials_volume.ts b/packages/loader/src/gltf/extensions/KHR_materials_volume.ts index e69de29bb2..479358c448 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_volume.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_volume.ts @@ -0,0 +1,33 @@ +import { PBRMaterial, Texture2D } from "@galacean/engine-core"; +import { Color } from "@galacean/engine-math"; +import { GLTFMaterialParser } from "../parser/GLTFMaterialParser"; +import { registerGLTFExtension } from "../parser/GLTFParser"; +import { GLTFParserContext, GLTFParserType } from "../parser/GLTFParserContext"; +import { GLTFExtensionMode, GLTFExtensionParser } from "./GLTFExtensionParser"; +import { IKHRMaterialsVolume } from "./GLTFExtensionSchema"; + +@registerGLTFExtension("KHR_materials_volume", GLTFExtensionMode.AdditiveParse) +class KHR_materials_volume extends GLTFExtensionParser { + override additiveParse(context: GLTFParserContext, material: PBRMaterial, schema: IKHRMaterialsVolume): void { + const { thicknessFactor = 0, thicknessTexture, attenuationDistance = Infinity, attenuationColor } = schema; + material.thickness = thicknessFactor; + material.attenuationDistance = attenuationDistance; + + if (attenuationColor) { + material.attenuationColor.set( + Color.linearToGammaSpace(attenuationColor[0]), + Color.linearToGammaSpace(attenuationColor[1]), + Color.linearToGammaSpace(attenuationColor[2]), + undefined + ); + } + + if (thicknessTexture) { + GLTFMaterialParser._checkOtherTextureTransform(thicknessTexture, "Thickness texture"); + + context.get(GLTFParserType.Texture, thicknessTexture.index).then((texture) => { + material.thicknessTexture = texture; + }); + } + } +} diff --git a/packages/shader-shaderlab/src/shaders/Common.glsl b/packages/shader-shaderlab/src/shaders/Common.glsl index 46ac04ad5f..b245a1ca6c 100644 --- a/packages/shader-shaderlab/src/shaders/Common.glsl +++ b/packages/shader-shaderlab/src/shaders/Common.glsl @@ -5,6 +5,8 @@ #define RECIPROCAL_PI 0.31830988618 #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 saturate( a ) clamp( a, 0.0, 1.0 ) @@ -97,5 +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); +// } #endif \ No newline at end of file diff --git a/packages/shader-shaderlab/src/shaders/PBR.gs b/packages/shader-shaderlab/src/shaders/PBR.gs index 8bea8bb27a..ad6d98fc04 100644 --- a/packages/shader-shaderlab/src/shaders/PBR.gs +++ b/packages/shader-shaderlab/src/shaders/PBR.gs @@ -54,6 +54,11 @@ Shader "PBR.gs" { material_SheenTexture("ColorTexture", Texture2D); material_SheenRoughnessTexture("RoughnessTexture", Texture2D); } + + Header("Transmission"){ + material_Transmission("Transmission", Range(0, 1, 0.01)) = 0; + material_TransmissionTexture("TransmissionTexture", Texture2D); + } Header("Common") { material_AlphaCutoff( "AlphaCutoff", Range(0, 1, 0.01) ) = 0; diff --git a/packages/shader-shaderlab/src/shaders/shadingPBR/BRDF.glsl b/packages/shader-shaderlab/src/shaders/shadingPBR/BRDF.glsl index c8eb64615c..b885e33d34 100644 --- a/packages/shader-shaderlab/src/shaders/shadingPBR/BRDF.glsl +++ b/packages/shader-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,11 @@ struct SurfaceData{ vec3 sheenColor; #endif + #ifdef MATERIAL_ENABLE_TRANSMISSION + vec3 absorptionCoefficient; + float transmission; + float thickness; + #endif }; @@ -71,6 +77,7 @@ struct BRDFData{ vec3 diffuseColor; vec3 specularColor; float roughness; + vec3 envSpecularDFG; #ifdef MATERIAL_ENABLE_CLEAR_COAT vec3 clearCoatSpecularColor; @@ -387,9 +394,9 @@ void initBRDFData(SurfaceData surfaceData, out BRDFData brdfData){ brdfData.diffuseColor = albedoColor * ( 1.0 - specularStrength ); brdfData.specularColor = specularColor; #endif - brdfData.roughness = max(MIN_PERCEPTUAL_ROUGHNESS, min(roughness + getAARoughnessFactor(surfaceData.normal), 1.0)); - + brdfData.envSpecularDFG = envBRDFApprox(brdfData.specularColor, brdfData.roughness, surfaceData.dotNV); + #ifdef MATERIAL_ENABLE_CLEAR_COAT brdfData.clearCoatRoughness = max(MIN_PERCEPTUAL_ROUGHNESS, min(surfaceData.clearCoatRoughness + getAARoughnessFactor(surfaceData.clearCoatNormal), 1.0)); brdfData.clearCoatSpecularColor = vec3(0.04); diff --git a/packages/shader-shaderlab/src/shaders/shadingPBR/BTDF.glsl b/packages/shader-shaderlab/src/shaders/shadingPBR/BTDF.glsl new file mode 100644 index 0000000000..bbc5fabde1 --- /dev/null +++ b/packages/shader-shaderlab/src/shaders/shadingPBR/BTDF.glsl @@ -0,0 +1,35 @@ +#ifndef BTDF_INCLUDED +#define BTDF_INCLUDED + +#include "Refraction.glsl" + +#ifdef MATERIAL_ENABLE_TRANSMISSION + sampler2D camera_OpaqueTexture; + vec3 evaluateRefraction(SurfaceData surfaceData, BRDFData brdfData) { + RefractionModelResult ray; + #ifdef REFRACTION_MODE + refractionModelBox(-surfaceData.viewDir, surfaceData.position, surfaceData.normal, surfaceData.IOR, 0.005, 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; + 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 - specularDFG); + + return refractionTransmitted; + } +#endif + +#endif \ No newline at end of file diff --git a/packages/shader-shaderlab/src/shaders/shadingPBR/ForwardPassPBR.glsl b/packages/shader-shaderlab/src/shaders/shadingPBR/ForwardPassPBR.glsl index 8717574808..2550aaf08b 100644 --- a/packages/shader-shaderlab/src/shaders/shadingPBR/ForwardPassPBR.glsl +++ b/packages/shader-shaderlab/src/shaders/shadingPBR/ForwardPassPBR.glsl @@ -8,6 +8,7 @@ #include "VaryingsPBR.glsl" #include "LightDirectPBR.glsl" #include "LightIndirectPBR.glsl" +#include "BTDF.glsl" #include "VertexPBR.glsl" #include "FragmentPBR.glsl" @@ -72,7 +73,9 @@ void PBRFragment(Varyings varyings) { // Can modify surfaceData here initBRDFData(surfaceData, brdfData); - vec4 color = vec4(0, 0, 0, surfaceData.opacity); + + vec3 totalDiffuseColor = vec3(0, 0, 0); + vec3 totalSpecularColor = vec3(0, 0, 0); // Get shadow attenuation float shadowAttenuation = 1.0; @@ -86,10 +89,18 @@ void PBRFragment(Varyings varyings) { #endif // Evaluate direct lighting - evaluateDirectRadiance(varyings, surfaceData, brdfData, shadowAttenuation, color.rgb); + evaluateDirectRadiance(varyings, surfaceData, brdfData, shadowAttenuation, totalDiffuseColor, totalSpecularColor); // IBL - evaluateIBL(varyings, surfaceData, brdfData, color.rgb); + evaluateIBL(varyings, surfaceData, brdfData, totalDiffuseColor, totalSpecularColor); + + #ifdef MATERIAL_ENABLE_TRANSMISSION + vec3 refractionTransmitted = evaluateRefraction(surfaceData, brdfData); + totalDiffuseColor = mix(totalDiffuseColor, refractionTransmitted, surfaceData.transmission); + #endif + + // Final color + vec4 color = vec4((totalDiffuseColor + totalSpecularColor).rgb, surfaceData.opacity); // Emissive color.rgb += surfaceData.emissiveColor; diff --git a/packages/shader-shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl b/packages/shader-shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl index 0ec9afe48c..3fbd0b619e 100644 --- a/packages/shader-shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl +++ b/packages/shader-shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl @@ -62,6 +62,13 @@ float material_OcclusionTextureCoord; #endif #endif +#ifdef MATERIAL_ENABLE_TRANSMISSION + float material_Transmission; + #ifdef MATERIAL_HAS_TRANSMISSION_TEXTURE + sampler2D material_TransmissionTexture; + #endif +#endif + // Texture #ifdef MATERIAL_HAS_BASETEXTURE sampler2D material_BaseTexture; @@ -167,6 +174,8 @@ SurfaceData getSurfaceData(Varyings v, vec2 aoUV, bool isFrontFacing){ surfaceData.metallic = metallic; surfaceData.roughness = roughness; surfaceData.f0 = f0; + surfaceData.IOR = material_IOR; + #ifdef MATERIAL_IS_TRANSPARENT surfaceData.opacity = baseColor.a; @@ -292,6 +301,13 @@ SurfaceData getSurfaceData(Varyings v, vec2 aoUV, bool isFrontFacing){ #endif #endif + #ifdef MATERIAL_ENABLE_TRANSMISSION + surfaceData.transmission = material_Transmission; + #ifdef MATERIAL_HAS_TRANSMISSION_TEXTURE + surfaceData.transmission *= texture2D(material_TransmissionTexture, uv).r; + #endif + #endif + // AO float diffuseAO = 1.0; float specularAO = 1.0; diff --git a/packages/shader-shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl b/packages/shader-shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl index f8ad6e77a4..789ac7a8f1 100644 --- a/packages/shader-shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl +++ b/packages/shader-shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl @@ -22,7 +22,7 @@ #include "Light.glsl" #include "ReflectionLobe.glsl" -void surfaceShading(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, vec3 incidentDirection, vec3 lightColor, inout vec3 color) { +void surfaceShading(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, vec3 incidentDirection, vec3 lightColor, inout vec3 totalDiffuseColor, inout vec3 totalSpecularColor) { vec3 diffuseColor = vec3(0); vec3 specularColor = vec3(0); @@ -40,17 +40,18 @@ void surfaceShading(Varyings varyings, SurfaceData surfaceData, BRDFData brdfDat // Sheen Lobe FUNCTION_SHEEN_LOBE(varyings, surfaceData, brdfData, incidentDirection, attenuationIrradiance, diffuseColor, specularColor); - color += diffuseColor + specularColor; + totalDiffuseColor += diffuseColor; + totalSpecularColor += specularColor; } #ifdef SCENE_DIRECT_LIGHT_COUNT - void addDirectionalDirectLightRadiance(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, DirectLight directionalLight, inout vec3 color) { + void addDirectionalDirectLightRadiance(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, DirectLight directionalLight, inout vec3 totalDiffuseColor, inout vec3 totalSpecularColor) { vec3 lightColor = directionalLight.color; vec3 direction = -directionalLight.direction; - FUNCTION_SURFACE_SHADING(varyings, surfaceData, brdfData, direction, lightColor, color); + FUNCTION_SURFACE_SHADING(varyings, surfaceData, brdfData, direction, lightColor, totalDiffuseColor, totalSpecularColor); } @@ -58,7 +59,7 @@ void surfaceShading(Varyings varyings, SurfaceData surfaceData, BRDFData brdfDat #ifdef SCENE_POINT_LIGHT_COUNT - void addPointDirectLightRadiance(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, PointLight pointLight, inout vec3 color) { + void addPointDirectLightRadiance(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, PointLight pointLight, inout vec3 totalDiffuseColor, inout vec3 totalSpecularColor) { vec3 lVector = pointLight.position - surfaceData.position; vec3 direction = normalize( lVector ); float lightDistance = length( lVector ); @@ -66,14 +67,14 @@ void surfaceShading(Varyings varyings, SurfaceData surfaceData, BRDFData brdfDat vec3 lightColor = pointLight.color; lightColor *= clamp(1.0 - pow(lightDistance/pointLight.distance, 4.0), 0.0, 1.0); - FUNCTION_SURFACE_SHADING(varyings, surfaceData, brdfData, direction, lightColor, color); + FUNCTION_SURFACE_SHADING(varyings, surfaceData, brdfData, direction, lightColor, totalDiffuseColor, totalSpecularColor); } #endif #ifdef SCENE_SPOT_LIGHT_COUNT - void addSpotDirectLightRadiance(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, SpotLight spotLight, inout vec3 color) { + void addSpotDirectLightRadiance(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, SpotLight spotLight, inout vec3 totalDiffuseColor, inout vec3 totalSpecularColor) { vec3 lVector = spotLight.position - surfaceData.position; vec3 direction = normalize( lVector ); @@ -86,14 +87,14 @@ void surfaceShading(Varyings varyings, SurfaceData surfaceData, BRDFData brdfDat vec3 lightColor = spotLight.color; lightColor *= spotEffect * decayEffect; - FUNCTION_SURFACE_SHADING(varyings, surfaceData, brdfData, direction, lightColor, color); + FUNCTION_SURFACE_SHADING(varyings, surfaceData, brdfData, direction, lightColor, totalDiffuseColor, totalSpecularColor); } #endif -void evaluateDirectRadiance(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, float shadowAttenuation, inout vec3 color){ +void evaluateDirectRadiance(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, float shadowAttenuation, inout vec3 totalDiffuseColor, inout vec3 totalSpecularColor){ #ifdef SCENE_DIRECT_LIGHT_COUNT for ( int i = 0; i < SCENE_DIRECT_LIGHT_COUNT; i ++ ) { @@ -112,7 +113,7 @@ void evaluateDirectRadiance(Varyings varyings, SurfaceData surfaceData, BRDFData directionalLight.color *= shadowAttenuation; } #endif - addDirectionalDirectLightRadiance(varyings, surfaceData, brdfData, directionalLight, color ); + addDirectionalDirectLightRadiance(varyings, surfaceData, brdfData, directionalLight, totalDiffuseColor, totalSpecularColor ); } } @@ -130,7 +131,7 @@ void evaluateDirectRadiance(Varyings varyings, SurfaceData surfaceData, BRDFData pointLight.position = scene_PointLightPosition[i]; pointLight.distance = scene_PointLightDistance[i]; #endif - addPointDirectLightRadiance(varyings, surfaceData, brdfData, pointLight, color ); + addPointDirectLightRadiance(varyings, surfaceData, brdfData, pointLight, totalDiffuseColor, totalSpecularColor); } } @@ -151,7 +152,7 @@ void evaluateDirectRadiance(Varyings varyings, SurfaceData surfaceData, BRDFData spotLight.angleCos = scene_SpotLightAngleCos[i]; spotLight.penumbraCos = scene_SpotLightPenumbraCos[i]; #endif - addSpotDirectLightRadiance( varyings, surfaceData, brdfData, spotLight, color ); + addSpotDirectLightRadiance( varyings, surfaceData, brdfData, spotLight, totalDiffuseColor, totalSpecularColor); } } diff --git a/packages/shader-shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl b/packages/shader-shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl index e0f5780b58..d1e279d018 100644 --- a/packages/shader-shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl +++ b/packages/shader-shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl @@ -76,7 +76,7 @@ void evaluateSpecularIBL(Varyings varyings, SurfaceData surfaceData, BRDFData br vec3 speculaColor = brdfData.specularColor; #endif - outSpecularColor += surfaceData.specularAO * radianceAttenuation * radiance * envBRDFApprox(speculaColor, brdfData.roughness, surfaceData.dotNV); + outSpecularColor += surfaceData.specularAO * radianceAttenuation * radiance * brdfData.envSpecularDFG; } void evaluateSheenIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, float radianceAttenuation, inout vec3 diffuseColor, inout vec3 specularColor){ @@ -89,7 +89,7 @@ void evaluateSheenIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfD #endif } -void evaluateIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, inout vec3 color){ +void evaluateIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, inout vec3 totalDiffuseColor, inout vec3 totalSpecularColor){ vec3 diffuseColor = vec3(0); vec3 specularColor = vec3(0); @@ -104,10 +104,10 @@ void evaluateIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, // IBL sheen FUNCTION_SHEEN_IBL(varyings, surfaceData, brdfData, radianceAttenuation, diffuseColor, specularColor); - - color += diffuseColor + specularColor; -} + totalDiffuseColor += diffuseColor; + totalSpecularColor += specularColor; +} #endif \ No newline at end of file diff --git a/packages/shader-shaderlab/src/shaders/shadingPBR/Refraction.glsl b/packages/shader-shaderlab/src/shaders/shadingPBR/Refraction.glsl new file mode 100644 index 0000000000..91e3f1ebaf --- /dev/null +++ b/packages/shader-shaderlab/src/shaders/shadingPBR/Refraction.glsl @@ -0,0 +1,24 @@ +#ifndef REFRACTION_INCLUDED +#define REFRACTION_INCLUDED + +#ifdef MATERIAL_ENABLE_TRANSMISSION + +struct RefractionModelResult{ + float transmissionLength; // length of the transmission during refraction through the shape + vec3 positionExit; // out ray position + // vec3 directionExit; // out ray direction +}; + +void refractionModelBox(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 + float dist = thickness / max(dot(-normalWS, R), 1e-5f); + + ray.transmissionLength = dist; + ray.positionExit = vec3(positionWS + R * dist); + // ray.directionExit = V; +} +#endif + +#endif \ No newline at end of file diff --git a/packages/shader-shaderlab/src/shaders/shadingPBR/index.ts b/packages/shader-shaderlab/src/shaders/shadingPBR/index.ts index 2f34850456..3f374d2d7d 100644 --- a/packages/shader-shaderlab/src/shaders/shadingPBR/index.ts +++ b/packages/shader-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" } ];