ソースを参照

Added Parallax Mapping and Parallax Occlusion Mapping feature which lies on the top of the Bump feature.
The Heightmap needed for Parallax Mapping must be encoded in the Alpha channel of the Bump's NormalMap.

nockawa 9 年 前
コミット
cbcf1306a1

ファイルの差分が大きいため隠しています
+ 860 - 830
src/Materials/babylon.standardMaterial.js


+ 31 - 1
src/Materials/babylon.standardMaterial.ts

@@ -8,6 +8,8 @@
         public EMISSIVE = false;
         public SPECULAR = false;
         public BUMP = false;
+        public PARALLAX = false;
+        public PARALLAXOCCLUSION = false;
         public SPECULAROVERALPHA = false;
         public CLIPPLANE = false;
         public ALPHATEST = false;
@@ -155,6 +157,15 @@
         public disableLighting = false;
 
         @serialize()
+        public useParallax = false;
+
+        @serialize()
+        public useParallaxOcclusion = false;
+
+        @serialize()
+        public parallaxScaleBias = 0.05;
+
+        @serialize()
         public roughness = 0;
 
         @serialize()
@@ -398,6 +409,13 @@
                     } else {
                         needUVs = true;
                         this._defines.BUMP = true;
+
+                        if (this.useParallax) {
+                            this._defines.PARALLAX = true;
+                            if (this.useParallaxOcclusion) {
+                                this._defines.PARALLAXOCCLUSION = true;
+                            }
+                        }
                     }
                 }
 
@@ -544,6 +562,14 @@
                     fallbacks.addFallback(0, "BUMP");
                 }
 
+                if (this._defines.PARALLAX) {
+                    fallbacks.addFallback(1, "PARALLAX");
+                }
+
+                if (this._defines.PARALLAXOCCLUSION) {
+                    fallbacks.addFallback(0, "PARALLAXOCCLUSION");
+                }
+
                 if (this._defines.SPECULAROVERALPHA) {
                     fallbacks.addFallback(0, "SPECULAROVERALPHA");
                 }
@@ -622,7 +648,7 @@
                         "vLightData2", "vLightDiffuse2", "vLightSpecular2", "vLightDirection2", "vLightGround2", "lightMatrix2",
                         "vLightData3", "vLightDiffuse3", "vLightSpecular3", "vLightDirection3", "vLightGround3", "lightMatrix3",
                         "vFogInfos", "vFogColor", "pointSize",
-                        "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos", "vLightmapInfos", "vRefractionInfos",
+                        "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos", "vParallaxScaleBias", "vLightmapInfos", "vRefractionInfos",
                         "mBones",
                         "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix", "lightmapMatrix", "refractionMatrix",
                         "shadowsInfo0", "shadowsInfo1", "shadowsInfo2", "shadowsInfo3", "depthValues",
@@ -768,6 +794,10 @@
 
                         this._effect.setFloat2("vBumpInfos", this.bumpTexture.coordinatesIndex, 1.0 / this.bumpTexture.level);
                         this._effect.setMatrix("bumpMatrix", this.bumpTexture.getTextureMatrix());
+
+                        if (this.useParallax) {
+                            this._effect.setFloat("vParallaxScaleBias", this.parallaxScaleBias);
+                        }
                     }
 
                     if (this.refractionTexture && StandardMaterial.RefractionTextureEnabled) {

+ 102 - 4
src/Shaders/ShadersInclude/bumpFragmentFunctions.fx

@@ -23,11 +23,109 @@
 		return mat3(tangent * invmax, binormal * invmax, normal);
 	}
 
-	vec3 perturbNormal(vec3 viewDir)
+	vec3 perturbNormal(vec3 viewDir, mat3 cotangentFrame, vec2 uv)
 	{
-		vec3 map = texture2D(bumpSampler, vBumpUV).xyz;
+		vec3 map = texture2D(bumpSampler, uv).xyz;
 		map = map * 255. / 127. - 128. / 127.;
-		mat3 TBN = cotangent_frame(vNormalW * vBumpInfos.y, -viewDir, vBumpUV);
-		return normalize(TBN * map);
+		return normalize(cotangentFrame * map);
 	}
+
+#ifdef PARALLAX
+	uniform float vParallaxScaleBias;
+
+	const float minSamples = 4.;
+	const float maxSamples = 15.;
+	const int iMaxSamples = 15;
+
+	// http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/a-closer-look-at-parallax-occlusion-mapping-r3262
+	vec2 parallaxOcclusion(vec3 vViewDirCoT, vec3 vNormalCoT, vec2 texCoord, float parallaxScale) {
+
+		// Calculate the parallax offset vector max length.
+		// This is equivalent to the tangent of the angle between the
+		// viewer position and the fragment location.
+		float parallaxLimit = length(vViewDirCoT.xy) / vViewDirCoT.z;
+
+		// Scale the parallax limit according to heightmap scale.
+		parallaxLimit *= parallaxScale;
+
+		// Calculate the parallax offset vector direction and maximum offset.
+		vec2 vOffsetDir = normalize(vViewDirCoT.xy);
+		vec2 vMaxOffset = vOffsetDir * parallaxLimit;
+
+		// Calculate how many samples should be taken along the view ray
+		// to find the surface intersection.  This is based on the angle
+		// between the surface normal and the view vector.
+		float numSamples = maxSamples + (dot(vViewDirCoT, vNormalCoT) * (minSamples - maxSamples));
+
+		// Specify the view ray step size.  Each sample will shift the current
+		// view ray by this amount.
+		float stepSize = 1.0 / numSamples;
+
+		// Calculate the texture coordinate partial derivatives in screen
+		// space for the tex2Dgrad texture sampling instruction.
+		//vec2 dx = dFdx(vBumpUV);			// Uncomment whevener GL_EXT_shader_texture_lod with texture2DGradEXT will work
+		//vec2 dy = dFdy(vBumpUV);
+
+		// Initialize the starting view ray height and the texture offsets.
+		float currRayHeight = 1.0;
+		vec2 vCurrOffset = vec2(0, 0);
+		vec2 vLastOffset = vec2(0, 0);
+
+		float lastSampledHeight = 1.0;
+		float currSampledHeight = 1.0;
+
+		for (int i = 0; i < iMaxSamples; i++)
+		{
+			// Sample the heightmap at the current texcoord offset.  The heightmap 
+			// is stored in the alpha channel of the height/normal map.
+			currSampledHeight = texture2D(bumpSampler, vBumpUV + vCurrOffset).w;
+
+			// Uncomment whevener GL_EXT_shader_texture_lod with texture2DGradEXT will work
+			//currSampledHeight = texture2DGradEXT(bumpSampler, vBumpUV + vCurrOffset, dx, dy).w;	
+
+			// Test if the view ray has intersected the surface.
+			if (currSampledHeight > currRayHeight)
+			{
+				// Find the relative height delta before and after the intersection.
+				// This provides a measure of how close the intersection is to 
+				// the final sample location.
+				float delta1 = currSampledHeight - currRayHeight;
+				float delta2 = (currRayHeight + stepSize) - lastSampledHeight;
+				float ratio = delta1 / (delta1 + delta2);
+
+				// Interpolate between the final two segments to 
+				// find the true intersection point offset.
+				vCurrOffset = (ratio)* vLastOffset + (1.0 - ratio) * vCurrOffset;
+
+				// Force the exit of the loop
+				break;
+			}
+			else
+			{
+				// take the next view ray height step,
+				currRayHeight -= stepSize;
+
+				// save the current texture coordinate offset and increment
+				// to the next sample location, 
+				vLastOffset = vCurrOffset;
+				vCurrOffset += stepSize * vMaxOffset;
+
+				// and finally save the current heightmap height.
+				lastSampledHeight = currSampledHeight;
+			}
+		}
+
+		return vCurrOffset;
+	}
+
+	vec2 parallaxOffset(vec3 viewDir, float heightScale)
+	{
+		// calculate amount of offset for Parallax Mapping With Offset Limiting
+		float height = texture2D(bumpSampler, vBumpUV).w;
+		vec2 texCoordOffset = heightScale * viewDir.xy * height;
+		return -texCoordOffset;
+	}
+
+#endif
+
 #endif

+ 14 - 0
src/Shaders/ShadersInclude/helperFunctions.fx

@@ -0,0 +1,14 @@
+
+mat3 transposeMat3(mat3 inMatrix) {
+	vec3 i0 = inMatrix[0];
+	vec3 i1 = inMatrix[1];
+	vec3 i2 = inMatrix[2];
+
+	mat3 outMatrix = mat3(
+		vec3(i0.x, i1.x, i2.x),
+		vec3(i0.y, i1.y, i2.y),
+		vec3(i0.z, i1.z, i2.z)
+		);
+
+	return outMatrix;
+}

+ 57 - 13
src/Shaders/default.fragment.fx

@@ -1,4 +1,8 @@
-#ifdef BUMP
+#ifdef PARALLAX
+//#extension GL_EXT_shader_texture_lod : enable		// Doesn't work right now with texture2DGradExt...
+#endif
+
+#ifdef BUMP
 #extension GL_OES_standard_derivatives : enable
 #endif
 
@@ -30,6 +34,9 @@ varying vec3 vNormalW;
 varying vec4 vColor;
 #endif
 
+// Helper functions
+#include<helperFunctions>
+
 // Lights
 #include<light0FragmentDeclaration>
 #include<light1FragmentDeclaration>
@@ -161,6 +168,55 @@ void main(void) {
 	// Alpha
 	float alpha = vDiffuseColor.a;
 
+	// Bump
+#ifdef NORMAL
+	vec3 normalW = normalize(vNormalW);
+#else
+	vec3 normalW = vec3(1.0, 1.0, 1.0);
+#endif
+
+#ifdef DIFFUSE
+	vec2 diffuseUV = vDiffuseUV;
+#endif
+
+#ifdef BUMP
+	vec2 bumpUV = vBumpUV;
+#endif
+
+#if defined(BUMP) || defined(PARALLAX)
+	mat3 TBN = cotangent_frame(vNormalW * vBumpInfos.y, -viewDirectionW, bumpUV);
+#endif
+
+#ifdef PARALLAX
+	mat3 invTBN = transposeMat3(TBN);
+
+#ifdef PARALLAXOCCLUSION
+	vec2 uvOffset = parallaxOcclusion(invTBN * -viewDirectionW, invTBN * normalW, bumpUV, vParallaxScaleBias);
+#else
+	vec2 uvOffset = parallaxOffset(invTBN * viewDirectionW, vParallaxScaleBias);
+#endif
+
+	diffuseUV += uvOffset;
+	bumpUV += uvOffset;
+
+	// Note by Loic:
+	// Parallax mapping apply an offset on the UV, which may leads to out of bound coordinates
+	// If we use texture wrapping we should NOT doing the following test, otherwise this test
+	//  will discard the pixel if we detect an out of bound coordinate.
+	// It makes senses only with occlusion because the fragment is accurately computed.
+	// I'm quite hesitating about keeping this or not, I think future will tell!
+#ifdef PARALLAXOCCLUSION
+	if (diffuseUV.x > 1.0 || diffuseUV.y > 1.0 || diffuseUV.x < 0.0 || diffuseUV.y < 0.0) {
+		discard;
+	}
+#endif
+
+#endif
+
+#ifdef BUMP
+	normalW = perturbNormal(viewDirectionW, TBN, bumpUV);
+#endif
+
 #ifdef DIFFUSE
 	baseColor = texture2D(diffuseSampler, vDiffuseUV);
 
@@ -180,18 +236,6 @@ void main(void) {
 	baseColor.rgb *= vColor.rgb;
 #endif
 
-	// Bump
-#ifdef NORMAL
-	vec3 normalW = normalize(vNormalW);
-#else
-	vec3 normalW = vec3(1.0, 1.0, 1.0);
-#endif
-
-
-#ifdef BUMP
-	normalW = perturbNormal(viewDirectionW);
-#endif
-
 	// Ambient color
 	vec3 baseAmbientColor = vec3(1., 1., 1.);