فهرست منبع

Materials Library: Fur Material

- Abstracted shells: FurMaterial.FurifyMesh
- Added notion of density
- Set Hight Level to true by default
- Fixed UVs of fur
- Fixed serialization and deserialization
luaacro 9 سال پیش
والد
کامیت
a9991e85e8

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 55 - 5
materialsLibrary/dist/babylon.furMaterial.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
materialsLibrary/dist/babylon.furMaterial.min.js


+ 3 - 0
materialsLibrary/dist/dts/babylon.furMaterial.d.ts

@@ -11,6 +11,7 @@ declare module BABYLON {
         furSpacing: number;
         furGravity: Vector3;
         furSpeed: number;
+        furDensity: number;
         furTexture: DynamicTexture;
         disableLighting: boolean;
         highLevelFur: boolean;
@@ -21,6 +22,7 @@ declare module BABYLON {
         private _defines;
         private _cachedDefines;
         constructor(name: string, scene: Scene);
+        furTime: number;
         needAlphaBlending(): boolean;
         needAlphaTesting(): boolean;
         getAlphaTestTexture(): BaseTexture;
@@ -34,5 +36,6 @@ declare module BABYLON {
         serialize(): any;
         static Parse(source: any, scene: Scene, rootUrl: string): FurMaterial;
         static GenerateTexture(name: string, scene: Scene): DynamicTexture;
+        static FurifyMesh(sourceMesh: Mesh, quality: number): Mesh[];
     }
 }

+ 60 - 5
materialsLibrary/materials/fur/babylon.furMaterial.ts

@@ -69,14 +69,15 @@ module BABYLON {
         public furAngle: number = 0;
         public furColor = new Color3(0.44,0.21,0.02);
         
-        public furOffset: number = 0.9;
+        public furOffset: number = 0.0;
         public furSpacing: number = 12;
         public furGravity = new Vector3(0, 0, 0);
         public furSpeed: number = 100;
+        public furDensity: number = 20;
         public furTexture: DynamicTexture;
         
         public disableLighting = false;
-        public highLevelFur: boolean = false;
+        public highLevelFur: boolean = true;
 
         private _worldViewProjectionMatrix = Matrix.Zero();
         private _scaledDiffuse = new Color3(1.,1.,1.);
@@ -92,6 +93,14 @@ module BABYLON {
 
             this._cachedDefines.BonesPerMesh = -1;
         }
+        
+        public get furTime() {
+            return this._furTime;
+        }
+        
+        public set furTime(furTime: number) {
+            this._furTime = furTime;
+        }
 
         public needAlphaBlending(): boolean {
             return (this.alpha < 1.0);
@@ -383,7 +392,7 @@ module BABYLON {
                         "mBones",
                         "vClipPlane", "diffuseMatrix",
                         "shadowsInfo0", "shadowsInfo1", "shadowsInfo2", "shadowsInfo3",
-                        "furLength", "furAngle", "furColor", "furOffset", "furGravity", "furTime", "furSpacing"
+                        "furLength", "furAngle", "furColor", "furOffset", "furGravity", "furTime", "furSpacing", "furDensity"
                     ],
                     ["diffuseSampler",
                         "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3",
@@ -520,6 +529,7 @@ module BABYLON {
                 this._effect.setVector3("furGravity", this.furGravity);
                 this._effect.setFloat("furOffset", this.furOffset);
                 this._effect.setFloat("furSpacing", this.furSpacing);
+                this._effect.setFloat("furDensity", this.furDensity);
                 
                 this._furTime += this.getScene().getEngine().getDeltaTime() / this.furSpeed;
                 this._effect.setFloat("furTime", this._furTime);
@@ -577,10 +587,16 @@ module BABYLON {
             serializationObject.customType      = "BABYLON.FurMaterial";
             serializationObject.diffuseColor    = this.diffuseColor.asArray();
             serializationObject.disableLighting = this.disableLighting;
+            
             serializationObject.furLength = this.furLength;
             serializationObject.furAngle = this.furAngle;
             serializationObject.furColor = this.furColor.asArray();
             
+            serializationObject.furGravity = this.furGravity.asArray();
+            serializationObject.furSpacing = this.furSpacing;
+            serializationObject.furSpeed = this.furSpeed;
+            serializationObject.furDensity = this.furDensity;
+            
             if (this.diffuseTexture) {
                 serializationObject.diffuseTexture = this.diffuseTexture.serialize();
             }
@@ -599,11 +615,15 @@ module BABYLON {
             material.furLength          = source.furLength;
             material.furAngle           = source.furAngle;
             material.furColor           = Color3.FromArray(source.furColor);
+            material.furGravity         = Vector3.FromArray(source.furGravity);
+            material.furSpacing         = source.furSpacing;
+            material.furSpeed           = source.furSpeed;
+            material.furDensity         = source.furDensity;
             material.disableLighting    = source.disableLighting;
 
-            material.alpha          = source.alpha;
+            material.alpha              = source.alpha;
 
-            material.id             = source.id;
+            material.id                 = source.id;
 
             Tags.AddTagsTo(material, source.tags);
             material.backFaceCulling = source.backFaceCulling;
@@ -640,6 +660,41 @@ module BABYLON {
             
             return texture;
         }
+        
+        // Creates and returns an array of meshes used as shells for the Fur Material
+        // that can be disposed later in your code
+        // The quality is in interval [0, 100]
+        public static FurifyMesh(sourceMesh: Mesh, quality: number): Mesh[] {
+            var meshes = [sourceMesh];
+            var mat: FurMaterial = <FurMaterial>sourceMesh.material;
+            
+            if (!(mat instanceof FurMaterial)) {
+                throw "The material of the source mesh must be a Fur Material";
+            }
+            
+            for (var i = 1; i < quality; i++) {
+                var offsetFur = new BABYLON.FurMaterial(mat.name + i, sourceMesh.getScene());
+                offsetFur.furLength = mat.furLength;
+                offsetFur.furAngle = mat.furAngle;
+                offsetFur.furGravity = mat.furGravity;
+                offsetFur.furSpacing = mat.furSpacing;
+                offsetFur.furSpeed = mat.furSpeed;
+                offsetFur.furColor = mat.furColor;
+                offsetFur.diffuseTexture = mat.diffuseTexture;
+                offsetFur.furOffset = i / quality;
+                offsetFur.furTexture = mat.furTexture;
+                offsetFur.highLevelFur = mat.highLevelFur;
+                offsetFur.furTime = mat.furTime;
+                offsetFur.furDensity = mat.furDensity;
+                
+                var offsetMesh = sourceMesh.clone(sourceMesh.name + i);
+                offsetMesh.material = offsetFur;
+                offsetMesh.skeleton = sourceMesh.skeleton;
+                meshes.push(offsetMesh);
+            }
+            
+            return meshes;
+        }
     }
 } 
 

+ 3 - 10
materialsLibrary/materials/fur/fur.fragment.fx

@@ -392,11 +392,8 @@ void main(void) {
 	float alpha = vDiffuseColor.a;
 
 #ifdef DIFFUSE
-	#ifdef HIGHLEVEL
-	baseColor = vec4(0.0, 0.0, 0.0, 0.0);
-	#else
-	baseColor = texture2D(diffuseSampler, vDiffuseUV);
-	#endif
+	baseColor *= texture2D(diffuseSampler, vDiffuseUV);
+    
 #ifdef ALPHATEST
 	if (baseColor.a < 0.4)
 		discard;
@@ -420,15 +417,11 @@ void main(void) {
 	// Fur
 	vec4 furTextureColor = texture2D(furTexture, vec2(vFurUV.x, vFurUV.y));
 	
-	#ifdef DIFFUSE
-	baseColor = texture2D(diffuseSampler, vec2(vFurUV.x * 0.2, vFurUV.y * 0.2)) * 2.0;
-	#endif
-	
 	if (furTextureColor.a <= 0.0 || furTextureColor.g < furOffset) {
 		discard;
 	}
 	
-	baseColor = vec4(furColor.xyz * baseColor.xyz, 1.1 - furOffset);
+	baseColor = vec4(baseColor.xyz, 1.0 - furOffset);
 	#endif
 
 	// Lighting

+ 11 - 6
materialsLibrary/materials/fur/fur.vertex.fx

@@ -27,6 +27,7 @@ uniform float furOffset;
 uniform vec3 furGravity;
 uniform float furTime;
 uniform float furSpacing;
+uniform float furDensity;
 #endif
 #ifdef HEIGHTMAP
 uniform sampler2D heightTexture;
@@ -146,7 +147,7 @@ float r = Rand(position);
 	tangent1 = normalize(tangent1);
 	
     vec3 newPosition = position + normal * vfur_length*cos(furAngle) + tangent1 * vfur_length * sin(furAngle);
-	
+    
 	#ifdef HIGHLEVEL
 	// Compute fur data passed to the pixel shader
 	vec3 forceDirection = vec3(0.0, 0.0, 0.0);
@@ -167,7 +168,7 @@ float r = Rand(position);
 	
 	#ifdef NORMAL
 	#ifdef HIGHLEVEL
-	vNormalW = normalize(normal * aNormal);
+	vNormalW = normalize(vec3(finalWorld * vec4(normal, 0.0)) * aNormal);
 	#else
 	vNormalW = normalize(vec3(finalWorld * vec4(normal, 0.0)));
 	#endif
@@ -187,10 +188,6 @@ float r = Rand(position);
 	vec2 uv2 = vec2(0., 0.);
 #endif
 
-	#ifdef HIGHLEVEL
-	vFurUV = uv * 20.0;
-	#endif
-
 #ifdef DIFFUSE
 	if (vDiffuseInfos.x == 0.)
 	{
@@ -200,6 +197,14 @@ float r = Rand(position);
 	{
 		vDiffuseUV = vec2(diffuseMatrix * vec4(uv2, 1.0, 0.0));
 	}
+    
+    #ifdef HIGHLEVEL
+	vFurUV = vDiffuseUV * furDensity;
+	#endif
+#else
+    #ifdef HIGHLEVEL
+	vFurUV = uv * furDensity;
+	#endif
 #endif
 
 	// Clip plane

+ 26 - 22
materialsLibrary/materials/fur/readme.md

@@ -53,49 +53,53 @@ furMaterial.diffuseTexture = new BABYLON.Texture("leopard_fur.jpg", scene); // S
 Fur materials have always been subjects of a lot of theories and conferences with multiple implementations thanks to multiple technologies.
 Here, with WebGL, we decided to choose one of these implementations, not hard to use and pretty smart (with performances) with simple models
 
-First, activate the high level:
+First, activate the high level (activated by default):
 ```
 furMaterial.highLevelFur = true;
 ```
 
-That's all. Now, the most difficult part should be to configure the shells and create the fur effect.
-Indeed, you'll have to draw several times the same mesh with an offset (computed in the effect) to create the illusion of fur:
+That's all. Now, the most difficult part should be to configure the shells and the fur texture to create the fur effect.
+Indeed, you'll have to draw several times the same mesh with an offset (computed in the effect) to create the illusion of fur.
+Hopefully, there is a function that creates and returns the shells:
 
 ```
-furMaterial.furOffset = 0;
+// Generate a fur texture (internally used), working like a noise texture, that will be shared between all the shells
+var furTexture = BABYLON.FurMaterial.GenerateTexture("furTexture", scene);
+
+furMaterial.furTexture = furTexture;
 myMesh.material = furMaterial;
 
-var shells = 30; // Works as the quality
+var quality = 30; // Average quality
 
-// Generate a fur texture (internally used), working like a noise texture, that will be shared between all the shells
-var furTexture = BABYLON.FurMaterial.GenerateTexture("furTexture", scene);
+// Create shells
+var shells = BABYLON.FurMaterial.FurifyMesh(myMesh, quality);
+```
 
-for (var i = 1; i < shells; i++) {
-	var offsetFur = new BABYLON.FurMaterial("fur" + i, scene);
-	offsetFur.diffuseTexture = myDiffuseTexture;
-	
-	offsetFur.furOffset = i / shells; // Create the offset
-	offsetFur.furTexture = furTexture; // Assign the fur texture (previously generated)
-	offsetFur.highLevelFur = fur.highLevelFur; // Set as high level
-	
-	var offsetMesh = myMesh.clone(mesh.name + i); // Clone the mesh. For more performances, you can use LOD system of cloned meshes
-	offsetMesh.material = offsetFur; // Assign the material with appropriate offset
-	offsetMesh.skeleton = mesh.skeleton; // If the mesh is animated, keep the skeleton reference
+It is now working!
+The function "BABYLON.FurMaterial.FurifyMesh" returns an array of "BABYLON.Mesh" that you can dispose later.
+The first element is the mesh you used as the source mesh (myMesh here):
+```
+for (var i=0; i < shells.length; i++) {
+    shells[i].material.dispose();
+    shells[i].dispose();
 }
 ```
 
-It is now working! You can customize the high level fur rendering thanks to some properties:
+You can customize the high level fur rendering thanks to some properties:
+```
+allFurMaterials.furSpacing = 2; // Computes the space between shells. In others words, works as the fur height
+```
+
 ```
-allFurMaterials.furSpacing = 2; // Computes the space between shells. In others words, works as the fur density 
+allFurMaterials.furDensity = 20; // Computes the fur density. More the density is high, more you'll have to zoom on the model
 ```
 
 ```
-allFurMaterials.furSpeed = 1; // Divides the animation of fur in time according to the gravity
+allFurMaterials.furSpeed = 100; // Divides the animation of fur in time according to the gravity
 ```
 
 ```
 // Compute the gravity followed by the fur
-// In range [-1, 1] for X, Y and Z
 allFurMaterials.furGravity = new BABYLON.Vector3(0, -1, 0);
 ```
 

+ 27 - 46
materialsLibrary/test/add/addfur.js

@@ -1,36 +1,31 @@
 window.prepareFur = function() {
 	var shells = 30;
-	
-	var materials = [];
 	var meshes = [];
 	
 	var diffuseTexture = new BABYLON.Texture("textures/leopard_fur.jpg", scene);
 	var heightTexture = new BABYLON.Texture("textures/speckles.jpg", scene);
 	var furTexture = BABYLON.FurMaterial.GenerateTexture("furTexture", scene);
 	
-	var fur = new BABYLON.FurMaterial("fur0", scene);
+	var fur = new BABYLON.FurMaterial("fur", scene);
 	fur.furLength = 4;
 	fur.furAngle = 0;
-	fur.furColor = new BABYLON.Color3(0.2, 0.2, 0.2);
+	fur.furColor = new BABYLON.Color3(1.0, 1.0, 1.0);
 	fur.diffuseTexture = diffuseTexture;
-	fur.furOffset = 0;
-	fur.furTexture = furTexture;
+    fur.furTexture = furTexture;
 	
+    // Sets the same value to all shells
 	var setValue = function(property, value) {
-		for (var i=0; i < materials.length; i++) {
-			materials[i][property] = value;
+		for (var i=0; i < meshes.length; i++) {
+			meshes[i].material[property] = value;
 		}
 	}
 	
 	var resetFur = function() {
-		for (var i=1; i < materials.length; i++) {
-			materials[i].dispose();
-		}
 		for (var i=1; i < meshes.length; i++) {
+            meshes[i].material.dispose();
 			meshes[i].dispose();
 		}
-		
-		materials = [];
+        
 		meshes = [];
 	};
 	
@@ -40,30 +35,11 @@ window.prepareFur = function() {
 		}
 	}
 	
-	var configureFur = function(mesh, apply) {
-		meshes = [mesh];
-		materials = [fur];
-		
-		mesh.material = fur;
-		
-		for (var i = 1; i < shells; i++) {
-			var offsetFur = new BABYLON.FurMaterial("fur" + i, scene);
-			offsetFur.furLength = 4;
-			offsetFur.furAngle = 0;
-			offsetFur.furColor = new BABYLON.Color3(0.2, 0.2, 0.2);
-			offsetFur.diffuseTexture = diffuseTexture;
-			offsetFur.furOffset = i / shells;
-			offsetFur.furTexture = furTexture;
-			offsetFur.highLevelFur = fur.highLevelFur;
-			materials.push(offsetFur);
-			
-			var offsetMesh = mesh.clone(mesh.name + i);
-			offsetMesh.isVisible = fur.highLevelFur && apply;
-			offsetMesh.material = offsetFur;
-			offsetMesh.skeleton = mesh.skeleton;
-			meshes.push(offsetMesh);
-		}
-		
+	var configureFur = function(mesh) {
+        mesh.material = fur;
+        meshes = BABYLON.FurMaterial.FurifyMesh(mesh, shells);
+        
+        // For animated meshes
 		for (var i=0; i < scene.skeletons.length; i++) {
 			scene.beginAnimation(scene.skeletons[i], 0, 100, true, 0.8);
 		}
@@ -104,27 +80,36 @@ window.prepareFur = function() {
 		setValue("heightTexture", HTON ? heightTexture : null);
 	});
 	
+    // If High level fur
 	registerRangeUI("fur", "Hight Level fur", false, true, function(value) {
 		setValue("highLevelFur", value);
 		setMeshesVisible(value);
     }, function() {
         return fur.highLevelFur;
     });
+    
+    // Fur density
+    registerRangeUI("fur", "Fur Density", 1, 50, function(value) {
+       setValue("furDensity", value); 
+    }, function() {
+        return fur.furDensity;
+    });
 	
+    // Fur Gravity
 	registerRangeUI("fur", "Fur Gravity", 0, 1, function(value) {
-		setValue("furGravity", new BABYLON.Vector3(value, value,value));
+		setValue("furGravity", new BABYLON.Vector3(value, value, value));
     }, function() {
         return fur.furGravity.x;
     });
 	
-	// fur animation speed
+	// Fur animation speed
     registerRangeUI("fur", "Fur speed", 1, 1000, function(value) {
 		setValue("furSpeed", value);
     }, function() {
         return fur.furSpeed;
     });
 	
-	// fur animation speed
+	// Fur spacing
     registerRangeUI("fur", "Fur Spacing", 0, 20, function(value) {
 		setValue("furSpacing", value);
     }, function() {
@@ -132,16 +117,12 @@ window.prepareFur = function() {
     });
     
     return {
-		/*
-		materials: furs,
-		meshes: meshes
-		*/
 		material: fur,
 		resetFur: function() {
 			resetFur();
 		},
-		configureFur: function(mesh, apply) {
-			configureFur(mesh, apply);
+		configureFur: function(mesh) {
+			configureFur(mesh);
 		}
 	};
 };

+ 3 - 2
materialsLibrary/test/index.html

@@ -242,7 +242,7 @@
 							break;
 						case "fur":
 							currentMaterial = fur.material;
-							fur.configureFur(currentMesh, true);
+							fur.configureFur(currentMesh);
 							break;
 						case "triPlanar":
 							currentMaterial = triPlanar;
@@ -293,8 +293,9 @@
 					water.mesh = currentMesh;
 					
 					if (currentMaterial === fur.material) {
+                        // Furify the mesh
 						fur.resetFur();
-						fur.configureFur(currentMesh, currentMaterial === fur.material);
+						fur.configureFur(currentMesh);
 					}
 				});