Преглед изворни кода

Merge pull request #9760 from BabylonJS/morph-infinite

Morph infinite
David Catuhe пре 4 година
родитељ
комит
a644f1526b
36 измењених фајлова са 339 додато и 44 уклоњено
  1. BIN
      Playground/scenes/cubeMorph_8target.glb
  2. 2 0
      dist/preview release/what's new.md
  3. 2 0
      src/Engines/engineCapabilities.ts
  4. 1 0
      src/Engines/nativeEngine.ts
  5. 1 0
      src/Engines/nullEngine.ts
  6. 1 0
      src/Engines/thinEngine.ts
  7. 1 0
      src/Engines/webgpuEngine.ts
  8. 9 2
      src/Layers/effectLayer.ts
  9. 10 2
      src/Lights/Shadows/shadowGenerator.ts
  10. 40 7
      src/Materials/Node/Blocks/Vertex/morphTargetsBlock.ts
  11. 1 0
      src/Materials/Node/nodeMaterial.ts
  12. 3 2
      src/Materials/PBR/pbrBaseMaterial.ts
  13. 5 0
      src/Materials/materialHelper.ts
  14. 4 2
      src/Materials/standardMaterial.ts
  15. 9 0
      src/Meshes/mesh.ts
  16. 141 3
      src/Morph/morphTargetManager.ts
  17. 9 2
      src/Rendering/depthRenderer.ts
  18. 9 3
      src/Rendering/geometryBufferRenderer.ts
  19. 11 2
      src/Rendering/outlineRenderer.ts
  20. 31 9
      src/Shaders/ShadersInclude/morphTargetsVertex.fx
  21. 11 9
      src/Shaders/ShadersInclude/morphTargetsVertexDeclaration.fx
  22. 5 0
      src/Shaders/ShadersInclude/morphTargetsVertexGlobal.fx
  23. 13 0
      src/Shaders/ShadersInclude/morphTargetsVertexGlobalDeclaration.fx
  24. 1 0
      src/Shaders/default.vertex.fx
  25. 2 0
      src/Shaders/depth.vertex.fx
  26. 2 0
      src/Shaders/geometry.vertex.fx
  27. 3 0
      src/Shaders/glowMapGeneration.vertex.fx
  28. 1 0
      src/Shaders/outline.vertex.fx
  29. 1 0
      src/Shaders/pbr.vertex.fx
  30. 1 0
      src/Shaders/shadowMap.vertex.fx
  31. 2 0
      src/Shaders/volumetricLightScatteringPass.vertex.fx
  32. 5 0
      src/scene.ts
  33. BIN
      tests/validation/ReferenceImages/morphtargetstexture.png
  34. BIN
      tests/validation/ReferenceImages/morphtargetstextureshadows.png
  35. BIN
      tests/validation/ReferenceImages/simulatePointer.png
  36. 2 1
      tests/validation/config.json

BIN
Playground/scenes/cubeMorph_8target.glb


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

@@ -2,6 +2,8 @@
 
 ## Major updates
 
+- Infinite Morph Targets: When supported (WebGL2+) you are no more limited to 4 morph targets per mesh ([Deltakosh](https://github.com/deltakosh))
+
 ## Updates
 
 ### General

+ 2 - 0
src/Engines/engineCapabilities.ts

@@ -90,4 +90,6 @@ export interface EngineCapabilities {
     blendMinMax: boolean;
     /** In some iOS + WebGL1, gl_InstanceID (and gl_InstanceIDEXT) is undefined even if instancedArrays is true. So don't use gl_InstanceID in those cases */
     canUseGLInstanceID: boolean;
+    /** Defines if gl_vertexID is available */
+    canUseGLVertexID: boolean;
 }

+ 1 - 0
src/Engines/nativeEngine.ts

@@ -796,6 +796,7 @@ export class NativeEngine extends Engine {
             blendMinMax: false,
             maxMSAASamples: 1,
             canUseGLInstanceID: true,
+            canUseGLVertexID: true
         };
 
         this._features = {

+ 1 - 0
src/Engines/nullEngine.ts

@@ -142,6 +142,7 @@ export class NullEngine extends Engine {
             maxMSAASamples: 1,
             blendMinMax: false,
             canUseGLInstanceID: false,
+            canUseGLVertexID: false
         };
 
         this._features = {

+ 1 - 0
src/Engines/thinEngine.ts

@@ -911,6 +911,7 @@ export class ThinEngine {
             oculusMultiview: this._gl.getExtension('OCULUS_multiview'),
             depthTextureExtension: false,
             canUseGLInstanceID: !(this._badOS && this._webGLVersion <= 1),
+            canUseGLVertexID: this._webGLVersion > 1,
         };
 
         // Infos

+ 1 - 0
src/Engines/webgpuEngine.ts

@@ -540,6 +540,7 @@ export class WebGPUEngine extends Engine {
             blendMinMax: true,
             maxMSAASamples: 4,
             canUseGLInstanceID: true,
+            canUseGLVertexID: true
         };
 
         this._caps.parallelShaderCompile = null as any;

+ 9 - 2
src/Layers/effectLayer.ts

@@ -533,6 +533,9 @@ export abstract class EffectLayer {
                 defines.push("#define MORPHTARGETS");
                 morphInfluencers = manager.numInfluencers;
                 defines.push("#define NUM_MORPH_INFLUENCERS " + morphInfluencers);
+                if (manager.isUsingTextureForTargets) {
+                    defines.push("#define MORPHTARGETS_TEXTURE");
+                }
                 MaterialHelper.PrepareAttributesForMorphTargetsInfluencers(attribs, mesh, morphInfluencers);
             }
         }
@@ -556,8 +559,9 @@ export abstract class EffectLayer {
                 attribs,
                 ["world", "mBones", "viewProjection",
                     "glowColor", "morphTargetInfluences", "boneTextureWidth",
-                    "diffuseMatrix", "emissiveMatrix", "opacityMatrix", "opacityIntensity"],
-                ["diffuseSampler", "emissiveSampler", "opacitySampler", "boneSampler"], join,
+                    "diffuseMatrix", "emissiveMatrix", "opacityMatrix", "opacityIntensity",
+                    "morphTargetTextureInfo"],
+                ["diffuseSampler", "emissiveSampler", "opacitySampler", "boneSampler", "morphTargets"], join,
                 fallbacks, undefined, undefined, { maxSimultaneousMorphTargets: morphInfluencers });
         }
 
@@ -787,6 +791,9 @@ export abstract class EffectLayer {
 
             // Morph targets
             MaterialHelper.BindMorphTargetParameters(renderingMesh, this._effectLayerMapGenerationEffect);
+            if (renderingMesh.morphTargetManager && renderingMesh.morphTargetManager.isUsingTextureForTargets) {
+                renderingMesh.morphTargetManager._bind(this._effectLayerMapGenerationEffect);
+            }
 
             // Alpha mode
             if (enableAlphaMode) {

+ 10 - 2
src/Lights/Shadows/shadowGenerator.ts

@@ -1176,6 +1176,10 @@ export class ShadowGenerator implements IShadowGenerator {
                 // Morph targets
                 MaterialHelper.BindMorphTargetParameters(renderingMesh, effect);
 
+                if (renderingMesh.morphTargetManager && renderingMesh.morphTargetManager.isUsingTextureForTargets) {
+                    renderingMesh.morphTargetManager._bind(effect);
+                }
+
                 // Clip planes
                 MaterialHelper.BindClipPlane(effect, scene);
             }
@@ -1414,6 +1418,9 @@ export class ShadowGenerator implements IShadowGenerator {
                     defines.push("#define MORPHTARGETS");
                     morphInfluencers = manager.numInfluencers;
                     defines.push("#define NUM_MORPH_INFLUENCERS " + morphInfluencers);
+                    if (manager.isUsingTextureForTargets) {
+                        defines.push("#define MORPHTARGETS_TEXTURE");
+                    }
                     MaterialHelper.PrepareAttributesForMorphTargetsInfluencers(attribs, mesh, morphInfluencers);
                 }
             }
@@ -1465,8 +1472,9 @@ export class ShadowGenerator implements IShadowGenerator {
 
                 let shaderName = "shadowMap";
                 let uniforms = ["world", "mBones", "viewProjection", "diffuseMatrix", "lightDataSM", "depthValuesSM", "biasAndScaleSM", "morphTargetInfluences", "boneTextureWidth",
-                                "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "vClipPlane5", "vClipPlane6", "softTransparentShadowSM"];
-                let samplers = ["diffuseSampler", "boneSampler"];
+                                "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "vClipPlane5", "vClipPlane6", "softTransparentShadowSM",
+                                "morphTargetTextureInfo"];
+                let samplers = ["diffuseSampler", "boneSampler", "morphTargets"];
 
                 // Custom shader?
                 if (this.customShaderOptions) {

+ 40 - 7
src/Materials/Node/Blocks/Vertex/morphTargetsBlock.ts

@@ -155,6 +155,10 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
     public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh) {
         if (mesh && mesh.morphTargetManager && mesh.morphTargetManager.numInfluencers > 0) {
             MaterialHelper.BindMorphTargetParameters(mesh, effect);
+
+            if (mesh.morphTargetManager.isUsingTextureForTargets) {
+                mesh.morphTargetManager._bind(effect);
+            }
         }
     }
 
@@ -177,26 +181,53 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
 
         let injectionCode = "";
 
+        if (manager?.isUsingTextureForTargets && repeatCount > 0) {
+            injectionCode += `float vertexID = float(gl_VertexID) * morphTargetTextureInfo.x;\r\n`;
+        }
+
         for (var index = 0; index < repeatCount; index++) {
             injectionCode += `#ifdef MORPHTARGETS\r\n`;
-            injectionCode += `${positionOutput.associatedVariableName} += (position${index} - ${position.associatedVariableName}) * morphTargetInfluences[${index}];\r\n`;
+            if (manager?.isUsingTextureForTargets) {
+                injectionCode += `${positionOutput.associatedVariableName} += (readVector3FromRawSampler(morphTargets[${index}], vertexID) - ${position.associatedVariableName}) * morphTargetInfluences[${index}];\r\n`;
+                injectionCode += `vertexID += 1.0;\r\n`;
+            } else {
+                injectionCode += `${positionOutput.associatedVariableName} += (position${index} - ${position.associatedVariableName}) * morphTargetInfluences[${index}];\r\n`;
+            }
 
             if (hasNormals) {
                 injectionCode += `#ifdef MORPHTARGETS_NORMAL\r\n`;
-                injectionCode += `${normalOutput.associatedVariableName} += (normal${index} - ${normal.associatedVariableName}) * morphTargetInfluences[${index}];\r\n`;
+                if (manager?.isUsingTextureForTargets) {
+                    injectionCode += `${normalOutput.associatedVariableName} += (readVector3FromRawSampler(morphTargets[${index}], vertexID) - ${normal.associatedVariableName}) * morphTargetInfluences[${index}];\r\n`;
+                    injectionCode += `vertexID += 1.0;\r\n`;
+                } else {
+                    injectionCode += `${normalOutput.associatedVariableName} += (normal${index} - ${normal.associatedVariableName}) * morphTargetInfluences[${index}];\r\n`;
+                }
+                injectionCode += `#endif\r\n`;
+            }
+
+            if (hasUVs) {
+                injectionCode += `#ifdef MORPHTARGETS_UV\r\n`;
+                if (manager?.isUsingTextureForTargets) {
+                    injectionCode += `${uvOutput.associatedVariableName} += (readVector3FromRawSampler(morphTargets[${index}], vertexID).xy - ${uv.associatedVariableName}) * morphTargetInfluences[${index}];\r\n`;
+                    injectionCode += `vertexID += 1.0;\r\n`;
+                } else {
+                    injectionCode += `${uvOutput.associatedVariableName}.xy += (uv_${index} - ${uv.associatedVariableName}.xy) * morphTargetInfluences[${index}];\r\n`;
+                }
                 injectionCode += `#endif\r\n`;
             }
 
             if (hasTangents) {
                 injectionCode += `#ifdef MORPHTARGETS_TANGENT\r\n`;
-                injectionCode += `${tangentOutput.associatedVariableName}.xyz += (tangent${index} - ${tangent.associatedVariableName}.xyz) * morphTargetInfluences[${index}];\r\n`;
+                if (manager?.isUsingTextureForTargets) {
+                    injectionCode += `${tangentOutput.associatedVariableName} += (readVector3FromRawSampler(morphTargets[${index}], vertexID) - ${tangent.associatedVariableName}) * morphTargetInfluences[${index}];\r\n`;
+                } else {
+                    injectionCode += `${tangentOutput.associatedVariableName}.xyz += (tangent${index} - ${tangent.associatedVariableName}.xyz) * morphTargetInfluences[${index}];\r\n`;
+                }
                 injectionCode += `#endif\r\n`;
             }
 
-            if (hasUVs) {
-                injectionCode += `#ifdef MORPHTARGETS_UV\r\n`;
-                injectionCode += `${uvOutput.associatedVariableName}.xy += (uv_${index} - ${uv.associatedVariableName}.xy) * morphTargetInfluences[${index}];\r\n`;
-                injectionCode += `#endif\r\n`;
+            if (manager?.isUsingTextureForTargets) {
+                injectionCode += `vertexID = float(gl_VertexID) * morphTargetTextureInfo.x;\r\n`;
             }
 
             injectionCode += `#endif\r\n`;
@@ -247,6 +278,8 @@ export class MorphTargetsBlock extends NodeMaterialBlock {
         let comments = `//${this.name}`;
 
         state.uniforms.push("morphTargetInfluences");
+        state.uniforms.push("morphTargetTextureInfo");
+        state.samplers.push("morphTargets");
 
         state._emitFunctionFromInclude("morphTargetsVertexGlobalDeclaration", comments);
         state._emitFunctionFromInclude("morphTargetsVertexDeclaration", comments, {

+ 1 - 0
src/Materials/Node/nodeMaterial.ts

@@ -82,6 +82,7 @@ export class NodeMaterialDefines extends MaterialDefines implements IImageProces
     public MORPHTARGETS_TANGENT = false;
     public MORPHTARGETS_UV = false;
     public NUM_MORPH_INFLUENCERS = 0;
+    public MORPHTARGETS_TEXTURE = false;
 
     /** IMAGE PROCESSING */
     public IMAGEPROCESSING = false;

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

@@ -192,6 +192,7 @@ export class PBRMaterialDefines extends MaterialDefines
     public MORPHTARGETS_TANGENT = false;
     public MORPHTARGETS_UV = false;
     public NUM_MORPH_INFLUENCERS = 0;
+    public MORPHTARGETS_TEXTURE = false;
 
     public IMAGEPROCESSING = false;
     public VIGNETTE = false;
@@ -1295,13 +1296,13 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             "vSphericalL2_2", "vSphericalL2_1", "vSphericalL20", "vSphericalL21", "vSphericalL22",
             "vReflectionMicrosurfaceInfos",
             "vTangentSpaceParams", "boneTextureWidth",
-            "vDebugMode"
+            "vDebugMode", "morphTargetTextureInfo"
         ];
 
         var samplers = ["albedoSampler", "reflectivitySampler", "ambientSampler", "emissiveSampler",
             "bumpSampler", "lightmapSampler", "opacitySampler",
             "reflectionSampler", "reflectionSamplerLow", "reflectionSamplerHigh", "irradianceSampler",
-            "microSurfaceSampler", "environmentBrdfSampler", "boneSampler", "metallicReflectanceSampler"];
+            "microSurfaceSampler", "environmentBrdfSampler", "boneSampler", "metallicReflectanceSampler", "morphTargets"];
 
         var uniformBuffers = ["Material", "Scene", "Mesh"];
 

+ 5 - 0
src/Materials/materialHelper.ts

@@ -269,6 +269,8 @@ export class MaterialHelper {
             defines["MORPHTARGETS_NORMAL"] = manager.supportsNormals && defines["NORMAL"];
             defines["MORPHTARGETS"] = (manager.numInfluencers > 0);
             defines["NUM_MORPH_INFLUENCERS"] = manager.numInfluencers;
+
+            defines["MORPHTARGETS_TEXTURE"] = manager.isUsingTextureForTargets;
         } else {
             defines["MORPHTARGETS_UV"] = false;
             defines["MORPHTARGETS_TANGENT"] = false;
@@ -755,6 +757,9 @@ export class MaterialHelper {
         if (influencers > 0 && EngineStore.LastCreatedEngine) {
             var maxAttributesCount = EngineStore.LastCreatedEngine.getCaps().maxVertexAttribs;
             var manager = (<Mesh>mesh).morphTargetManager;
+            if (manager?.isUsingTextureForTargets) {
+                return;
+            }
             var normal = manager && manager.supportsNormals && defines["NORMAL"];
             var tangent = manager && manager.supportsTangents && defines["TANGENT"];
             var uv = manager && manager.supportsUVs && defines["UV1"];

+ 4 - 2
src/Materials/standardMaterial.ts

@@ -123,6 +123,7 @@ export class StandardMaterialDefines extends MaterialDefines implements IImagePr
     public MORPHTARGETS_TANGENT = false;
     public MORPHTARGETS_UV = false;
     public NUM_MORPH_INFLUENCERS = 0;
+    public MORPHTARGETS_TEXTURE = false;
     public NONUNIFORMSCALING = false; // https://playground.babylonjs.com#V6DWIH
     public PREMULTIPLYALPHA = false; // https://playground.babylonjs.com#LNVJJ7
     public ALPHATEST_AFTERALLALPHACOMPUTATIONS = false;
@@ -1214,12 +1215,13 @@ export class StandardMaterial extends PushMaterial {
                 "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "vClipPlane5", "vClipPlane6", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix", "normalMatrix", "lightmapMatrix", "refractionMatrix",
                 "diffuseLeftColor", "diffuseRightColor", "opacityParts", "reflectionLeftColor", "reflectionRightColor", "emissiveLeftColor", "emissiveRightColor", "refractionLeftColor", "refractionRightColor",
                 "vReflectionPosition", "vReflectionSize",
-                "logarithmicDepthConstant", "vTangentSpaceParams", "alphaCutOff", "boneTextureWidth"
+                "logarithmicDepthConstant", "vTangentSpaceParams", "alphaCutOff", "boneTextureWidth",
+                "morphTargetTextureInfo"
             ];
 
             var samplers = ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler",
                 "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler", "lightmapSampler",
-                "refractionCubeSampler", "refraction2DSampler", "boneSampler"];
+                "refractionCubeSampler", "refraction2DSampler", "boneSampler", "morphTargets"];
 
             var uniformBuffers = ["Material", "Scene", "Mesh"];
 

+ 9 - 0
src/Meshes/mesh.ts

@@ -1511,6 +1511,11 @@ export class Mesh extends AbstractMesh implements IGetSetVerticesData {
 
         var engine = this.getScene().getEngine();
 
+        // Morph targets
+        if (this.morphTargetManager && this.morphTargetManager.isUsingTextureForTargets) {
+            this.morphTargetManager._bind(effect);
+        }
+
         // Wireframe
         var indexToBind;
 
@@ -3298,6 +3303,10 @@ export class Mesh extends AbstractMesh implements IGetSetVerticesData {
                 return;
             }
 
+            if (morphTargetManager.isUsingTextureForTargets) {
+                return;
+            }
+
             for (var index = 0; index < morphTargetManager.numInfluencers; index++) {
                 var morphTarget = morphTargetManager.getActiveTarget(index);
 

+ 141 - 3
src/Morph/morphTargetManager.ts

@@ -2,15 +2,18 @@ import { Observer } from "../Misc/observable";
 import { SmartArray } from "../Misc/smartArray";
 import { Logger } from "../Misc/logger";
 import { Nullable } from "../types";
-import { Scene } from "../scene";
+import { IDisposable, Scene } from "../scene";
 import { EngineStore } from "../Engines/engineStore";
 import { Mesh } from "../Meshes/mesh";
 import { MorphTarget } from "./morphTarget";
+import { RawTexture } from "../Materials/Textures/rawTexture";
+import { Constants } from "../Engines/constants";
+import { Effect } from "../Materials/effect";
 /**
  * This class is used to deform meshes using morphing between different targets
  * @see https://doc.babylonjs.com/how_to/how_to_use_morphtargets
  */
-export class MorphTargetManager {
+export class MorphTargetManager implements IDisposable {
     private _targets = new Array<MorphTarget>();
     private _targetInfluenceChangedObservers = new Array<Nullable<Observer<boolean>>>();
     private _targetDataLayoutChangedObservers = new Array<Nullable<Observer<void>>>();
@@ -21,8 +24,15 @@ export class MorphTargetManager {
     private _supportsTangents = false;
     private _supportsUVs = false;
     private _vertexCount = 0;
+    private _textureVertexStride = 0;
+    private _textureWidth = 0;
+    private _textureHeight = 1;
     private _uniqueId = 0;
     private _tempInfluences = new Array<number>();
+    private _canUseTextureForTargets = false;
+
+    /** @hidden */
+    public _targetStoreTextures: Array<RawTexture> = [];
 
     /**
      * Gets or sets a boolean indicating if normals must be morphed
@@ -54,6 +64,9 @@ export class MorphTargetManager {
             this._scene.morphTargetManagers.push(this);
 
             this._uniqueId = this._scene.getUniqueId();
+
+            const engineCaps = this._scene.getEngine().getCaps();
+            this._canUseTextureForTargets = engineCaps.canUseGLVertexID && engineCaps.textureFloat && engineCaps.maxVertexTextureImageUnits > 0;
         }
     }
 
@@ -113,6 +126,26 @@ export class MorphTargetManager {
         return this._influences;
     }
 
+    private _useTextureToStoreTargets = true;
+    /**
+     * Gets or sets a boolean indicating that targets should be stored as a texture instead of using vertex attributes (default is true).
+     * Please note that this option is not available if the hardware does not support it
+     */
+    public get useTextureToStoreTargets(): boolean {
+        return this._useTextureToStoreTargets;
+    }
+
+    public set useTextureToStoreTargets(value: boolean) {
+        this._useTextureToStoreTargets = value;
+    }
+
+    /**
+     * Gets a boolean indicating that the targets are stored into a texture (instead of as attributes)
+     */
+    public get isUsingTextureForTargets() {
+        return this.useTextureToStoreTargets && this._canUseTextureForTargets;
+    }
+
     /**
      * Gets the active target at specified index. An active target is a target with an influence > 0
      * @param index defines the index to check
@@ -161,6 +194,12 @@ export class MorphTargetManager {
         }
     }
 
+    /** @hidden */
+    public _bind(effect: Effect) {
+        effect.setFloat3("morphTargetTextureInfo", this._textureVertexStride, this._textureWidth, this._textureHeight);
+        effect.setTextureArray("morphTargets", this._targetStoreTextures);
+    }
+
     /**
      * Clone the current manager
      * @returns a new MorphTargetManager
@@ -242,12 +281,100 @@ export class MorphTargetManager {
     }
 
     /**
-     * Syncrhonize the targets with all the meshes using this morph target manager
+     * Synchronize the targets with all the meshes using this morph target manager
      */
     public synchronize(): void {
         if (!this._scene) {
             return;
         }
+
+        if (this.isUsingTextureForTargets) {
+            if (!this._vertexCount) {
+                return;
+            }
+
+            this._textureVertexStride = 1;
+
+            if (this._supportsNormals) {
+                this._textureVertexStride++;
+            }
+
+            if (this._supportsTangents) {
+                this._textureVertexStride++;
+            }
+
+            if (this._supportsUVs) {
+                this._textureVertexStride++;
+            }
+
+            this._textureWidth = this._vertexCount * this._textureVertexStride;
+            this._textureHeight = 1;
+
+            const maxTextureSize = this._scene.getEngine().getCaps().maxTextureSize;
+            if (this._textureWidth > maxTextureSize) {
+                this._textureHeight = Math.ceil(this._textureWidth / maxTextureSize);
+                this._textureWidth = maxTextureSize;
+            }
+
+            for (var index = 0; index < this._targets.length; index++) {
+                let target = this._targets[index];
+
+                if (!this._targetStoreTextures[index]
+                    || this._targetStoreTextures[index].getSize().width !== this._textureWidth
+                    || this._targetStoreTextures[index].getSize().height !== this._textureHeight) {
+                    if (this._targetStoreTextures[index]) {
+                        this._targetStoreTextures[index].dispose();
+                    }
+
+                    let data = new Float32Array(this._textureWidth * this._textureHeight * 4);
+
+                    let offset = 0;
+                    const positions = target.getPositions();
+                    const normals = target.getNormals();
+                    const uvs = target.getUVs();
+                    const tangents = target.getTangents();
+
+                    if (!positions) {
+                        if (index === 0) {
+                            Logger.Error("Invalid morph target. Target must have positions.");
+                        }
+                        return;
+                    }
+
+                    for (var vertex = 0; vertex < this._vertexCount; vertex++) {
+                        data[offset] = positions[vertex * 3];
+                        data[offset + 1] = positions[vertex * 3 + 1];
+                        data[offset + 2] = positions[vertex * 3 + 2];
+
+                        offset += 4;
+
+                        if (normals) {
+                            data[offset] = normals[vertex * 3];
+                            data[offset + 1] = normals[vertex * 3 + 1];
+                            data[offset + 2] = normals[vertex * 3 + 2];
+                            offset += 4;
+                        }
+
+                        if (uvs) {
+                            data[offset] = uvs[vertex * 2];
+                            data[offset + 1] = uvs[vertex * 2 + 1];
+                            offset += 4;
+                        }
+
+                        if (tangents) {
+                            data[offset] = tangents[vertex * 3];
+                            data[offset + 1] = tangents[vertex * 3 + 1];
+                            data[offset + 2] = tangents[vertex * 3 + 2];
+                            offset += 4;
+                        }
+                    }
+
+                    this._targetStoreTextures[index] = RawTexture.CreateRGBATexture(data, this._textureWidth, this._textureHeight,
+                        this._scene, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_FLOAT);
+                }
+            }
+        }
+
         // Flag meshes as dirty to resync with the active targets
         for (var mesh of this._scene.meshes) {
             if ((<any>mesh).morphTargetManager === this) {
@@ -256,6 +383,17 @@ export class MorphTargetManager {
         }
     }
 
+    /**
+     * Release all resources
+     */
+    public dispose() {
+        for (var targetTexture of this._targetStoreTextures) {
+            targetTexture.dispose();
+        }
+
+        this._targetStoreTextures = [];
+    }
+
     // Statics
 
     /**

+ 9 - 2
src/Rendering/depthRenderer.ts

@@ -154,6 +154,9 @@ export class DepthRenderer {
 
                 // Morph targets
                 MaterialHelper.BindMorphTargetParameters(renderingMesh, this._effect);
+                if (renderingMesh.morphTargetManager && renderingMesh.morphTargetManager.isUsingTextureForTargets) {
+                    renderingMesh.morphTargetManager._bind(this._effect);
+                }
 
                 // Draw
                 renderingMesh._processRendering(effectiveMesh, subMesh, this._effect, material.fillMode, batch, hardwareInstancedRendering,
@@ -237,6 +240,10 @@ export class DepthRenderer {
                 defines.push("#define MORPHTARGETS");
                 defines.push("#define NUM_MORPH_INFLUENCERS " + numMorphInfluencers);
 
+                if (morphTargetManager.isUsingTextureForTargets) {
+                    defines.push("#define MORPHTARGETS_TEXTURE");
+                }
+
                 MaterialHelper.PrepareAttributesForMorphTargetsInfluencers(attribs, mesh, numMorphInfluencers);
             }
         }
@@ -266,8 +273,8 @@ export class DepthRenderer {
             this._cachedDefines = join;
             this._effect = this._scene.getEngine().createEffect("depth",
                 attribs,
-                ["world", "mBones", "viewProjection", "diffuseMatrix", "depthValues", "morphTargetInfluences"],
-                ["diffuseSampler"], join,
+                ["world", "mBones", "viewProjection", "diffuseMatrix", "depthValues", "morphTargetInfluences", "morphTargetTextureInfo"],
+                ["diffuseSampler", "morphTargets"], join,
                 undefined, undefined, undefined, { maxSimultaneousMorphTargets: numMorphInfluencers });
         }
 

+ 9 - 3
src/Rendering/geometryBufferRenderer.ts

@@ -428,7 +428,9 @@ export class GeometryBufferRenderer {
 
                 defines.push("#define MORPHTARGETS");
                 defines.push("#define NUM_MORPH_INFLUENCERS " + numMorphInfluencers);
-
+                if (morphTargetManager.isUsingTextureForTargets) {
+                    defines.push("#define MORPHTARGETS_TEXTURE");
+                }
                 MaterialHelper.PrepareAttributesForMorphTargetsInfluencers(attribs, mesh, numMorphInfluencers);
             }
         }
@@ -458,9 +460,10 @@ export class GeometryBufferRenderer {
                     attributes: attribs,
                     uniformsNames: [
                         "world", "mBones", "viewProjection", "diffuseMatrix", "view", "previousWorld", "previousViewProjection", "mPreviousBones",
-                        "morphTargetInfluences", "bumpMatrix", "reflectivityMatrix", "vTangentSpaceParams", "vBumpInfos"
+                        "bumpMatrix", "reflectivityMatrix", "vTangentSpaceParams", "vBumpInfos",
+                        "morphTargetInfluences", "morphTargetTextureInfo"
                     ],
-                    samplers: ["diffuseSampler", "bumpSampler", "reflectivitySampler"],
+                    samplers: ["diffuseSampler", "bumpSampler", "reflectivitySampler", "morphTargets"],
                     defines: join,
                     onCompiled: null,
                     fallbacks: null,
@@ -664,6 +667,9 @@ export class GeometryBufferRenderer {
 
                 // Morph targets
                 MaterialHelper.BindMorphTargetParameters(renderingMesh, this._effect);
+                if (renderingMesh.morphTargetManager && renderingMesh.morphTargetManager.isUsingTextureForTargets) {
+                    renderingMesh.morphTargetManager._bind(this._effect);
+                }
 
                 // Velocity
                 if (this._enableVelocity) {

+ 11 - 2
src/Rendering/outlineRenderer.ts

@@ -190,6 +190,10 @@ export class OutlineRenderer implements ISceneComponent {
             this._effect.setMatrices("mBones", renderingMesh.skeleton.getTransformMatrices(renderingMesh));
         }
 
+        if (renderingMesh.morphTargetManager && renderingMesh.morphTargetManager.isUsingTextureForTargets) {
+            renderingMesh.morphTargetManager._bind(this._effect);
+        }
+
         // Morph targets
         MaterialHelper.BindMorphTargetParameters(renderingMesh, this._effect);
 
@@ -267,6 +271,10 @@ export class OutlineRenderer implements ISceneComponent {
                 defines.push("#define MORPHTARGETS");
                 defines.push("#define NUM_MORPH_INFLUENCERS " + numMorphInfluencers);
 
+                if (morphTargetManager.isUsingTextureForTargets) {
+                    defines.push("#define MORPHTARGETS_TEXTURE");
+                }
+
                 MaterialHelper.PrepareAttributesForMorphTargetsInfluencers(attribs, mesh, numMorphInfluencers);
             }
         }
@@ -286,8 +294,9 @@ export class OutlineRenderer implements ISceneComponent {
             this._cachedDefines = join;
             this._effect = this.scene.getEngine().createEffect("outline",
                 attribs,
-                ["world", "mBones", "viewProjection", "diffuseMatrix", "offset", "color", "logarithmicDepthConstant", "morphTargetInfluences"],
-                ["diffuseSampler"], join,
+                ["world", "mBones", "viewProjection", "diffuseMatrix", "offset", "color", "logarithmicDepthConstant",
+                "morphTargetInfluences", "morphTargetTextureInfo"],
+                ["diffuseSampler", "morphTargets"], join,
                 undefined, undefined, undefined,
                 { maxSimultaneousMorphTargets: numMorphInfluencers });
         }

+ 31 - 9
src/Shaders/ShadersInclude/morphTargetsVertex.fx

@@ -1,15 +1,37 @@
 #ifdef MORPHTARGETS
-	positionUpdated += (position{X} - position) * morphTargetInfluences[{X}];
+	#ifdef MORPHTARGETS_TEXTURE	
+		positionUpdated += (readVector3FromRawSampler(morphTargets[{X}], vertexID) - position) * morphTargetInfluences[{X}];
+		vertexID += 1.0;
 	
-	#ifdef MORPHTARGETS_NORMAL
-	normalUpdated += (normal{X} - normal) * morphTargetInfluences[{X}];
-	#endif
+		#ifdef MORPHTARGETS_NORMAL
+			normalUpdated += (readVector3FromRawSampler(morphTargets[{X}], vertexID)  - normal) * morphTargetInfluences[{X}];
+			vertexID += 1.0;
+		#endif
 
-	#ifdef MORPHTARGETS_TANGENT
-	tangentUpdated.xyz += (tangent{X} - tangent.xyz) * morphTargetInfluences[{X}];
-	#endif
+		#ifdef MORPHTARGETS_UV
+			uvUpdated += (readVector3FromRawSampler(morphTargets[{X}], vertexID).xy - uv) * morphTargetInfluences[{X}];
+			vertexID += 1.0;
+		#endif
+
+		#ifdef MORPHTARGETS_TANGENT
+			tangentUpdated.xyz += (readVector3FromRawSampler(morphTargets[{X}], vertexID)  - tangent.xyz) * morphTargetInfluences[{X}];
+		#endif
+
+		// Restore for next target
+		vertexID = float(gl_VertexID) * morphTargetTextureInfo.x;
+	#else
+		positionUpdated += (position{X} - position) * morphTargetInfluences[{X}];
+		
+		#ifdef MORPHTARGETS_NORMAL
+		normalUpdated += (normal{X} - normal) * morphTargetInfluences[{X}];
+		#endif
+
+		#ifdef MORPHTARGETS_TANGENT
+		tangentUpdated.xyz += (tangent{X} - tangent.xyz) * morphTargetInfluences[{X}];
+		#endif
 
-    #ifdef MORPHTARGETS_UV
-	uvUpdated += (uv_{X} - uv) * morphTargetInfluences[{X}];
+		#ifdef MORPHTARGETS_UV
+		uvUpdated += (uv_{X} - uv) * morphTargetInfluences[{X}];
+		#endif
 	#endif
 #endif

+ 11 - 9
src/Shaders/ShadersInclude/morphTargetsVertexDeclaration.fx

@@ -1,15 +1,17 @@
 #ifdef MORPHTARGETS
-	attribute vec3 position{X};
+	#ifndef MORPHTARGETS_TEXTURE
+		attribute vec3 position{X};
 
-	#ifdef MORPHTARGETS_NORMAL
-	attribute vec3 normal{X};
-	#endif
+		#ifdef MORPHTARGETS_NORMAL
+		attribute vec3 normal{X};
+		#endif
 
-	#ifdef MORPHTARGETS_TANGENT
-	attribute vec3 tangent{X};
-	#endif
+		#ifdef MORPHTARGETS_TANGENT
+		attribute vec3 tangent{X};
+		#endif
 
-    #ifdef MORPHTARGETS_UV
-	attribute vec2 uv_{X};
+		#ifdef MORPHTARGETS_UV
+		attribute vec2 uv_{X};
+		#endif
 	#endif
 #endif

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

@@ -0,0 +1,5 @@
+#ifdef MORPHTARGETS
+	#ifdef MORPHTARGETS_TEXTURE
+		float vertexID = float(gl_VertexID) * morphTargetTextureInfo.x;
+	#endif
+#endif

+ 13 - 0
src/Shaders/ShadersInclude/morphTargetsVertexGlobalDeclaration.fx

@@ -1,3 +1,16 @@
 #ifdef MORPHTARGETS
 	uniform float morphTargetInfluences[NUM_MORPH_INFLUENCERS];
+
+	#ifdef MORPHTARGETS_TEXTURE	
+		uniform vec3 morphTargetTextureInfo;
+		uniform sampler2D morphTargets[NUM_MORPH_INFLUENCERS];
+
+		vec3 readVector3FromRawSampler(sampler2D smp, float vertexIndex)
+		{			
+			float y = floor(vertexIndex / morphTargetTextureInfo.y);
+			float x = vertexIndex - y * morphTargetTextureInfo.y;
+			vec2 textureUV = vec2((x + 0.5) / morphTargetTextureInfo.y, y / morphTargetTextureInfo.z);
+			return texture2D(smp, textureUV).xyz;
+		}
+	#endif
 #endif

+ 1 - 0
src/Shaders/default.vertex.fx

@@ -120,6 +120,7 @@ void main(void) {
 	vec2 uvUpdated = uv;
 #endif
 
+#include<morphTargetsVertexGlobal>
 #include<morphTargetsVertex>[0..maxSimultaneousMorphTargets]
 
 #ifdef REFLECTIONMAP_SKYBOX

+ 2 - 0
src/Shaders/depth.vertex.fx

@@ -30,6 +30,8 @@ void main(void)
 #ifdef UV1
     vec2 uvUpdated = uv;
 #endif
+
+#include<morphTargetsVertexGlobal>
 #include<morphTargetsVertex>[0..maxSimultaneousMorphTargets]
 
 #include<instancesVertex>

+ 2 - 0
src/Shaders/geometry.vertex.fx

@@ -71,6 +71,8 @@ void main(void)
 #ifdef UV1
     vec2 uvUpdated = uv;
 #endif
+
+#include<morphTargetsVertexGlobal>
 #include<morphTargetsVertex>[0..maxSimultaneousMorphTargets]
 
 #include<instancesVertex>

+ 3 - 0
src/Shaders/glowMapGeneration.vertex.fx

@@ -47,7 +47,10 @@ void main(void)
 #ifdef UV1
     vec2 uvUpdated = uv;
 #endif
+
+#include<morphTargetsVertexGlobal>
 #include<morphTargetsVertex>[0..maxSimultaneousMorphTargets]
+
 #include<instancesVertex>
 #include<bonesVertex>
 

+ 1 - 0
src/Shaders/outline.vertex.fx

@@ -33,6 +33,7 @@ void main(void)
 #ifdef UV1
     vec2 uvUpdated = uv;
 #endif    
+    #include<morphTargetsVertexGlobal>
     #include<morphTargetsVertex>[0..maxSimultaneousMorphTargets]
 
 	vec3 offsetPosition = positionUpdated + (normalUpdated * offset);

+ 1 - 0
src/Shaders/pbr.vertex.fx

@@ -173,6 +173,7 @@ void main(void) {
     vec2 uvUpdated = uv;
 #endif  
 
+#include<morphTargetsVertexGlobal>
 #include<morphTargetsVertex>[0..maxSimultaneousMorphTargets]
 
 #ifdef REFLECTIONMAP_SKYBOX

+ 1 - 0
src/Shaders/shadowMap.vertex.fx

@@ -41,6 +41,7 @@ vec3 positionUpdated = position;
 	vec3 normalUpdated = normal;
 #endif
 
+#include<morphTargetsVertexGlobal>
 #include<morphTargetsVertex>[0..maxSimultaneousMorphTargets]
 
 #include<instancesVertex>

+ 2 - 0
src/Shaders/volumetricLightScatteringPass.vertex.fx

@@ -28,6 +28,8 @@ void main(void)
 #if (defined(ALPHATEST) || defined(NEED_UV)) && defined(UV1)
     vec2 uvUpdated = uv;
 #endif
+
+#include<morphTargetsVertexGlobal>
 #include<morphTargetsVertex>[0..maxSimultaneousMorphTargets]
 
 #include<instancesVertex>

+ 5 - 0
src/scene.ts

@@ -4385,6 +4385,11 @@ export class Scene extends AbstractScene implements IAnimatable, IClipPlanesHold
             this.textures[0].dispose();
         }
 
+        // Release morph targets
+        while (this.morphTargetManagers.length) {
+            this.morphTargetManagers[0].dispose();
+        }
+
         // Release UBO
         this._sceneUbo.dispose();
 

BIN
tests/validation/ReferenceImages/morphtargetstexture.png


BIN
tests/validation/ReferenceImages/morphtargetstextureshadows.png


BIN
tests/validation/ReferenceImages/simulatePointer.png


+ 2 - 1
tests/validation/config.json

@@ -1,6 +1,7 @@
 {
     "root": "https://cdn.babylonjs.com",
     "tests": [
+
         {
             "title": "Sprites",
             "playgroundId": "#ZX8DJ3#1",
@@ -1165,7 +1166,7 @@
         },
         {
             "title": "Prepass SSAO + visibility",
-            "renderCount": 10,
+            "renderCount": 30,
             "playgroundId": "#PXC9CF#4",
             "excludedEngines": ["webgl1"],
             "referenceImage": "prepass-ssao-visibility.png"