Browse Source

Add anisotropy to pbr

sebavan 6 years ago
parent
commit
ae09c950c6

+ 1 - 0
dist/preview release/what's new.md

@@ -26,6 +26,7 @@
   - InvalidateRect added to AdvancedDynamicTexture to improve perf for heavily populated GUIs, works with shadows ([TrevorDev](https://github.com/TrevorDev))
 - Migrate the code to modules and deploy es6 npm packages ([Sebavan](https://github.com/Sebavan))
 - Add clear coat support to PBR ([Sebavan](https://github.com/Sebavan))
+- Add anisotropy support to PBR ([Sebavan](https://github.com/Sebavan))
 
 ## Updates
 

+ 148 - 0
src/Materials/PBR/pbrAnisotropicConfiguration.ts

@@ -0,0 +1,148 @@
+import { SerializationHelper, serialize, expandToProperty } from "../../Misc/decorators";
+import { EffectFallbacks } from "../../Materials/effect";
+import { UniformBuffer } from "../../Materials/uniformBuffer";
+import { AbstractMesh } from "../../Meshes/abstractMesh";
+import { VertexBuffer } from "../../Meshes/buffer";
+
+/**
+ * @hidden
+ */
+export interface IMaterialAnisotropicDefines {
+    ANISOTROPIC: boolean;
+    MAINUV1: boolean;
+
+    _areMiscDirty: boolean;
+    _needUVs: boolean;
+}
+
+/**
+ * Define the code related to the anisotropic parameters of the pbr material.
+ */
+export class PBRAnisotropicConfiguration {
+
+    @serialize()
+    private _isEnabled = false;
+    /**
+     * Defines if the anisotropy is enabled in the material.
+     */
+    @expandToProperty("_markAllSubMeshesAsMiscDirty")
+    public isEnabled = false;
+
+    /**
+     * Defines the anisotropy strength (between 0 and 1) it defaults to 1.
+     */
+    @serialize()
+    public intensity: number = 1;
+
+    /**
+     * Defines if the effect is along the tangents or bitangents.
+     * By default, the effect is "strectching" the highlights along the tangents.
+     */
+    @serialize()
+    public followTangents = true;
+
+    /** @hidden */
+    private _internalMarkAllSubMeshesAsMiscDirty: () => void;
+
+    /** @hidden */
+    public _markAllSubMeshesAsMiscDirty(): void {
+        this._internalMarkAllSubMeshesAsMiscDirty();
+    }
+
+    /**
+     * Instantiate a new istance of clear coat configuration.
+     * @param markAllSubMeshesAsMiscDirty Callback to flag the material to dirty
+     */
+    constructor(markAllSubMeshesAsMiscDirty: () => void) {
+        this._internalMarkAllSubMeshesAsMiscDirty = markAllSubMeshesAsMiscDirty;
+    }
+
+    /**
+     * Checks to see if a texture is used in the material.
+     * @param defines the list of "defines" to update.
+     * @param mesh the mesh we are preparing the defines for.
+     */
+    public prepareDefines(defines: IMaterialAnisotropicDefines, mesh: AbstractMesh): void {
+        if (defines._areMiscDirty) {
+            defines.ANISOTROPIC = this._isEnabled;
+            if (this._isEnabled && !mesh.isVerticesDataPresent(VertexBuffer.TangentKind)) {
+                defines._needUVs = true;
+                defines.MAINUV1 = true;
+            }
+        }
+    }
+
+    /**
+     * Binds the material data.
+     * @param uniformBuffer defines the Uniform buffer to fill in.
+     * @param isFrozen defines wether the material is frozen or not.
+     */
+    public bindForSubMesh(uniformBuffer: UniformBuffer, isFrozen: boolean): void {
+        if (!uniformBuffer.useUbo || !isFrozen || !uniformBuffer.isSync) {
+            // Clear Coat
+            uniformBuffer.updateFloat("anisotropy", this.followTangents ? this.intensity : -this.intensity);
+        }
+    }
+
+    /**
+    * Get the current class name of the texture useful for serialization or dynamic coding.
+    * @returns "PBRAnisotropicConfiguration"
+    */
+    public getClassName(): string {
+        return "PBRAnisotropicConfiguration";
+    }
+
+    /**
+     * Makes a duplicate of the current configuration into another one.
+     * @param anisotropicConfiguration define the config where to copy the info
+     */
+    public copyTo(anisotropicConfiguration: PBRAnisotropicConfiguration): void {
+        SerializationHelper.Clone(() => anisotropicConfiguration, this);
+    }
+
+    /**
+     * Serializes this clear coat configuration.
+     * @returns - An object with the serialized config.
+     */
+    public serialize(): any {
+        return SerializationHelper.Serialize(this);
+    }
+
+    /**
+     * Parses a Clear Coat Configuration from a serialized object.
+     * @param source - Serialized object.
+     */
+    public parse(source: any): void {
+        SerializationHelper.Parse(() => this, source, null);
+    }
+
+    /**
+     * Add fallbacks to the effect fallbacks list.
+     * @param defines defines the Base texture to use.
+     * @param fallbacks defines the current fallback list.
+     * @param currentRank defines the current fallback rank.
+     * @returns the new fallback rank.
+     */
+    public static AddFallbacks(defines: IMaterialAnisotropicDefines, fallbacks: EffectFallbacks, currentRank: number): number {
+        if (defines.ANISOTROPIC) {
+            fallbacks.addFallback(currentRank++, "ANISOTROPIC");
+        }
+        return currentRank;
+    }
+
+    /**
+     * Add the required uniforms to the current list.
+     * @param uniforms defines the current uniform list.
+     */
+    public static AddUniforms(uniforms: string[]): void {
+        uniforms.push("anisotropy");
+    }
+
+    /**
+     * Add the required uniforms to the current buffer.
+     * @param uniformBuffer defines the current uniform buffer.
+     */
+    public static PrepareUniformBuffer(uniformBuffer: UniformBuffer): void {
+        uniformBuffer.addUniform("anisotropy", 1);
+    }
+}

+ 19 - 3
src/Materials/PBR/pbrBaseMaterial.ts

@@ -15,6 +15,7 @@ import { Mesh } from "../../Meshes/mesh";
 import { _TimeToken } from "../../Instrumentation/timeToken";
 import { _DepthCullingState, _StencilState, _AlphaState } from "../../States/index";
 import { IMaterialClearCoatDefines, PBRClearCoatConfiguration } from "./pbrClearCoatConfiguration";
+import { IMaterialAnisotropicDefines, PBRAnisotropicConfiguration } from "./pbrAnisotropicConfiguration";
 
 import { ImageProcessingConfiguration, IImageProcessingConfigurationDefines } from "../../Materials/imageProcessingConfiguration";
 import { Effect, EffectFallbacks, EffectCreationOptions } from "../../Materials/effect";
@@ -38,7 +39,8 @@ import "../../Shaders/pbr.vertex";
  * Manages the defines for the PBR Material.
  * @hiddenChildren
  */
-class PBRMaterialDefines extends MaterialDefines implements IImageProcessingConfigurationDefines, IMaterialClearCoatDefines {
+class PBRMaterialDefines extends MaterialDefines
+    implements IImageProcessingConfigurationDefines, IMaterialClearCoatDefines, IMaterialAnisotropicDefines {
     public PBR = true;
 
     public MAINUV1 = false;
@@ -184,6 +186,8 @@ class PBRMaterialDefines extends MaterialDefines implements IImageProcessingConf
     public CLEARCOAT_BUMP = false;
     public CLEARCOAT_BUMPDIRECTUV = 0;
 
+    public ANISOTROPIC = false;
+
     public UNLIT = false;
 
     /**
@@ -655,6 +659,11 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     public readonly clearCoat = new PBRClearCoatConfiguration(this._markAllSubMeshesAsTexturesDirty.bind(this));
 
     /**
+     * Defines the anisotropic parameters for the material.
+     */
+    public readonly anisotropy = new PBRAnisotropicConfiguration(this._markAllSubMeshesAsMiscDirty.bind(this));
+
+    /**
      * Instantiates a new PBRMaterial instance.
      *
      * @param name The material name
@@ -1010,6 +1019,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             fallbacks.addFallback(fallbackRank++, "PARALLAXOCCLUSION");
         }
 
+        fallbackRank = PBRAnisotropicConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
         fallbackRank = PBRClearCoatConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
 
         if (defines.ENVIRONMENTBRDF) {
@@ -1115,6 +1125,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         PBRClearCoatConfiguration.AddUniforms(uniforms);
         PBRClearCoatConfiguration.AddSamplers(samplers);
 
+        PBRAnisotropicConfiguration.AddUniforms(uniforms);
+
         if (ImageProcessingConfiguration) {
             ImageProcessingConfiguration.PrepareUniforms(uniforms, defines);
             ImageProcessingConfiguration.PrepareSamplers(samplers, defines);
@@ -1391,8 +1403,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             defines.SPECULARAA = scene.getEngine().getCaps().standardDerivatives && this._enableSpecularAntiAliasing;
         }
 
-        this.clearCoat.prepareDefines(defines, scene);
-
         if (defines._areImageProcessingDirty && this._imageProcessingConfiguration) {
             this._imageProcessingConfiguration.prepareDefines(defines);
         }
@@ -1409,6 +1419,10 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             defines.UNLIT = this._unlit || ((this.pointsCloud || this.wireframe) && !mesh.isVerticesDataPresent(VertexBuffer.NormalKind));
         }
 
+        // External config
+        this.clearCoat.prepareDefines(defines, scene);
+        this.anisotropy.prepareDefines(defines, mesh);
+
         // Values that need to be evaluated on every frame
         MaterialHelper.PrepareDefinesForFrameBoundValues(scene, engine, defines, useInstances ? true : false, useClipPlane);
 
@@ -1482,6 +1496,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         this._uniformBuffer.addUniform("pointSize", 1);
 
         PBRClearCoatConfiguration.PrepareUniformBuffer(this._uniformBuffer);
+        PBRAnisotropicConfiguration.PrepareUniformBuffer(this._uniformBuffer);
 
         this._uniformBuffer.create();
     }
@@ -1748,6 +1763,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             }
 
             this.clearCoat.bindForSubMesh(this._uniformBuffer, scene, engine, this._disableBumpMap, this.isFrozen, this._invertNormalMapX, this._invertNormalMapY);
+            this.anisotropy.bindForSubMesh(this._uniformBuffer, this.isFrozen);
 
             // Clip plane
             MaterialHelper.BindClipPlane(this._activeEffect, scene);

+ 5 - 5
src/Materials/PBR/pbrClearCoatConfiguration.ts

@@ -83,7 +83,7 @@ export class PBRClearCoatConfiguration {
 
     /**
      * Specifies that the submesh is ready to be used.
-     * @param defines defines the Base texture to use.
+     * @param defines the list of "defines" to update.
      * @param scene defines the scene the material belongs to.
      * @param engine defines the engine the material belongs to.
      * @param disableBumpMap defines wether the material disables bump or not.
@@ -112,7 +112,7 @@ export class PBRClearCoatConfiguration {
 
     /**
      * Checks to see if a texture is used in the material.
-     * @param defines defines the Base texture to use.
+     * @param defines the list of "defines" to update.
      * @param scene defines the scene to the material belongs to.
      */
     public prepareDefines(defines: IMaterialClearCoatDefines, scene: Scene): void {
@@ -257,10 +257,10 @@ export class PBRClearCoatConfiguration {
 
     /**
      * Makes a duplicate of the current configuration into another one.
-     * @param clearCoatconfiguration define the config where to copy the info
+     * @param clearCoatConfiguration define the config where to copy the info
      */
-    public copyTo(clearCoatconfiguration: PBRClearCoatConfiguration): void {
-        SerializationHelper.Clone(() => clearCoatconfiguration, this);
+    public copyTo(clearCoatConfiguration: PBRClearCoatConfiguration): void {
+        SerializationHelper.Clone(() => clearCoatConfiguration, this);
     }
 
     /**

+ 1 - 1
src/Materials/effect.ts

@@ -775,7 +775,7 @@ export class Effect {
         this.onCompiled = () => {
             var scenes = this.getEngine().scenes;
             for (var i = 0; i < scenes.length; i++) {
-                scenes[i].markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
+                scenes[i].markAllMaterialsAsDirty(Constants.MATERIAL_AllDirtyFlag);
             }
 
             if (onCompiled) {

+ 5 - 1
src/Shaders/ShadersInclude/bumpFragment.fx

@@ -13,7 +13,11 @@
 		mat3 TBN = cotangent_frame(normalW * normalScale, vPositionW, vBumpUV);
 	#endif
 #elif defined(ANISOTROPIC)
-	mat3 TBN = cotangent_frame(normalW, vPositionW, vMainUV1, vec2(1., 1.));
+	#if defined(TANGENT) && defined(NORMAL)
+		mat3 TBN = vTBN;
+	#else
+		mat3 TBN = cotangent_frame(normalW, vPositionW, vMainUV1, vec2(1., 1.));
+	#endif
 #endif
 
 #ifdef PARALLAX

+ 6 - 4
src/Shaders/ShadersInclude/lightFragment.fx

@@ -61,10 +61,12 @@
             #endif
 
             // Specular contribution
-            #ifdef ANISOTROPIC
-                info.specular = computeAnisotropicSpecularLighting(preInfo, viewDirectionW, normalW, vec3 T, vec3 B, anisotropy, specularEnvironmentR0, specularEnvironmentR90, AARoughnessFactors.x, light{X}.vLightDiffuse.rgb) {
-            #elif defined(SPECULARTERM)
-                info.specular = computeSpecularLighting(preInfo, normalW, specularEnvironmentR0, specularEnvironmentR90, AARoughnessFactors.x, light{X}.vLightDiffuse.rgb);
+            #ifdef SPECULARTERM
+                #ifdef ANISOTROPIC
+                    info.specular = computeAnisotropicSpecularLighting(preInfo, viewDirectionW, normalW, normalize(TBN[0]), normalize(TBN[1]), anisotropy, specularEnvironmentR0, specularEnvironmentR90, AARoughnessFactors.x, light{X}.vLightDiffuse.rgb);
+                #else
+                    info.specular = computeSpecularLighting(preInfo, normalW, specularEnvironmentR0, specularEnvironmentR90, AARoughnessFactors.x, light{X}.vLightDiffuse.rgb);
+                #endif
             #endif
 
             // Clear Coat contribution

+ 5 - 0
src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx

@@ -79,4 +79,9 @@ uniform mat4 view;
         uniform vec2 vClearCoatTangentSpaceParams;
         uniform mat4 clearCoatBumpMatrix;
     #endif
+#endif
+
+// Anisotropy
+#ifdef ANISOTROPIC
+    uniform float anisotropy;
 #endif

+ 2 - 2
src/Shaders/ShadersInclude/pbrFunctions.fx

@@ -92,7 +92,7 @@ float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH, float alphaG)
 vec2 getAnisotropicRoughness(float alphaG, float anisotropy) {
     float alphaT = max(alphaG * (1.0 + anisotropy), MINIMUMVARIANCE);
     float alphaB = max(alphaG * (1.0 - anisotropy), MINIMUMVARIANCE);
-    return vec2(at, ab);
+    return vec2(alphaT, alphaB);
 }
 
 // GGX Distribution Anisotropic
@@ -160,7 +160,7 @@ vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, flo
 
 vec3 computeAnisotropicSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float TdotH, float BdotH, float TdotV, float BdotV, float TdotL, float BdotL, float roughness, float anisotropy, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor) {
     float alphaG = convertRoughnessToAverageSlope(roughness);
-    vec2 alphaTB = getAnisotropicRoughness(alphaG, anisotropy)
+    vec2 alphaTB = getAnisotropicRoughness(alphaG, anisotropy);
     alphaTB = max(alphaTB, geometricRoughnessFactor * geometricRoughnessFactor);
 
     float distribution = normalDistributionFunction_BurleyGGX_Anisotropic(NdotH, TdotH, BdotH, alphaTB);

+ 1 - 2
src/Shaders/ShadersInclude/pbrLightingFunctions.fx

@@ -39,8 +39,7 @@ vec3 computeAnisotropicSpecularLighting(preLightingInfo info, vec3 V, vec3 N, ve
     float TdotL = dot(T, info.L);
     float BdotL = dot(B, info.L);
 
-    computeAnisotropicSpecularTerm(NdotH, info.NdotL, info.NdotV, info.VdotH, TdotH, BdotH, TdotV, BdotV, TdotL, BdotL, info.roughness, anisotropy, reflectance0, reflectance90, geometricRoughnessFactor);
-    vec3 specTerm = computeSpecularTerm(NdotH, info.NdotL, info.NdotV, info.VdotH, info.roughness, reflectance0, reflectance90, geometricRoughnessFactor);
+    vec3 specTerm = computeAnisotropicSpecularTerm(NdotH, info.NdotL, info.NdotV, info.VdotH, TdotH, BdotH, TdotV, BdotV, TdotL, BdotL, info.roughness, anisotropy, reflectance0, reflectance90, geometricRoughnessFactor);
     return specTerm * info.attenuation * info.NdotL * lightColor;
 }
 

+ 2 - 0
src/Shaders/ShadersInclude/pbrUboDeclaration.fx

@@ -44,6 +44,8 @@ uniform Material
     uniform vec2 vClearCoatBumpInfos;
     uniform vec2 vClearCoatTangentSpaceParams;
     uniform mat4 clearCoatBumpMatrix;
+
+    uniform float anisotropy;
 };
 
 uniform Scene {

+ 25 - 24
src/Shaders/pbr.fragment.fx

@@ -10,8 +10,6 @@
 #extension GL_EXT_frag_depth : enable
 #endif
 
-#define ANISOTROPIC 
-
 precision highp float;
 
 #include<__decl__pbrFragment>
@@ -449,7 +447,7 @@ void main(void) {
         #endif
     #endif
 
-    // _____________________________ Compute LODs Fetch ____________________________________
+    // _____________________________ Compute Geometry info _________________________________
     float NdotVUnclamped = dot(normalW, viewDirectionW);
     float NdotV = clamp(NdotVUnclamped,0., 1.) + 0.00001;
     float alphaG = convertRoughnessToAverageSlope(roughness);
@@ -460,22 +458,23 @@ void main(void) {
         alphaG += AARoughnessFactors.y;
     #endif
 
+    #ifdef ANISOTROPIC
+        vec3 anisotropicFrameDirection = anisotropy >= 0.0 ? TBN[1] : TBN[0];
+        vec3 anisotropicFrameTangent = cross(normalize(anisotropicFrameDirection), viewDirectionW);
+        vec3 anisotropicFrameNormal = cross(anisotropicFrameTangent, anisotropicFrameDirection);
+        vec3 anisotropicNormal = normalize(mix(normalW, anisotropicFrameNormal, abs(anisotropy)));
+    #endif
+
     // _____________________________ Refraction Info _______________________________________
     #ifdef REFRACTION
         vec4 environmentRefraction = vec4(0., 0., 0., 0.);
 
-        // #ifdef ANISOTROPIC
-        //     float anisotropy = -1.;
-        //     vec3 anisotropicDirection = anisotropy >= 0.0 ? TBN[1] : TBN[0];
-        //     vec3 anisotropicTangent = cross(normalize(anisotropicDirection), normalize(viewDirectionW));
-        //     vec3 anisotropicNormal = cross(anisotropicTangent, anisotropicDirection);
-        //     vec3 bentNormal = normalize(mix(normalW, anisotropicNormal, abs(anisotropy)));
-        //     vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), bentNormal);
-        // #else
-        //     vec3 reflectionVector = normalW;
-        // #endif
-
-        vec3 refractionVector = refract(-viewDirectionW, normalW, vRefractionInfos.y);
+        #ifdef ANISOTROPIC
+            vec3 refractionVector = refract(-viewDirectionW, anisotropicNormal, vRefractionInfos.y);
+        #else
+            vec3 refractionVector = refract(-viewDirectionW, normalW, vRefractionInfos.y);
+        #endif
+
         #ifdef REFRACTIONMAP_OPPOSITEZ
             refractionVector.z *= -1.0;
         #endif
@@ -555,15 +554,11 @@ void main(void) {
     #ifdef REFLECTION
         vec4 environmentRadiance = vec4(0., 0., 0., 0.);
         vec3 environmentIrradiance = vec3(0., 0., 0.);
+
         #ifdef ANISOTROPIC
-            float anisotropy = 1.;
-            vec3 anisotropicDirection = anisotropy >= 0.0 ? TBN[1] : TBN[0];
-            vec3 anisotropicTangent = cross(normalize(anisotropicDirection), normalize(viewDirectionW));
-            vec3 anisotropicNormal = cross(anisotropicTangent, anisotropicDirection);
-            vec3 bentNormal = normalize(mix(normalW, anisotropicNormal, abs(anisotropy)));
-            vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), bentNormal);
+            vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), anisotropicNormal);
         #else
-            vec3 reflectionVector = normalW;
+            vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), normalW);
         #endif
 
         #ifdef REFLECTIONMAP_OPPOSITEZ
@@ -642,10 +637,16 @@ void main(void) {
             #if defined(NORMAL) && defined(USESPHERICALINVERTEX)
                 environmentIrradiance = vEnvironmentIrradiance;
             #else
-                vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz;
+                #ifdef ANISOTROPIC
+                    vec3 irradianceVector = vec3(reflectionMatrix * vec4(anisotropicNormal, 0)).xyz;
+                #else
+                    vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz;
+                #endif
+
                 #ifdef REFLECTIONMAP_OPPOSITEZ
                     irradianceVector.z *= -1.0;
                 #endif
+
                 environmentIrradiance = environmentIrradianceJones(irradianceVector);
             #endif
         #endif
@@ -1159,7 +1160,7 @@ void main(void) {
 
     //gl_FragColor = vec4(seo * eho, seo * eho, seo * eho, 1.0);
 
-    //gl_FragColor = vec4(normalize(TBN[0]) * 0.5 + 0.5, 1.0);
+    //gl_FragColor = vec4(normalize(-TBN[0]), 1.0);
 
     //gl_FragColor = vec4(vPositionW * 0.5 + 0.5, 1.0);
 

BIN
tests/validation/ReferenceImages/anisotropic.png


+ 5 - 0
tests/validation/config.json

@@ -2,6 +2,11 @@
   "root": "https://rawgit.com/BabylonJS/Website/master",
   "tests": [
     {
+      "title": "Anisotropic",
+      "playgroundId": "#MAXCNU#1",
+      "referenceImage": "anisotropic.png"
+    },
+    {
       "title": "Clear Coat",
       "playgroundId": "#YACNQS#2",
       "referenceImage": "clearCoat.png"