Răsfoiți Sursa

PBR shader and PBR test case with custom model

Nehon 10 ani în urmă
părinte
comite
81c995edc4

+ 26 - 0
.nb-gradle-properties

@@ -6,4 +6,30 @@
     <template>license.txt</template>
     <property name="organization">jMonkeyEngine</property>
   </license-header>
+  <built-in-tasks>
+    <task>
+      <display-name>run</display-name>
+      <non-blocking>no</non-blocking>
+      <task-names>
+        <name must-exist="no">run</name>
+      </task-names>
+      <task-args/>
+      <task-jvm-args>
+        <arg>-ea</arg>
+      </task-jvm-args>
+    </task>
+    <task>
+      <display-name>run.single</display-name>
+      <non-blocking>no</non-blocking>
+      <task-names>
+        <name must-exist="no">${project}:run</name>
+      </task-names>
+      <task-args>
+        <arg>-PmainClass=${selected-class}</arg>
+      </task-args>
+      <task-jvm-args>
+        <arg>-ea</arg>
+      </task-jvm-args>
+    </task>
+  </built-in-tasks>
 </gradle-project-properties>

+ 1 - 1
gradle.properties

@@ -6,7 +6,7 @@ jmeMainVersion = 3.1
 jmeVersionTag = snapshot-github
 
 # specify if JavaDoc should be built
-buildJavaDoc = true
+buildJavaDoc = false
 
 # specify if SDK and Native libraries get built
 buildSdkProject = true

+ 243 - 0
jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag

@@ -0,0 +1,243 @@
+#import "Common/ShaderLib/Parallax.glsllib"
+#import "Common/ShaderLib/PBR.glsllib"
+#import "Common/ShaderLib/Lighting.glsllib"
+
+varying vec2 texCoord;
+#ifdef SEPARATE_TEXCOORD
+  varying vec2 texCoord2;
+#endif
+
+varying vec4 Color;
+
+uniform vec4 g_LightData[NB_LIGHTS];
+
+uniform vec3 g_CameraPosition;
+
+uniform float m_Roughness;
+uniform float m_Metallic;
+
+varying vec3 wPosition;    
+
+
+#ifdef INDIRECT_LIGHTING
+  uniform sampler2D m_IntegrateBRDF;
+  uniform samplerCube m_PrefEnvMap;
+  uniform samplerCube m_IrradianceMap;
+#endif
+
+#ifdef BASECOLORMAP
+  uniform sampler2D m_BaseColorMap;
+#endif
+#ifdef METALLICMAP
+  uniform sampler2D m_MetallicMap;
+#endif
+#ifdef ROUGHNESSMAP
+  uniform sampler2D m_RoughnessMap;
+#endif
+
+#ifdef EMISSIVE
+    uniform vec4 m_Emissive;
+#endif
+#ifdef EMISSIVEMAP
+    uniform sampler2D m_EmissiveMap;
+#endif
+#if defined(EMISSIVE) || defined(EMISSIVEMAP)
+    uniform float m_EmissivePower;
+    uniform float m_EmissiveIntensity;
+#endif 
+
+#ifdef SPECGLOSSPIPELINE
+  uniform sampler2D m_SpecularMap;
+  uniform sampler2D m_GlossMap;
+#endif
+
+#ifdef PARALLAXMAP
+  uniform sampler2D m_ParallaxMap;  
+#endif
+#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP)))
+    uniform float m_ParallaxHeight;
+#endif
+
+#ifdef LIGHTMAP
+  uniform sampler2D m_LightMap;
+#endif
+  
+#ifdef NORMALMAP
+  uniform sampler2D m_NormalMap;   
+  varying vec3 wTangent;
+  varying vec3 wBinormal;
+#endif
+varying vec3 wNormal;
+
+#ifdef DISCARD_ALPHA
+uniform float m_AlphaDiscardThreshold;
+#endif
+
+void main(){
+    vec2 newTexCoord;
+     
+    #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP)))
+     
+       #ifdef STEEP_PARALLAX
+           #ifdef NORMALMAP_PARALLAX
+               //parallax map is stored in the alpha channel of the normal map         
+               newTexCoord = steepParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
+           #else
+               //parallax map is a texture
+               newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);         
+           #endif
+       #else
+           #ifdef NORMALMAP_PARALLAX
+               //parallax map is stored in the alpha channel of the normal map         
+               newTexCoord = classicParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
+           #else
+               //parallax map is a texture
+               newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);
+           #endif
+       #endif
+    #else
+       newTexCoord = texCoord;    
+    #endif
+    
+    #ifdef BASECOLORMAP
+        vec4 albedo = texture2D(m_BaseColorMap, newTexCoord);
+    #else
+        vec4 albedo = Color;
+    #endif
+    #ifdef ROUGHNESSMAP
+        float Roughness = texture2D(m_RoughnessMap, newTexCoord).r * max(m_Roughness,1e-8);
+    #else
+        float Roughness =  max(m_Roughness,1e-8);
+    #endif
+    #ifdef METALLICMAP   
+        float Metallic = texture2D(m_MetallicMap, newTexCoord).r;
+    #else
+        float Metallic =  max(m_Metallic,0.00);
+    #endif
+
+ //Roughness =  max(m_Roughness,1e-8);
+ //Metallic =  max(m_Metallic,0.00);
+    float alpha = Color.a * albedo.a;
+
+    #ifdef DISCARD_ALPHA
+        if(alpha < m_AlphaDiscardThreshold){
+            discard;
+        }
+    #endif
+ 
+    // ***********************
+    // Read from textures
+    // ***********************
+    #if defined(NORMALMAP)
+      vec4 normalHeight = texture2D(m_NormalMap, newTexCoord);
+      //Note the -2.0 and -1.0. We invert the green channel of the normal map, 
+      //as it's complient with normal maps generated with blender.
+      //see http://hub.jmonkeyengine.org/forum/topic/parallax-mapping-fundamental-bug/#post-256898
+      //for more explanation.
+      vec3 normal = normalize((normalHeight.xyz * vec3(2.0,-2.0,2.0) - vec3(1.0,-1.0,1.0)));
+    #else
+      vec3 normal = normalize(wNormal);            
+    #endif
+
+   
+    #ifdef LIGHTMAP
+       vec3 lightMapColor;
+       #ifdef SEPARATE_TEXCOORD
+          lightMapColor = texture2D(m_LightMap, texCoord2).rgb;
+       #else
+          lightMapColor = texture2D(m_LightMap, texCoord).rgb;
+       #endif
+       specularColor.rgb *= lightMapColor;
+       albedo.rgb  *= lightMapColor;
+    #endif
+
+    int i = 0;
+
+
+    #ifdef NORMALMAP   
+        mat3 tbnMat = mat3(normalize(wTangent.xyz) , normalize(wBinormal.xyz) , normalize(wNormal.xyz));
+        normal = normalize(tbnMat * normal);
+//        normal = normalize(normal * inverse(tbnMat));
+    #endif
+    vec3 viewDir = normalize(g_CameraPosition - wPosition);
+    
+   
+    float specular = 0.5;
+
+    #ifdef SPECGLOSSPIPELINE
+          vec4 specularColor = texture2D(m_SpecularMap, newTexCoord);
+          vec4 diffuseColor = albedo;
+          Roughness = 1.0 - texture2D(m_GlossMap, newTexCoord);          
+    #else      
+        float nonMetalSpec = 0.08 * specular;
+        vec4 specularColor = (nonMetalSpec - nonMetalSpec * Metallic) + albedo * Metallic;
+        vec4 diffuseColor = albedo - albedo * Metallic;
+    #endif
+
+    gl_FragColor.rgb = vec3(0.0);
+    float ndotv = max( dot( normal, viewDir ),0.0);
+    for( int i = 0;i < NB_LIGHTS; i+=3){
+        vec4 lightColor = g_LightData[i];
+        vec4 lightData1 = g_LightData[i+1];                
+        vec4 lightDir;
+        vec3 lightVec;            
+        lightComputeDir(wPosition, lightColor.w, lightData1, lightDir, lightVec);
+
+        float fallOff = 1.0;
+        #if __VERSION__ >= 110
+            // allow use of control flow
+        if(lightColor.w > 1.0){
+        #endif
+            fallOff =  computeSpotFalloff(g_LightData[i+2], lightVec);
+        #if __VERSION__ >= 110
+        }
+        #endif
+        //point light attenuation
+        fallOff *= lightDir.w;
+
+        lightDir.xyz = normalize(lightDir.xyz);            
+        vec3 directDiffuse;
+        vec3 directSpecular;
+        
+        PBR_ComputeDirectLight(normal, lightDir.xyz, viewDir,
+                            lightColor.rgb,specular, Roughness, ndotv,
+                            directDiffuse,  directSpecular);
+
+        vec3 directLighting = diffuseColor.rgb *directDiffuse + directSpecular * specularColor.rgb;
+        
+        gl_FragColor.rgb += directLighting * fallOff;
+    }
+
+    #ifdef INDIRECT_LIGHTING
+        vec3 rv = reflect(-viewDir.xyz, normal.xyz);     
+       
+         //horizon fade from http://marmosetco.tumblr.com/post/81245981087
+        float horiz = dot(rv, wNormal.xyz);
+        float horizFadePower= 1.0 - Roughness;
+        horiz = clamp( 1.0 + horizFadePower * horiz, 0.0, 1.0 );
+        horiz *= horiz;
+        
+        vec3 indirectDiffuse = vec3(0.0);
+        vec3 indirectSpecular = vec3(0.0);    
+        indirectDiffuse = textureCube(m_IrradianceMap, rv.xyz).rgb * albedo.rgb;
+        
+        indirectSpecular = ApproximateSpecularIBL(m_PrefEnvMap,m_IntegrateBRDF, specularColor.rgb, Roughness, ndotv, rv.xyz);
+        indirectSpecular *= vec3(horiz);
+
+        vec3 indirectLighting =  indirectDiffuse + indirectSpecular;
+        
+        gl_FragColor.rgb = gl_FragColor.rgb + indirectLighting ;        
+    #endif
+ 
+    #if defined(EMISSIVE) || defined (EMISSIVEMAP)
+        #ifdef EMISSIVEMAP
+            vec4 emissive = texture2D(m_EmissiveMap, newTexCoord);
+        #else
+            vec4 emissive = m_Emissive;
+        #endif
+        gl_FragColor += emissive * pow(emissive.a, m_EmissivePower) * m_EmissiveIntensity;
+    #endif
+           
+    gl_FragColor.a = alpha;
+   
+}

+ 305 - 0
jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md

@@ -0,0 +1,305 @@
+MaterialDef PBR Lighting {
+
+    MaterialParameters {
+
+        // Alpha threshold for fragment discarding
+        Float AlphaDiscardThreshold (AlphaTestFallOff)
+
+        //metalness of the material
+        Float Metallic : 0.0
+        //Roughness of the material
+        Float Roughness : 1.0        
+        // Base material color
+        Color BaseColor
+        // The emissive color of the object
+        Color Emissive        
+        // the emissive power
+        Float EmissivePower : 3.0        
+        // the emissive intensity
+        Float EmissiveIntensity : 1.0
+
+        // BaseColor map
+        Texture2D BaseColorMap
+
+        // Specular/gloss map
+        Texture2D MetallicMap -LINEAR
+        
+        // Roughness Map
+        Texture2D RoughnessMap -LINEAR
+        
+        // Texture of the emissive parts of the material
+        Texture2D EmissiveMap
+
+        // Normal map
+        Texture2D NormalMap -LINEAR
+
+        // For Spec gloss pipeline
+        Texture2D SpecularMap
+        Texture2D GlossMap
+
+        // Prefiltered Env Map for indirect specular lighting
+        TextureCubeMap PrefEnvMap -LINEAR
+        
+        // Irradiance map for indirect diffuse lighting
+        TextureCubeMap IrradianceMap -LINEAR
+
+        //integrate BRDF map for indirect Lighting
+        Texture2D IntegrateBRDF -LINEAR
+
+        // Parallax/height map
+        Texture2D ParallaxMap -LINEAR
+
+        //Set to true is parallax map is stored in the alpha channel of the normal map
+        Boolean PackedNormalParallax   
+
+        //Sets the relief height for parallax mapping
+        Float ParallaxHeight : 0.05       
+
+        //Set to true to activate Steep Parallax mapping
+        Boolean SteepParallax
+
+        // Set to Use Lightmap
+        Texture2D LightMap
+
+        // Set to use TexCoord2 for the lightmap sampling
+        Boolean SeparateTexCoord
+
+        //shadows
+        Int FilterMode
+        Boolean HardwareShadows
+
+        Texture2D ShadowMap0
+        Texture2D ShadowMap1
+        Texture2D ShadowMap2
+        Texture2D ShadowMap3
+        //pointLights
+        Texture2D ShadowMap4
+        Texture2D ShadowMap5
+        
+        Float ShadowIntensity
+        Vector4 Splits
+        Vector2 FadeInfo
+
+        Matrix4 LightViewProjectionMatrix0
+        Matrix4 LightViewProjectionMatrix1
+        Matrix4 LightViewProjectionMatrix2
+        Matrix4 LightViewProjectionMatrix3
+        //pointLight
+        Matrix4 LightViewProjectionMatrix4
+        Matrix4 LightViewProjectionMatrix5   
+        Vector3 LightPos
+        Vector3 LightDir
+
+        Float PCFEdge
+        Float ShadowMapSize
+
+        // For hardware skinning
+        Int NumberOfBones
+        Matrix4Array BoneMatrices
+                
+        //For instancing
+        Boolean UseInstancing
+
+        //For Vertex Color
+        Boolean UseVertexColor
+    }
+
+ Technique {
+        LightMode SinglePass
+        
+        VertexShader GLSL100:   Common/MatDefs/Light/PBRLighting.vert
+        FragmentShader GLSL100: Common/MatDefs/Light/PBRLighting.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+            CameraPosition
+            WorldMatrix
+        }
+
+        Defines {         
+            BASECOLORMAP : BaseColorMap            
+            NORMALMAP : NormalMap
+            METALLICMAP : MetallicMap
+            ROUGHNESSMAP : RoughnessMap
+            EMISSIVEMAP : EmissiveMap
+            EMISSIVE : Emissive
+            SPECGLOSSPIPELINE : SpecularMap
+            PARALLAXMAP : ParallaxMap
+            NORMALMAP_PARALLAX : PackedNormalParallax
+            STEEP_PARALLAX : SteepParallax
+            LIGHTMAP : LightMap
+            SEPARATE_TEXCOORD : SeparateTexCoord
+            DISCARD_ALPHA : AlphaDiscardThreshold                        
+            NUM_BONES : NumberOfBones                        
+            INSTANCING : UseInstancing
+            INDIRECT_LIGHTING : IntegrateBRDF
+            VERTEX_COLOR : UseVertexColor
+        }
+    }
+
+   
+
+    Technique PreShadow {
+
+        VertexShader GLSL100 :   Common/MatDefs/Shadow/PreShadow.vert
+        FragmentShader GLSL100 : Common/MatDefs/Shadow/PreShadow.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+            WorldViewMatrix
+            ViewProjectionMatrix
+            ViewMatrix
+        }
+
+        Defines {
+            COLOR_MAP : ColorMap
+            DISCARD_ALPHA : AlphaDiscardThreshold
+            NUM_BONES : NumberOfBones
+            INSTANCING : UseInstancing
+        }
+
+        ForcedRenderState {
+            FaceCull Off
+            DepthTest On
+            DepthWrite On
+            PolyOffset 5 3
+            ColorWrite Off
+        }
+
+    }
+
+
+    Technique PostShadow15{
+        VertexShader GLSL150:   Common/MatDefs/Shadow/PostShadow15.vert
+        FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+            WorldMatrix
+            ViewProjectionMatrix
+            ViewMatrix
+        }
+
+        Defines {
+            HARDWARE_SHADOWS : HardwareShadows
+            FILTER_MODE : FilterMode
+            PCFEDGE : PCFEdge
+            DISCARD_ALPHA : AlphaDiscardThreshold           
+            COLOR_MAP : ColorMap
+            SHADOWMAP_SIZE : ShadowMapSize
+            FADE : FadeInfo
+            PSSM : Splits
+            POINTLIGHT : LightViewProjectionMatrix5
+            NUM_BONES : NumberOfBones
+            INSTANCING : UseInstancing
+        }
+
+        ForcedRenderState {
+            Blend Modulate
+            DepthWrite Off                 
+            PolyOffset -0.1 0
+        }
+    }
+
+    Technique PostShadow{
+        VertexShader GLSL100:   Common/MatDefs/Shadow/PostShadow.vert
+        FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+            WorldMatrix
+            ViewProjectionMatrix
+            ViewMatrix
+        }
+
+        Defines {
+            HARDWARE_SHADOWS : HardwareShadows
+            FILTER_MODE : FilterMode
+            PCFEDGE : PCFEdge
+            DISCARD_ALPHA : AlphaDiscardThreshold           
+            COLOR_MAP : ColorMap
+            SHADOWMAP_SIZE : ShadowMapSize
+            FADE : FadeInfo
+            PSSM : Splits
+            POINTLIGHT : LightViewProjectionMatrix5
+            NUM_BONES : NumberOfBones
+            INSTANCING : UseInstancing
+        }
+
+        ForcedRenderState {
+            Blend Modulate
+            DepthWrite Off   
+            PolyOffset -0.1 0  
+        }
+    }
+
+  Technique PreNormalPass {
+
+        VertexShader GLSL100 :   Common/MatDefs/SSAO/normal.vert
+        FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+            WorldViewMatrix
+            NormalMatrix
+            ViewProjectionMatrix
+            ViewMatrix
+        }
+
+        Defines {
+            DIFFUSEMAP_ALPHA : DiffuseMap
+            NUM_BONES : NumberOfBones
+            INSTANCING : UseInstancing
+        }
+
+    }
+
+
+    Technique PreNormalPassDerivative {
+
+        VertexShader GLSL100 :   Common/MatDefs/MSSAO/normal.vert
+        FragmentShader GLSL100 : Common/MatDefs/MSSAO/normal.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+            WorldViewMatrix
+            NormalMatrix                        
+            ViewProjectionMatrix
+            ViewMatrix
+        }
+
+        Defines {
+            DIFFUSEMAP_ALPHA : DiffuseMap
+            NUM_BONES : NumberOfBones
+            INSTANCING : UseInstancing
+        }
+
+    }
+
+    Technique GBuf {
+
+        VertexShader GLSL100:   Common/MatDefs/Light/GBuf.vert
+        FragmentShader GLSL100: Common/MatDefs/Light/GBuf.frag
+
+        WorldParameters {
+            WorldViewProjectionMatrix
+            NormalMatrix
+            WorldViewMatrix
+            WorldMatrix
+        }
+
+        Defines {
+            VERTEX_COLOR : UseVertexColor
+            MATERIAL_COLORS : UseMaterialColors
+            V_TANGENT : VTangent
+            MINNAERT  : Minnaert
+            WARDISO   : WardIso
+
+            DIFFUSEMAP : DiffuseMap
+            NORMALMAP : NormalMap
+            SPECULARMAP : SpecularMap
+            PARALLAXMAP : ParallaxMap
+        }
+    }
+
+}

+ 67 - 0
jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert

@@ -0,0 +1,67 @@
+#import "Common/ShaderLib/Instancing.glsllib"
+#import "Common/ShaderLib/Skinning.glsllib"
+
+uniform vec4 m_BaseColor;
+
+uniform vec4 g_AmbientLightColor;
+varying vec2 texCoord;
+
+#ifdef SEPARATE_TEXCOORD
+  varying vec2 texCoord2;
+  attribute vec2 inTexCoord2;
+#endif
+
+varying vec4 Color;
+
+attribute vec3 inPosition;
+attribute vec2 inTexCoord;
+attribute vec3 inNormal;
+
+#ifdef VERTEX_COLOR
+  attribute vec4 inColor;
+#endif
+
+varying vec3 wNormal;
+varying vec3 wPosition;
+#ifdef NORMALMAP
+    attribute vec4 inTangent;
+    varying vec3 wTangent;
+    varying vec3 wBinormal;
+#endif
+
+void main(){
+    vec4 modelSpacePos = vec4(inPosition, 1.0);
+    vec3 modelSpaceNorm = inNormal;
+
+    #if  defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+         vec3 modelSpaceTan  = inTangent.xyz;
+    #endif
+
+    #ifdef NUM_BONES
+         #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+         Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
+         #else
+         Skinning_Compute(modelSpacePos, modelSpaceNorm);
+         #endif
+    #endif
+
+    gl_Position = TransformWorldViewProjection(modelSpacePos);
+    texCoord = inTexCoord;
+    #ifdef SEPARATE_TEXCOORD
+       texCoord2 = inTexCoord2;
+    #endif
+
+    wPosition = TransformWorld(modelSpacePos).xyz;
+    wNormal  = TransformWorld(vec4(modelSpaceNorm,0.0)).xyz;
+       
+    #if defined(NORMALMAP) 
+      wTangent = TransformWorld(vec4(modelSpaceTan,0.0)).xyz;
+      wBinormal = cross(wNormal, wTangent)* inTangent.w;            
+    #endif
+
+    Color = m_BaseColor;
+    
+    #ifdef VERTEX_COLOR                    
+        Color *= inColor;
+    #endif
+}

+ 120 - 0
jme3-core/src/main/resources/Common/ShaderLib/PBR.glsllib

@@ -0,0 +1,120 @@
+
+#ifndef PI
+    #define PI 3.14159265358979323846264
+#endif
+
+//Specular fresnel computation
+vec3 F_Shlick(float vh,	vec3 F0){
+	float fresnelFact = pow(2.0, (-5.55473*vh - 6.98316) * vh);
+	return mix(F0, vec3(1.0, 1.0, 1.0), fresnelFact);
+}
+
+void PBR_ComputeDirectLightSpecWF(vec3 normal, vec3 lightDir, vec3 viewDir,
+                            vec3 lightColor, vec3 specColor, float roughness, float ndotv,
+                            out vec3 outDiffuse, out vec3 outSpecular){
+    // Compute halfway vector.
+    vec3 halfVec = normalize(lightDir + viewDir);
+
+    // Compute ndotl, ndoth,  vdoth terms which are needed later.
+    float ndotl = max( dot(normal,   lightDir), 0.0);
+    float ndoth = max( dot(normal,   halfVec),  0.0);       
+    float hdotv = max( dot(viewDir,  halfVec),  0.0);
+
+    // Compute diffuse using energy-conserving Lambert.
+    // Alternatively, use Oren-Nayar for really rough 
+    // materials or if you have lots of processing power ...
+    outDiffuse = vec3(ndotl) * lightColor;
+
+    //cook-torrence, microfacet BRDF : http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
+   
+    float alpha = roughness * roughness;
+
+    //D, GGX normaal Distribution function     
+    float alpha2 = alpha * alpha;
+    float sum  = ((ndoth * ndoth) * (alpha2 - 1.0) + 1.0);
+    float denom = PI * sum * sum;
+    float D = alpha2 / denom;  
+
+    // Compute Fresnel function via Schlick's approximation.
+    vec3 fresnel =  F_Shlick(hdotv,  specColor);
+    
+    //G Shchlick GGX Gometry shadowing term,  k = alpha/2
+    float k = alpha * 0.5;
+
+    // UE4 way to optimise shlick GGX Gometry shadowing term
+    //http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html
+    float G_V = ndotv + sqrt( (ndotv - ndotv * k) * ndotv + k );
+    float G_L = ndotl + sqrt( (ndotl - ndotl * k) * ndotl + k );    
+    // the max here is to avoid division by 0 that may cause some small glitches.
+    float G = 1.0/max( G_V * G_L ,0.01); 
+
+    float specular = D * G * ndotl;  
+ 
+    outSpecular = fresnel * vec3(specular) * lightColor;
+}
+
+void PBR_ComputeDirectLight(vec3 normal, vec3 lightDir, vec3 viewDir,
+                            vec3 lightColor, float fZero, float roughness, float ndotv,
+                            out vec3 outDiffuse, out vec3 outSpecular){
+    // Compute halfway vector.
+    vec3 halfVec = normalize(lightDir + viewDir);
+
+    // Compute ndotl, ndoth,  vdoth terms which are needed later.
+    float ndotl = max( dot(normal,   lightDir), 0.0);
+    float ndoth = max( dot(normal,   halfVec),  0.0);       
+    float hdotv = max( dot(viewDir,  halfVec),  0.0);
+
+    // Compute diffuse using energy-conserving Lambert.
+    // Alternatively, use Oren-Nayar for really rough 
+    // materials or if you have lots of processing power ...
+    outDiffuse = vec3(ndotl) * lightColor;
+
+    //cook-torrence, microfacet BRDF : http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
+   
+    float alpha = roughness * roughness;
+
+    //D, GGX normaal Distribution function     
+    float alpha2 = alpha * alpha;
+    float sum  = ((ndoth * ndoth) * (alpha2 - 1.0) + 1.0);
+    float denom = PI * sum * sum;
+    float D = alpha2 / denom;  
+
+    // Compute Fresnel function via Schlick's approximation.
+    float fresnel = fZero + ( 1.0 - fZero ) * pow( 1.0 - hdotv, 5.0 );
+    
+    //G Shchlick GGX Gometry shadowing term,  k = alpha/2
+    float k = alpha * 0.5;
+
+ /*   
+    //classic Schlick ggx
+    float G_V = ndotv / (ndotv * (1.0 - k) + k);
+    float G_L = ndotl / (ndotl * (1.0 - k) + k);
+    float G = ( G_V * G_L );
+    
+    float specular =(D* fresnel * G) /(4 * ndotv);
+   */
+ 
+    // UE4 way to optimise shlick GGX Gometry shadowing term
+    //http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html
+    float G_V = ndotv + sqrt( (ndotv - ndotv * k) * ndotv + k );
+    float G_L = ndotl + sqrt( (ndotl - ndotl * k) * ndotl + k );    
+    // the max here is to avoid division by 0 that may cause some small glitches.
+    float G = 1.0/max( G_V * G_L ,0.01); 
+
+    float specular = D * fresnel * G * ndotl;  
+ 
+    outSpecular = vec3(specular) * lightColor;
+}
+
+
+vec3 ApproximateSpecularIBL(samplerCube envMap,sampler2D integrateBRDF, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec){
+    //TODO magic values should be replaced by defines.
+    float Lod = log2(Roughness) * 1.2 + 6.0 - 1.0;
+    vec3 PrefilteredColor =  textureCube(envMap, refVec.xyz,Lod).rgb;
+    vec2 EnvBRDF = texture2D(integrateBRDF,vec2(Roughness, ndotv)).rg;
+    return PrefilteredColor * ( SpecularColor * EnvBRDF.x+ EnvBRDF.y );    
+}
+
+
+
+

BIN
jme3-core/src/main/resources/Common/Textures/integrateBRDF.ktx


+ 6 - 0
jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java

@@ -296,6 +296,12 @@ public class DDSLoader implements AssetLoader {
                     // exit here, the rest of the structure is not valid
                     // the real format will be available in the DX10 header
                     return;
+                    
+                case 113:
+                    compressed = false;
+                    bpp = 64;
+                    pixelFormat = Image.Format.RGBA16F;
+                    break;
                 default:
                     throw new IOException("Unknown fourcc: " + string(fourcc) + ", " + Integer.toHexString(fourcc));
             }

+ 1 - 1
jme3-examples/src/main/java/jme3test/batching/TestBatchNodeCluster.java

@@ -60,7 +60,7 @@ public class TestBatchNodeCluster extends SimpleApplication {
         settingst.setVSync(false);
         settingst.setFullscreen(false);
         app.setSettings(settingst);
-        app.setShowSettings(false);
+        app.setShowSettings(false); 
         app.start();
     }
     private ActionListener al = new ActionListener() {

+ 131 - 0
jme3-examples/src/main/java/jme3test/light/TestShadowBug.java

@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2009-2015 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.light;
+
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.light.SpotLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+import com.jme3.shadow.EdgeFilteringMode;
+import com.jme3.shadow.PointLightShadowRenderer;
+import com.jme3.shadow.SpotLightShadowRenderer;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+
+
+public class TestShadowBug extends SimpleApplication {
+  public static void main(String[] args) {
+    TestShadowBug app = new TestShadowBug();
+    app.start();
+  }
+
+  @Override
+  public void simpleInitApp() {
+    flyCam.setMoveSpeed(100f);
+    rootNode.attachChild(makeFloor());
+
+    Node characters = new Node("Characters");
+    characters.setShadowMode(ShadowMode.Cast);
+    rootNode.attachChild(characters);
+
+    Spatial golem = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+    golem.scale(0.5f);
+    golem.setLocalTranslation(200.0f, -6f, 200f);
+    golem.setShadowMode(ShadowMode.CastAndReceive);
+    characters.attachChild(golem);
+
+    DirectionalLight sun = new DirectionalLight();
+    sun.setDirection(new Vector3f(-1f, -1f, 1f));
+    sun.setColor(ColorRGBA.White.mult(1.3f));
+    rootNode.addLight(sun);
+    characters.addLight(sun);
+
+    SpotLight spot = new SpotLight();
+    spot.setSpotRange(13f);                           // distance
+    spot.setSpotInnerAngle(15f * FastMath.DEG_TO_RAD); // inner light cone (central beam)
+    spot.setSpotOuterAngle(20f * FastMath.DEG_TO_RAD); // outer light cone (edge of the light)
+    spot.setColor(ColorRGBA.White.mult(1.3f));         // light color
+    spot.setPosition(new Vector3f(192.0f, -1f, 192f));
+    spot.setDirection(new Vector3f(1, -0.5f, 1));
+    rootNode.addLight(spot);
+
+    PointLight lamp_light = new PointLight();
+    lamp_light.setColor(ColorRGBA.Yellow);
+    lamp_light.setRadius(20f);
+    lamp_light.setPosition(new Vector3f(210.0f, 0f, 210f));
+    rootNode.addLight(lamp_light);
+
+    SpotLightShadowRenderer slsr = new SpotLightShadowRenderer(assetManager, 512);
+    slsr.setLight(spot);
+    slsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
+    slsr.setShadowIntensity(0.6f);
+    slsr.setFlushQueues(false);
+    viewPort.addProcessor(slsr);
+
+    PointLightShadowRenderer plsr = new PointLightShadowRenderer(assetManager, 512);
+    plsr.setLight(lamp_light);
+    plsr.setShadowIntensity(0.6f);
+    plsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
+    plsr.setFlushQueues(false);
+    viewPort.addProcessor(plsr);
+
+    viewPort.getCamera().setLocation(new Vector3f(192.0f, 10f, 192f));
+    float[] angles = new float[]{3.14f/2, 3.14f/2, 0};
+    viewPort.getCamera().setRotation(new Quaternion(angles));
+  }
+
+  protected Geometry makeFloor() {
+    Box box = new Box(220, .2f, 220);
+    box.scaleTextureCoordinates(new Vector2f(10, 10));
+    Geometry floor = new Geometry("the Floor", box);
+    floor.setLocalTranslation(200, -9, 200);
+    Material matGroundL = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+    Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+    grass.setWrap(WrapMode.Repeat);
+    matGroundL.setTexture("DiffuseMap", grass);
+    floor.setMaterial(matGroundL);
+    floor.setShadowMode(ShadowMode.CastAndReceive);
+    return floor;
+  }
+}

+ 172 - 0
jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java

@@ -0,0 +1,172 @@
+package jme3test.light.pbr;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.ChaseCamera;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.FXAAFilter;
+import com.jme3.post.filters.ToneMapFilter;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.texture.pbr.EnvironmentCamera;
+import com.jme3.texture.plugins.ktx.KTXLoader;
+import com.jme3.util.SkyFactory;
+
+/**
+ * A test case for PBR lighting.
+ * Still experimental.
+ *
+ * @author nehon
+ */
+public class TestPBRLighting extends SimpleApplication {
+
+    public static void main(String[] args) {
+        TestPBRLighting app = new TestPBRLighting();
+        app.start();
+    }
+    private Geometry model;
+    private DirectionalLight dl;
+    private Node modelNode;
+    private int frame = 0;
+    private boolean indirectLighting = true;
+    private Material pbrMat;
+    private Material adHocMat;
+
+    @Override
+    public void simpleInitApp() {
+        assetManager.registerLoader(KTXLoader.class, "ktx");
+
+        viewPort.setBackgroundColor(ColorRGBA.White);
+        modelNode = (Node) new Node("modelNode");
+        model = (Geometry) assetManager.loadModel("Models/Tank/tank.j3o");
+        modelNode.attachChild(model);
+
+        dl = new DirectionalLight();
+        dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+        rootNode.addLight(dl);
+        dl.setColor(ColorRGBA.White);
+        rootNode.attachChild(modelNode);
+
+        final EnvironmentCamera envCam = new EnvironmentCamera(128, new Vector3f(0, 3f, 0));
+        stateManager.attach(envCam);
+        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+        fpp.addFilter(new FXAAFilter());
+        fpp.addFilter(new ToneMapFilter(Vector3f.UNIT_XYZ.mult(2.0f)));
+        viewPort.addProcessor(fpp);
+
+        //Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Sky_Cloudy.hdr", SkyFactory.EnvMapType.EquirectMap);
+        Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap);
+        //Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Stonewall.hdr", SkyFactory.EnvMapType.EquirectMap);
+        //Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/road.hdr", SkyFactory.EnvMapType.EquirectMap);
+        rootNode.attachChild(sky);
+
+        pbrMat = assetManager.loadMaterial("Models/Tank/tank.j3m");
+        model.setMaterial(pbrMat);
+
+        ChaseCamera chaser = new ChaseCamera(cam, modelNode, inputManager);
+        chaser.setDragToRotate(true);
+        chaser.setMinVerticalRotation(-FastMath.HALF_PI);
+        chaser.setMaxDistance(1000);
+        chaser.setSmoothMotion(true);
+        chaser.setRotationSensitivity(10);
+        chaser.setZoomSensitivity(5);
+        flyCam.setEnabled(false);
+        //flyCam.setMoveSpeed(100);
+
+        inputManager.addListener(new ActionListener() {
+            @Override
+            public void onAction(String name, boolean isPressed, float tpf) {
+                if (name.equals("toggle") && isPressed) {
+                    if (!indirectLighting) {
+                        toggleIBL();
+
+                    } else {
+                        pbrMat.clearParam("IntegrateBRDF");
+                        indirectLighting = false;
+                    }
+                }
+
+                if (name.equals("switchMats") && isPressed) {
+                    if (model.getMaterial() == pbrMat) {
+                        model.setMaterial(adHocMat);
+                    } else {
+                        model.setMaterial(pbrMat);
+                    }
+                }
+
+                if (name.equals("debug") && isPressed) {
+                    envCam.toggleDebug();
+                }
+
+                if (name.equals("up") && isPressed) {
+                    model.move(0, tpf * 100f, 0);
+                }
+
+                if (name.equals("down") && isPressed) {
+                    model.move(0, -tpf * 100f, 0);
+                }
+                if (name.equals("left") && isPressed) {
+                    model.move(0, 0, tpf * 100f);
+                }
+                if (name.equals("right") && isPressed) {
+                    model.move(0, 0, -tpf * 100f);
+                }
+                if (name.equals("light") && isPressed) {
+                    dl.setDirection(cam.getDirection().normalize());
+                }
+            }
+        }, "toggle", "light", "up", "down", "left", "right", "debug");
+
+        inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_RETURN));
+        inputManager.addMapping("light", new KeyTrigger(KeyInput.KEY_F));
+        inputManager.addMapping("up", new KeyTrigger(KeyInput.KEY_UP));
+        inputManager.addMapping("down", new KeyTrigger(KeyInput.KEY_DOWN));
+        inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_LEFT));
+        inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_RIGHT));
+        inputManager.addMapping("debug", new KeyTrigger(KeyInput.KEY_D));
+
+    }
+
+    private void toggleIBL() {
+        ensurePbrMat();
+        pbrMat.setTexture("IrradianceMap", stateManager.getState(EnvironmentCamera.class).getIrradianceMap());
+        pbrMat.setTexture("PrefEnvMap", stateManager.getState(EnvironmentCamera.class).getPrefilteredEnvMap());
+        pbrMat.setTexture("IntegrateBRDF", assetManager.loadTexture("Common/Textures/integrateBRDF.ktx"));
+        indirectLighting = true;
+    }
+
+    private void ensurePbrMat() {
+        if (model.getMaterial() != pbrMat && model.getMaterial() != adHocMat) {
+            pbrMat = model.getMaterial();
+        }
+    }
+
+    @Override
+    public void simpleUpdate(float tpf) {
+        frame++;
+
+        if (frame == 2) {
+            modelNode.removeFromParent();
+            stateManager.getState(EnvironmentCamera.class).snapshot(rootNode, new Runnable() {
+                 
+                //this code is ensured to be called in the update loop, the run method is called by the EnvCamera app state in it's update cycle
+                @Override
+                public void run() {                    
+                  toggleIBL();
+                }
+            });
+        }
+        if (frame > 2 && modelNode.getParent() == null) {
+            rootNode.attachChild(modelNode);
+        }
+    }
+
+}

BIN
jme3-testdata/src/main/resources/Models/Tank/Tank_Base_Color.png


BIN
jme3-testdata/src/main/resources/Models/Tank/Tank_Emissive.png


BIN
jme3-testdata/src/main/resources/Models/Tank/Tank_Metallic.png


BIN
jme3-testdata/src/main/resources/Models/Tank/Tank_Normal.png


BIN
jme3-testdata/src/main/resources/Models/Tank/Tank_Roughness.png


+ 14 - 0
jme3-testdata/src/main/resources/Models/Tank/tank.j3m

@@ -0,0 +1,14 @@
+Material Tank : Common/MatDefs/Light/PBRLighting.j3md {
+     MaterialParameters {
+        
+        MetallicMap : Flip Models/Tank/Tank_Metallic.png 
+        RoughnessMap : Flip Models/Tank/Tank_Roughness.png
+        NormalMap : Flip Models/Tank/Tank_Normal.png
+        BaseColorMap : Flip Models/Tank/Tank_Base_Color.png
+        EmissiveMap : Flip Models/Tank/Tank_Emissive.png
+        EmissiveIntensity : 2.0
+     
+     }
+    AdditionalRenderState {
+    }
+}

BIN
jme3-testdata/src/main/resources/Models/Tank/tank.j3o


+ 3 - 0
jme3-testdata/src/main/resources/Models/Tank/tank.j3odata

@@ -0,0 +1,3 @@
+#
+#Sat Apr 11 15:27:27 CEST 2015
+ORIGINAL_PATH=Models/Tank/tank.obj

BIN
jme3-testdata/src/main/resources/Textures/Sky/Path.hdr