Kaynağa Gözat

Merge pull request #920 from julien-moreau/master

Materials Library: improved fur to an optional high level
David Catuhe 9 yıl önce
ebeveyn
işleme
fde4c8c61b

Dosya farkı çok büyük olduğundan ihmal edildi
+ 36 - 4
materialsLibrary/dist/babylon.furMaterial.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
materialsLibrary/dist/babylon.furMaterial.min.js


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

@@ -7,10 +7,17 @@ declare module BABYLON {
         furLength: number;
         furAngle: number;
         furColor: Color3;
+        furOffset: number;
+        furSpacing: number;
+        furGravity: Vector3;
+        furSpeed: number;
+        furTexture: DynamicTexture;
         disableLighting: boolean;
+        highLevelFur: boolean;
         private _worldViewProjectionMatrix;
         private _scaledDiffuse;
         private _renderId;
+        private _furTime;
         private _defines;
         private _cachedDefines;
         constructor(name: string, scene: Scene);
@@ -26,5 +33,6 @@ declare module BABYLON {
         clone(name: string): FurMaterial;
         serialize(): any;
         static Parse(source: any, scene: Scene, rootUrl: string): FurMaterial;
+        static GenerateTexture(name: string, scene: Scene): DynamicTexture;
     }
 }

+ 47 - 3
materialsLibrary/materials/fur/babylon.furMaterial.ts

@@ -52,6 +52,7 @@ module BABYLON {
         public BONES4 = false;
         public BonesPerMesh = 0;
         public INSTANCES = false;
+        public HIGHLEVEL = false;
 
         constructor() {
             super();
@@ -63,14 +64,25 @@ module BABYLON {
         public diffuseTexture: BaseTexture;
         public heightTexture: BaseTexture;
         public diffuseColor = new Color3(1, 1, 1);
+        
         public furLength: number = 1;
         public furAngle: number = 0;
         public furColor = new Color3(0.44,0.21,0.02);
+        
+        public furOffset: number = 0.9;
+        public furSpacing: number = 12;
+        public furGravity = new Vector3(0, 0, 0);
+        public furSpeed: number = 100;
+        public furTexture: DynamicTexture;
+        
         public disableLighting = false;
+        public highLevelFur: boolean = false;
 
         private _worldViewProjectionMatrix = Matrix.Zero();
         private _scaledDiffuse = new Color3(1.,1.,1.);
         private _renderId: number;
+        
+        private _furTime: number = 0;
 
         private _defines = new FurMaterialDefines();
         private _cachedDefines = new FurMaterialDefines();
@@ -171,6 +183,11 @@ module BABYLON {
             if (scene.fogEnabled && mesh && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE && this.fogEnabled) {
                 this._defines.FOG = true;
             }
+            
+            // High level
+            if (this.highLevelFur) {
+                this._defines.HIGHLEVEL = true;
+            }
 
             var lightIndex = 0;
             if (scene.lightsEnabled && !this.disableLighting) {
@@ -366,11 +383,11 @@ module BABYLON {
                         "mBones",
                         "vClipPlane", "diffuseMatrix",
                         "shadowsInfo0", "shadowsInfo1", "shadowsInfo2", "shadowsInfo3",
-                        "furLength", "furAngle", "furColor"
+                        "furLength", "furAngle", "furColor", "furOffset", "furGravity", "furTime", "furSpacing"
                     ],
                     ["diffuseSampler",
                         "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3",
-                        "heightTexture"
+                        "heightTexture", "furTexture"
                     ],
                     join, fallbacks, this.onCompiled, this.onError);
             }
@@ -498,7 +515,17 @@ module BABYLON {
             this._effect.setFloat("furLength", this.furLength);
             this._effect.setFloat("furAngle", this.furAngle);
             this._effect.setColor4("furColor", this.furColor, 1.0);
-
+            
+            if (this.highLevelFur) {
+                this._effect.setVector3("furGravity", this.furGravity);
+                this._effect.setFloat("furOffset", this.furOffset);
+                this._effect.setFloat("furSpacing", this.furSpacing);
+                
+                this._furTime += this.getScene().getEngine().getDeltaTime() / this.furSpeed;
+                this._effect.setFloat("furTime", this._furTime);
+                
+                this._effect.setTexture("furTexture", this.furTexture);
+            }
  
             super.bind(world, mesh);
         }
@@ -596,6 +623,23 @@ module BABYLON {
 
             return material;
         }
+        
+        public static GenerateTexture(name: string, scene: Scene): DynamicTexture {
+            // Generate fur textures
+            var texture = new DynamicTexture("FurTexture " + name, 256, scene, true);
+            var context = texture.getContext();
+            
+            for ( var i = 0; i < 20000; ++i ) {
+                context.fillStyle = "rgba(255, " + Math.floor(Math.random() * 255) + ", " + Math.floor(Math.random() * 255) + ", 1)";
+                context.fillRect((Math.random() * texture.getSize().width), (Math.random() * texture.getSize().height), 2, 2);
+            }
+            
+            texture.update(false);
+            texture.wrapU = Texture.WRAP_ADDRESSMODE;
+            texture.wrapV = Texture.WRAP_ADDRESSMODE;
+            
+            return texture;
+        }
     }
 } 
 

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

@@ -105,6 +105,14 @@ uniform sampler2D diffuseSampler;
 uniform vec2 vDiffuseInfos;
 #endif
 
+// Fur uniforms
+#ifdef HIGHLEVEL
+uniform float furOffset;
+uniform sampler2D furTexture;
+
+varying vec2 vFurUV;
+#endif
+
 // Shadows
 #ifdef SHADOWS
 
@@ -384,8 +392,11 @@ 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
 #ifdef ALPHATEST
 	if (baseColor.a < 0.4)
 		discard;
@@ -405,6 +416,21 @@ void main(void) {
 	vec3 normalW = vec3(1.0, 1.0, 1.0);
 #endif
 
+	#ifdef HIGHLEVEL
+	// 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);
+	#endif
+
 	// Lighting
 	vec3 diffuseBase = vec3(0., 0., 0.);
 	float shadow = 1.;
@@ -552,10 +578,13 @@ void main(void) {
 	vec3 finalDiffuse = clamp(diffuseBase * diffuseColor, 0.0, 1.0) * baseColor.rgb;
 
 	// Composition
-	//float r = Rand(vPositionW) * 0.5;
+	#ifdef HIGHLEVEL
+	vec4 color = vec4(finalDiffuse, alpha);
+	#else
 	float r = vfur_length * 0.5;
 	vec4 color = vec4(finalDiffuse * (0.5 + r), alpha);
-
+	#endif
+	
 #ifdef FOG
 	float fog = CalcFogFactor();
 	color.rgb = fog * color.rgb + (1.0 - fog) * vFogColor;

+ 47 - 9
materialsLibrary/materials/fur/fur.vertex.fx

@@ -22,10 +22,20 @@ attribute vec4 matricesWeights;
 // Uniforms
 uniform float furLength;
 uniform float furAngle;
+#ifdef HIGHLEVEL
+uniform float furOffset;
+uniform vec3 furGravity;
+uniform float furTime;
+uniform float furSpacing;
+#endif
 #ifdef HEIGHTMAP
 uniform sampler2D heightTexture;
 #endif
 
+#ifdef HIGHLEVEL
+varying vec2 vFurUV;
+#endif
+
 #ifdef INSTANCES
 attribute vec4 world0;
 attribute vec4 world1;
@@ -118,6 +128,7 @@ void main(void) {
 #endif 
 
 #endif
+
 //FUR
 float r = Rand(position);
 #ifdef HEIGHTMAP	
@@ -127,13 +138,40 @@ float r = Rand(position);
 #endif
 	vec3 tangent1 = vec3(normal.y, -normal.x, 0);
 	vec3 tangent2 = vec3(-normal.z, 0, normal.x);
-	r = Rand(tangent1*r);
-	float J = (2.0 + 4.0* r);
+	r = Rand(tangent1 * r);
+	float J = (2.0 + 4.0 * r);
 	r = Rand(tangent2*r);
-	float K = (2.0 + 2.0* r);
-	tangent1 = tangent1*J + tangent2*K;
+	float K = (2.0 + 2.0 * r);
+	tangent1 = tangent1*J + tangent2 * K;
 	tangent1 = normalize(tangent1);
-    vec3 newPosition = position + normal * vfur_length*cos(furAngle) + tangent1*vfur_length*sin(furAngle);
+	
+    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);
+	forceDirection.x = sin(furTime + position.x * 0.05) * 0.2;
+	forceDirection.y = cos(furTime * 0.7 + position.y * 0.04) * 0.2;
+	forceDirection.z = sin(furTime * 0.7 + position.z * 0.04) * 0.2;
+	
+	vec3 displacement = vec3(0.0, 0.0, 0.0);
+	displacement = furGravity + forceDirection;
+	
+	float displacementFactor = pow(furOffset, 3.0);
+	
+	vec3 aNormal = normal;
+	aNormal.xyz += displacement * displacementFactor;
+	
+	newPosition = vec3(newPosition.x, newPosition.y, newPosition.z) + (normalize(aNormal) * furOffset * furSpacing);
+	#endif
+	
+	#ifdef NORMAL
+	#ifdef HIGHLEVEL
+	vNormalW = normalize(normal * aNormal);
+	#else
+	vNormalW = normalize(vec3(finalWorld * vec4(normal, 0.0)));
+	#endif
+	#endif
 	
 //END FUR
 	gl_Position = viewProjection * finalWorld * vec4(newPosition, 1.0);
@@ -141,10 +179,6 @@ float r = Rand(position);
 	vec4 worldPos = finalWorld * vec4(newPosition, 1.0);
 	vPositionW = vec3(worldPos);
 
-#ifdef NORMAL
-	vNormalW = normalize(vec3(finalWorld * vec4(normal, 0.0)));
-#endif
-
 	// Texture coordinates
 #ifndef UV1
 	vec2 uv = vec2(0., 0.);
@@ -153,6 +187,10 @@ float r = Rand(position);
 	vec2 uv2 = vec2(0., 0.);
 #endif
 
+	#ifdef HIGHLEVEL
+	vFurUV = uv * 20.0;
+	#endif
+
 #ifdef DIFFUSE
 	if (vDiffuseInfos.x == 0.)
 	{

+ 57 - 8
materialsLibrary/materials/fur/readme.md

@@ -1,6 +1,6 @@
 # Fur material
 
-## Using the fur material
+# Using the fur material
 
 The fur material needs a high number of the triangular facets that make up a mesh to work well.
 The number of facets needed also depends on the size of the mesh.
@@ -15,12 +15,11 @@ The fur material is created using
 
 ```
 var furMaterial = new BABYLON.furMaterial("fur_material", scene);
-waterMaterial.bumpTexture = new BABYLON.Texture("bump.png", scene); // Set the bump texture
 
-ground.material = waterMaterial;
+ground.material = furMaterial;
 ```
 
-## Customize the fur material
+# Customize the fur material
 
 You can customise three properties of the fur material:
 
@@ -30,9 +29,9 @@ furMaterial.furAngle = Math.PI/6; // Represents the angle the fur lies on the me
 furMaterial.furColor = new BABYLON.Color3(0.44, 0.21, 0.02); // is the default color if furColor is not set.
 ```
 
-## Using textures
+# Using textures
 
-#heightTexture
+##heightTexture
 
 A greyscale image can be used to set the fur length. 
 A speckled greyscale image can produce fur like results.
@@ -41,7 +40,7 @@ Any greyscale image with affect the fur length producing a heightMap type effect
 ```
 furMaterial.heightTexture = new BABYLON.Texture("speckles.jpg", scene); // Set the fur length with a texture.
 ```
-#diffuseTexture
+##diffuseTexture
 A texture can also be used to paint the mesh. 
 The leopard fur texture used in the test is by Martin Wegmann from [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Leopard_fur.JPG)
 under the [license](https://creativecommons.org/licenses/by-sa/3.0/deed.en)
@@ -50,7 +49,57 @@ under the [license](https://creativecommons.org/licenses/by-sa/3.0/deed.en)
 furMaterial.diffuseTexture = new BABYLON.Texture("leopard_fur.jpg", scene); // Set the fur length with a texture.
 ```
 
-## Meshes where the number of facets is not user controlled on creation.
+# Using the High Level mode
+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:
+```
+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:
+
+```
+furMaterial.furOffset = 0;
+myMesh.material = furMaterial;
+
+var shells = 30; // Works as the 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);
+
+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! 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 density 
+```
+
+```
+allFurMaterials.furSpeed = 1; // 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);
+```
+
+# Meshes where the number of facets is not user controlled on creation.
 
 Unlike the ground mesh where you can supply the number of subdivisions or the sphere mesh where you can supply the number of segments the majority of meshes are created using a minimum number of facets.
 To apply the fur material to these the number of facets per face of the mesh needs to be increased.

+ 2 - 2
materialsLibrary/materials/sky/babylon.skyMaterial.ts

@@ -13,7 +13,7 @@ module BABYLON {
             this._keys = Object.keys(this);
         }
     }
-
+    
     export class SkyMaterial extends Material {
         // Public members
         public luminance: number = 1.0;
@@ -120,7 +120,7 @@ module BABYLON {
                 if (this._defines.FOG) {
                     fallbacks.addFallback(1, "FOG");
                 }
-
+                
                 //Attributes
                 var attribs = [VertexBuffer.PositionKind];
 

+ 116 - 23
materialsLibrary/test/add/addfur.js

@@ -1,20 +1,84 @@
 window.prepareFur = function() {
-	var fur = new BABYLON.FurMaterial("fur", scene);
-	fur.furLength = 1;
+	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);
+	fur.furLength = 4;
 	fur.furAngle = 0;
-    fur.furColor = new BABYLON.Color3(0.44,0.21,0.02);
-
+	fur.furColor = new BABYLON.Color3(0.2, 0.2, 0.2);
+	fur.diffuseTexture = diffuseTexture;
+	fur.furOffset = 0;
+	fur.furTexture = furTexture;
+	
+	var setValue = function(property, value) {
+		for (var i=0; i < materials.length; i++) {
+			materials[i][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].dispose();
+		}
+		
+		materials = [];
+		meshes = [];
+	};
+	
+	var setMeshesVisible = function(visible) {
+		for (var i=1; i < meshes.length; i++) {
+			meshes[i].isVisible = visible;
+		}
+	}
+	
+	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);
+		}
+		
+		for (var i=0; i < scene.skeletons.length; i++) {
+			scene.beginAnimation(scene.skeletons[i], 0, 100, true, 0.8);
+		}
+	}
 
-// fur length
-    registerRangeUI("fur", "Fur length", 0, 15, function(value) {
-        fur.furLength = value;
+	// fur length
+    registerRangeUI("fur", "Fur length", 0, 45, function(value) {
+		setValue("furLength", value);
     }, function() {
         return fur.furLength;
     });
 	
 	// fur angle
     registerRangeUI("fur", "Fur angle", 0, Math.PI/2, function(value) {
-        fur.furAngle = value;
+		setValue("furAngle", value);
     }, function() {
         return fur.furAngle;
     });
@@ -31,24 +95,53 @@ window.prepareFur = function() {
 	var DTON = false;
 	registerButtonUI("fur", "Tgl Diffuse Tex", function() {
 		DTON = !DTON;
-		if(DTON) {
-			fur.diffuseTexture = new BABYLON.Texture("textures/leopard_fur.jpg", scene);
-		}
-		else {
-			fur.diffuseTexture = null;
-		}
-	})
+		setValue("diffuseTexture", DTON ? diffuseTexture : null);
+	});
 	
 	var HTON = false;
 	registerButtonUI("fur", "Tgl Height Tex", function() {
 		HTON = !HTON;
-		if(HTON) {
-			fur.heightTexture = new BABYLON.Texture("textures/speckles.jpg", scene);
-		}
-		else {
-			fur.heightTexture = null;
-		}
-	})
+		setValue("heightTexture", HTON ? heightTexture : null);
+	});
+	
+	registerRangeUI("fur", "Hight Level fur", false, true, function(value) {
+		setValue("highLevelFur", value);
+		setMeshesVisible(value);
+    }, function() {
+        return fur.highLevelFur;
+    });
+	
+	registerRangeUI("fur", "Fur Gravity", 0, 1, function(value) {
+		setValue("furGravity", new BABYLON.Vector3(value, value,value));
+    }, function() {
+        return fur.furGravity.x;
+    });
+	
+	// fur animation speed
+    registerRangeUI("fur", "Fur speed", 1, 1000, function(value) {
+		setValue("furSpeed", value);
+    }, function() {
+        return fur.furSpeed;
+    });
+	
+	// fur animation speed
+    registerRangeUI("fur", "Fur Spacing", 0, 20, function(value) {
+		setValue("furSpacing", value);
+    }, function() {
+        return fur.furSpacing;
+    });
     
-    return fur;
+    return {
+		/*
+		materials: furs,
+		meshes: meshes
+		*/
+		material: fur,
+		resetFur: function() {
+			resetFur();
+		},
+		configureFur: function(mesh, apply) {
+			configureFur(mesh, apply);
+		}
+	};
 };

+ 10 - 2
materialsLibrary/test/index.html

@@ -186,7 +186,7 @@
 				var normal = prepareNormal();
 
 				var gradient = prepareGradient();
-				
+								
 				var fur = prepareFur();
 				
 				var water = prepareWater();
@@ -213,6 +213,8 @@
 				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'fur', 'triPlanar', 'gradient', 'sky']).onFinishChange(function () {
 					water.enableRenderTargets(false);
 					skybox.material = skyboxMaterial;
+					currentMesh.isVisible = true;
+					fur.resetFur();
 					
 					switch (options.material) {
 						case "simple":
@@ -239,7 +241,8 @@
 							currentMaterial = pbr;
 							break;
 						case "fur":
-							currentMaterial = fur;
+							currentMaterial = fur.material;
+							fur.configureFur(currentMesh, true);
 							break;
 						case "triPlanar":
 							currentMaterial = triPlanar;
@@ -288,6 +291,11 @@
 					currentMesh.material = currentMaterial;
 					
 					water.mesh = currentMesh;
+					
+					if (currentMaterial === fur.material) {
+						fur.resetFur();
+						fur.configureFur(currentMesh, currentMaterial === fur.material);
+					}
 				});
 
 				var f1 = gui.addFolder('lights');