diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md index bc699e6b1..cdd1bdcff 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md @@ -27,6 +27,9 @@ MaterialDef Phong Lighting { // Specular power/shininess Float Shininess : 1 + // Ambient map + Texture2D AmbientMap + // Diffuse map Texture2D DiffuseMap @@ -79,7 +82,7 @@ MaterialDef Phong Lighting { Boolean EnvMapAsSphereMap //shadows - Int FilterMode + Int FilterMode Boolean HardwareShadows Texture2D ShadowMap0 @@ -117,6 +120,27 @@ MaterialDef Phong Lighting { Boolean BackfaceShadows : false } + Technique { + LightMode StaticPass + + VertexShader GLSL100 GLSL150 : Common/MatDefs/Light/StaticLighting.vert + FragmentShader GLSL100 GLSL150 : Common/MatDefs/Light/StaticLighting.frag + + WorldParameters { + WorldViewProjectionMatrix + NormalMatrix + WorldViewMatrix + ViewMatrix + CameraPosition + WorldMatrix + ViewProjectionMatrix + } + + Defines { + AMBIENTMAP : AmbientMap + } + } + Technique { LightMode SinglePass diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/StaticLighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/StaticLighting.frag new file mode 100755 index 000000000..962e2c122 --- /dev/null +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/StaticLighting.frag @@ -0,0 +1,203 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" +#import "Common/ShaderLib/BlinnPhongLighting.glsllib" +#import "Common/ShaderLib/Lighting.glsllib" +#import "Common/ShaderLib/InPassShadows.glsl" + +#ifndef NUM_DIR_LIGHTS +#define NUM_DIR_LIGHTS 0 +#endif + +#ifndef NUM_POINT_LIGHTS +#define NUM_POINT_LIGHTS 0 +#endif + +#ifndef NUM_SPOT_LIGHTS +#define NUM_SPOT_LIGHTS 0 +#endif + +#define DIR_SHADOW_LIGHT_START (0) +#define DIR_SHADOW_LIGHT_END (NUM_SHADOW_DIR_LIGHTS * 2) + +#define DIR_LIGHT_START (DIR_SHADOW_LIGHT_END) +#define DIR_LIGHT_END (NUM_DIR_LIGHTS * 2) + +#define POINT_LIGHT_START (DIR_LIGHT_END) +#define POINT_LIGHT_END (POINT_LIGHT_START + NUM_POINT_LIGHTS * 2) + +#define SPOT_SHADOW_LIGHT_START (POINT_LIGHT_END) +#define SPOT_SHADOW_LIGHT_END (SPOT_SHADOW_LIGHT_START + NUM_SHADOW_SPOT_LIGHTS * 3) + +#define SPOT_LIGHT_START (SPOT_SHADOW_LIGHT_END) +#define SPOT_LIGHT_END (SPOT_LIGHT_START + NUM_SPOT_LIGHTS * 3) + +#define LIGHT_DATA_SIZE (SPOT_LIGHT_END) + +uniform sampler2D m_AmbientMap; +uniform float m_AlphaDiscardThreshold; +uniform float m_Shininess; +uniform vec4 g_AmbientLightColor; + +#if LIGHT_DATA_SIZE > 0 +uniform vec4 g_LightData[LIGHT_DATA_SIZE]; +#else +const vec4 g_LightData[1] = vec4[]( vec4(1.0) ); +#endif + +varying vec3 vPos; +varying vec3 vNormal; +varying vec2 vTexCoord; + +struct surface_t { + vec3 position; + vec3 normal; + vec3 viewDir; + vec3 ambient; + vec4 diffuse; + vec4 specular; + float shininess; +}; + +vec2 Lighting_ProcessLighting(vec3 norm, vec3 viewDir, vec3 lightDir, float attenuation, float shininess) { + float diffuseFactor = max(0.0, dot(norm, lightDir)); + vec3 H = normalize(viewDir + lightDir); + float HdotN = max(0.0, dot(H, norm)); + float specularFactor = pow(HdotN, shininess); + return vec2(diffuseFactor, diffuseFactor * specularFactor) * vec2(attenuation); +} + +vec2 Lighting_ProcessDirectional(int lightIndex, surface_t surface) { + vec3 lightDirection = g_LightData[lightIndex + 1].xyz; + vec2 light = Lighting_ProcessLighting(surface.normal, surface.viewDir, -lightDirection, 1.0, surface.shininess); + return light; +} + +float Lighting_ProcessAttenuation(float invRadius, float dist) { + #ifdef SRGB + float atten = (1.0 - invRadius * dist) / (1.0 + invRadius * dist * dist); + return clamp(atten, 0.0, 1.0); + #else + return max(0.0, 1.0 - invRadius * dist); + #endif +} + +vec2 Lighting_ProcessPoint(int lightIndex, surface_t surface) { + vec4 lightPosition = g_LightData[lightIndex + 1]; + vec3 lightDirection = lightPosition.xyz - surface.position; + float dist = length(lightDirection); + lightDirection /= vec3(dist); + float atten = Lighting_ProcessAttenuation(lightPosition.w, dist); + return Lighting_ProcessLighting(surface.normal, surface.viewDir, lightDirection, atten, surface.shininess); +} + +vec2 Lighting_ProcessSpot(int lightIndex, surface_t surface) { + vec4 lightPosition = g_LightData[lightIndex + 1]; + vec4 lightDirection = g_LightData[lightIndex + 2]; + vec3 lightVector = lightPosition.xyz - surface.position; + float dist = length(lightVector); + lightVector /= vec3(dist); + float atten = Lighting_ProcessAttenuation(lightPosition.w, dist); + atten *= computeSpotFalloff(lightDirection, lightVector); + return Lighting_ProcessLighting(surface.normal, surface.viewDir, lightVector, atten, surface.shininess); +} + +void Lighting_ProcessAll(surface_t surface, out vec3 ambient, out vec3 diffuse, out vec3 specular) { + + ambient = g_AmbientLightColor.rgb; + diffuse = vec3(0.0); + specular = vec3(0.0); + + Shadow_ProcessPssmSlice(); + +#if LIGHT_DATA_SIZE > 0 + int projIndex = 0; + + for (int i = DIR_SHADOW_LIGHT_START; i < DIR_SHADOW_LIGHT_END; i += 2) { + vec4 lightColor = g_LightData[i]; + vec2 lightDiffSpec = Lighting_ProcessDirectional(i, surface); + float shadow = Shadow_ProcessDirectional(projIndex, lightColor.w); + lightDiffSpec *= vec2(shadow); + diffuse += lightColor.rgb * lightDiffSpec.x; + specular += lightColor.rgb * lightDiffSpec.y; + projIndex += NUM_PSSM_SPLITS; + } + + for (int i = DIR_LIGHT_START; i < DIR_LIGHT_END; i += 2) { + vec3 lightColor = g_LightData[i].rgb; + vec2 lightDiffSpec = Lighting_ProcessDirectional(i, surface); + diffuse += lightColor.rgb * lightDiffSpec.x; + specular += lightColor.rgb * lightDiffSpec.y; + } + + for (int i = POINT_LIGHT_START; i < POINT_LIGHT_END; i += 2) { + vec3 lightColor = g_LightData[i].rgb; + vec2 lightDiffSpec = Lighting_ProcessPoint(i, surface); + diffuse += lightColor.rgb * lightDiffSpec.x; + specular += lightColor.rgb * lightDiffSpec.y; + } + + for (int i = SPOT_SHADOW_LIGHT_START; i < SPOT_SHADOW_LIGHT_END; i += 3) { + vec4 lightColor = g_LightData[i]; + vec2 lightDiffSpec = Lighting_ProcessSpot(i, surface); + float shadow = Shadow_ProcessSpot(projIndex, lightColor.w); + lightDiffSpec *= vec2(shadow); + diffuse += lightColor.rgb * lightDiffSpec.x; + specular += lightColor.rgb * lightDiffSpec.y; + projIndex++; + } + + for (int i = SPOT_LIGHT_START; i < SPOT_LIGHT_END; i += 3) { + vec3 lightColor = g_LightData[i].rgb; + vec2 lightDiffSpec = Lighting_ProcessSpot(i, surface); + diffuse += lightColor * lightDiffSpec.x; + specular += lightColor * lightDiffSpec.y; + } + +#endif +} + +surface_t getSurface() { + surface_t s; + s.position = vPos; + s.normal = normalize(vNormal); + if (!gl_FrontFacing) { + s.normal = -s.normal; + } + s.viewDir = normalize(-vPos); +#ifdef AMBIENTMAP + s.ambient = texture2D(m_AmbientMap, vTexCoord).rgb; +#else + s.ambient = vec3(1.0); +#endif + s.diffuse = vec4(1.0); + s.specular = vec4(1.0); + s.shininess = m_Shininess; + return s; +} + +void main() { + vec3 ambient, diffuse, specular; + + surface_t surface = getSurface(); + Lighting_ProcessAll(surface, ambient, diffuse, specular); + + vec4 color = vec4(1.0); + color.rgb = surface.ambient.rgb * ambient + + surface.diffuse.rgb * diffuse + + surface.specular.rgb * specular; + + #ifdef DISCARD_ALPHA + if (color.a < m_AlphaDiscardThreshold) { + discard; + } + #endif + + gl_FragColor = color; + +/* + vec4 projCoord = vProjCoord[0]; + projCoord.xyz /= projCoord.w; + float shad = shadow2D(g_ShadowMapArray, vec4(projCoord.xy, 0.0, projCoord.z)).r; + vec3 amb = texture2D(m_AmbientMap, vTexCoord).rgb; + gl_FragColor = vec4(amb * vec3(shad), 1.0); +*/ +} \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/StaticLighting.vert b/jme3-core/src/main/resources/Common/MatDefs/Light/StaticLighting.vert new file mode 100755 index 000000000..354afec0c --- /dev/null +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/StaticLighting.vert @@ -0,0 +1,32 @@ +#import "Common/ShaderLib/GLSLCompat.glsllib" +#import "Common/ShaderLib/Skinning.glsllib" +#import "Common/ShaderLib/Instancing.glsllib" +#import "Common/ShaderLib/InPassShadows.glsl" + +attribute vec3 inPosition; +attribute vec3 inNormal; +attribute vec2 inTexCoord; +attribute vec4 inColor; + +varying vec3 vPos; +varying vec3 vNormal; +varying vec2 vTexCoord; + +void main() { + vTexCoord = inTexCoord; + + vec4 modelSpacePos = vec4(inPosition, 1.0); + vec3 modelSpaceNorm = inNormal; + + #ifdef NUM_BONES + Skinning_Compute(modelSpacePos, modelSpaceNorm); + #endif + + vPos = TransformWorldView(modelSpacePos).xyz; + vNormal = TransformNormal(modelSpaceNorm); + + vec3 shadowPos = TransformWorld(modelSpacePos).xyz; + Shadow_ProcessProjCoord(shadowPos); + + gl_Position = TransformWorldViewProjection(modelSpacePos); +} \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/ShaderLib/InPassShadows.glsl b/jme3-core/src/main/resources/Common/ShaderLib/InPassShadows.glsl new file mode 100755 index 000000000..7fec2be83 --- /dev/null +++ b/jme3-core/src/main/resources/Common/ShaderLib/InPassShadows.glsl @@ -0,0 +1,133 @@ +#ifndef NUM_SHADOW_DIR_LIGHTS +#define NUM_SHADOW_DIR_LIGHTS 0 +#endif +#ifndef NUM_SHADOW_POINT_LIGHTS +#define NUM_SHADOW_POINT_LIGHTS 0 +#endif +#ifndef NUM_SHADOW_SPOT_LIGHTS +#define NUM_SHADOW_SPOT_LIGHTS 0 +#endif +#ifndef NUM_PSSM_SPLITS +#define NUM_PSSM_SPLITS 0 +#endif + +#define SHADOW_DATA_SIZE (NUM_SHADOW_DIR_LIGHTS * NUM_PSSM_SPLITS + NUM_SHADOW_POINT_LIGHTS * 6 + NUM_SHADOW_SPOT_LIGHTS) + +#if SHADOW_DATA_SIZE > 0 + + varying vec4 vProjCoord[SHADOW_DATA_SIZE]; + + #ifdef VERTEX_SHADER + uniform mat4 g_ShadowMatrices[SHADOW_DATA_SIZE]; + + void Shadow_ProcessProjCoord(vec3 worldPos) { + for (int i = 0; i < SHADOW_DATA_SIZE; i++) { + vProjCoord[i] = g_ShadowMatrices[i] * vec4(worldPos, 1.0); + } + } + #else + uniform sampler2DArrayShadow g_ShadowMapArray; + uniform vec4 g_PssmSplits; + + int pssmSliceOffset; + + void Shadow_ProcessPssmSlice() { + #ifdef NUM_PSSM_SPLITS + float z = gl_FragCoord.z; + if (z < g_PssmSplits[0]) { + pssmSliceOffset = 0; + } else if (z < g_PssmSplits[1]) { + pssmSliceOffset = 1; + } else if (z < g_PssmSplits[2]) { + pssmSliceOffset = 2; + } else { + pssmSliceOffset = 3; + } + #else + pssmSliceOffset = 0; + #endif + } + + float Shadow_ProcessDirectional(int startProjIndex, float startArrayLayer) { + float arraySlice = startArrayLayer + float(pssmSliceOffset); + vec3 projCoord = vProjCoord[startProjIndex + pssmSliceOffset].xyz; + return texture(g_ShadowMapArray, vec4(projCoord.xy, arraySlice, projCoord.z)); + } + + float Shadow_ProcessSpot(int startProjIndex, float startArrayLayer) { + vec4 projCoord = vProjCoord[startProjIndex]; + projCoord.xyz /= projCoord.w; + return texture(g_ShadowMapArray, vec4(projCoord.xy, startArrayLayer, projCoord.z)); + } + #endif + +#elif NUM_PSSM_SPLITS > 0 + + // A lightweight version of in-pass lighting that only handles directional lights + // Control flow and loop iteration count are static + + varying vec4 vProjCoord[NUM_PSSM_SPLITS]; + + #ifdef VERTEX_SHADER + uniform mat4 g_DirectionalShadowMatrix[NUM_PSSM_SPLITS]; + void Shadow_ProcessProjCoord(vec3 worldPos) { + for (int i = 0; i < NUM_PSSM_SPLITS; i++) { + vProjCoord[i] = g_DirectionalShadowMatrix[i] * vec4(worldPos, 1.0); + } + } + #else + uniform sampler2DShadow g_DirectionalShadowMap[NUM_PSSM_SPLITS]; + uniform vec4 g_PssmSplits; + + const vec2 invTexSize = vec2(1.0 / 1024.0); + + float Shadow_SampleOffset(sampler2DShadow shadowMap, vec4 projCoord, vec2 offset) { + return shadow2D(shadowMap, vec3(projCoord.xy + offset * invTexSize, projCoord.z)).r; + } + + float Shadow_Sample(sampler2DShadow shadowMap, vec4 projCoord) { + return shadow2D(shadowMap, projCoord.xyz).r; + } + + #define GET_SHADOW(i) if (z < g_PssmSplits[i]) return Shadow_Sample(g_DirectionalShadowMap[i], vProjCoord[i]); + + void Shadow_ProcessPssmSlice() { + } + + float Shadow_ProcessDirectional() { + float z = gl_FragCoord.z; + + GET_SHADOW(0); + #if NUM_PSSM_SPLITS > 1 + GET_SHADOW(1) + #if NUM_PSSM_SPLITS > 2 + GET_SHADOW(2) + #if NUM_PSSM_SPLITS > 3 + GET_SHADOW(3) + #endif + #endif + #endif + + return 1.0; + } + #endif +#else + #define NUM_SHADOW_DIR_LIGHTS 0 + #define NUM_SHADOW_POINT_LIGHTS 0 + #define NUM_SHADOW_SPOT_LIGHTS 0 + #define NUM_PSSM_SPLITS 0 + + void Shadow_ProcessProjCoord(vec3 worldPos) { + } + + void Shadow_ProcessPssmSlice() { + } + + float Shadow_ProcessDirectional(int startLightIndex, float startArrayLayer) { + return 1.0; + } + + float Shadow_ProcessSpot(int startLightIndex, float startArrayLayer) { + return 1.0; + } +#endif diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java index d0c36afc6..7d6184b3f 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java @@ -646,7 +646,7 @@ public class J3MLoader implements AssetLoader { technique.setLogic(new SinglePassLightingLogic(technique)); break; case StaticPass: - technique.setLogic(new StaticPassLightingLogic(technique)); + technique.setLogic(new ShadowStaticPassLightingLogic(technique)); break; case SinglePassAndImageBased: technique.setLogic(new SinglePassAndImageBasedLightingLogic(technique)); diff --git a/jme3-core/src/plugins/java/com/jme3/shader/plugins/GLSLLoader.java b/jme3-core/src/plugins/java/com/jme3/shader/plugins/GLSLLoader.java index f1ef39262..1dc25da8e 100644 --- a/jme3-core/src/plugins/java/com/jme3/shader/plugins/GLSLLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/shader/plugins/GLSLLoader.java @@ -49,7 +49,7 @@ import java.util.*; public class GLSLLoader implements AssetLoader { private AssetManager assetManager; - private Map dependCache = new HashMap(); + private final Map dependCache = new HashMap<>(); /** * Used to load {@link ShaderDependencyNode}s. @@ -168,7 +168,7 @@ public class GLSLLoader implements AssetLoader { return node.getSource(); } else { StringBuilder sb = new StringBuilder(node.getSource()); - List resolvedShaderNodes = new ArrayList(); + List resolvedShaderNodes = new ArrayList<>(); for (ShaderDependencyNode dependencyNode : node.getDependencies()) { resolvedShaderNodes.add(resolveDependencies(dependencyNode, alreadyInjectedSet, extensions)); @@ -187,7 +187,8 @@ public class GLSLLoader implements AssetLoader { // to retrieve the fragment shader, use the content manager this.assetManager = info.getManager(); Reader reader = new InputStreamReader(info.openStream()); - if (info.getKey().getExtension().equals("glsllib")) { + String extension = info.getKey().getExtension(); + if (extension.equals("glsllib") || extension.equals("glsl")) { // NOTE: Loopback, GLSLLIB is loaded by this loader // and needs data as InputStream return reader;