- Basic and PSSM shadow renderer are now deprecated - There is now one processor and its filter conterpart for each light type - created an abstract shadow processor that hold the common shadowing code. It's totally independent of the shadow technique used. - extracted the CompareMode and FilterMode enum to their own files. - renamed FilterMode enum to EdgeFilteringMode - refactored the shader code, to avoid duplicate code. all shadow related code is now gathered into Shadows.glsllib and Shadows15.glsllib. - added spot light Shadows - removed the ShadowCamera class as it was not used. - removed "pssm" in the naming of classes, shader and shader libs since it's not relevant anymore git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9971 75d07b2b-3a1a-0410-a2c5-0572b91ccdca3.0
parent
304ecb4bf7
commit
0dadaa80f5
@ -0,0 +1,12 @@ |
||||
#import "Common/ShaderLib/BasicShadow.glsllib" |
||||
|
||||
uniform SHADOWMAP m_ShadowMap; |
||||
varying vec4 projCoord; |
||||
|
||||
void main() { |
||||
vec4 coord = projCoord; |
||||
coord.xyz /= coord.w; |
||||
float shad = Shadow_GetShadow(m_ShadowMap, coord) * 0.7 + 0.3; |
||||
gl_FragColor = vec4(shad,shad,shad,1.0); |
||||
} |
||||
|
@ -0,0 +1,28 @@ |
||||
MaterialDef Basic Post Shadow { |
||||
|
||||
MaterialParameters { |
||||
Texture2D ShadowMap |
||||
Matrix4 LightViewProjectionMatrix |
||||
} |
||||
|
||||
Technique { |
||||
VertexShader GLSL100: Common/MatDefs/Shadow/BasicPostShadow.vert |
||||
FragmentShader GLSL100: Common/MatDefs/Shadow/BasicPostShadow.frag |
||||
|
||||
WorldParameters { |
||||
WorldViewProjectionMatrix |
||||
WorldMatrix |
||||
} |
||||
|
||||
Defines { |
||||
NO_SHADOW2DPROJ |
||||
} |
||||
|
||||
RenderState { |
||||
Blend Modulate |
||||
DepthWrite Off |
||||
PolyOffset -0.1 0 |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,31 @@ |
||||
uniform mat4 m_LightViewProjectionMatrix; |
||||
uniform mat4 g_WorldViewProjectionMatrix; |
||||
uniform mat4 g_WorldMatrix; |
||||
|
||||
varying vec4 projCoord; |
||||
|
||||
attribute vec3 inPosition; |
||||
|
||||
const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0, |
||||
0.0, 0.5, 0.0, 0.0, |
||||
0.0, 0.0, 0.5, 0.0, |
||||
0.5, 0.5, 0.5, 1.0); |
||||
|
||||
void main(){ |
||||
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0); |
||||
|
||||
// get the vertex in world space |
||||
vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0); |
||||
|
||||
// convert vertex to light viewProj space |
||||
//projCoord = biasMat * (m_LightViewProjectionMatrix * worldPos); |
||||
vec4 coord = m_LightViewProjectionMatrix * worldPos; |
||||
projCoord = biasMat * coord; |
||||
//projCoord.z /= gl_DepthRange.far; |
||||
//projCoord = (m_LightViewProjectionMatrix * worldPos); |
||||
//projCoord /= projCoord.w; |
||||
//projCoord.xy = projCoord.xy * vec2(0.5, -0.5) + vec2(0.5); |
||||
|
||||
// bias from [-1, 1] to [0, 1] for sampling shadow map |
||||
//projCoord = (projCoord.xyzw * vec4(0.5)) + vec4(0.5); |
||||
} |
@ -1,12 +1,72 @@ |
||||
#import "Common/ShaderLib/Shadow.glsllib" |
||||
|
||||
uniform SHADOWMAP m_ShadowMap; |
||||
varying vec4 projCoord; |
||||
|
||||
void main() { |
||||
vec4 coord = projCoord; |
||||
coord.xyz /= coord.w; |
||||
float shad = Shadow_GetShadow(m_ShadowMap, coord) * 0.7 + 0.3; |
||||
gl_FragColor = vec4(shad,shad,shad,1.0); |
||||
} |
||||
|
||||
#import "Common/ShaderLib/Shadows.glsllib" |
||||
|
||||
#ifdef PSSM |
||||
varying float shadowPosition; |
||||
#endif |
||||
|
||||
varying vec4 projCoord0; |
||||
varying vec4 projCoord1; |
||||
varying vec4 projCoord2; |
||||
varying vec4 projCoord3; |
||||
|
||||
#ifdef POINTLIGHT |
||||
varying vec4 projCoord4; |
||||
varying vec4 projCoord5; |
||||
uniform vec3 m_LightPos; |
||||
varying vec4 worldPos; |
||||
#endif |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
#ifdef COLOR_MAP |
||||
uniform sampler2D m_ColorMap; |
||||
#else |
||||
uniform sampler2D m_DiffuseMap; |
||||
#endif |
||||
uniform float m_AlphaDiscardThreshold; |
||||
varying vec2 texCoord; |
||||
#endif |
||||
|
||||
#ifdef FADE |
||||
uniform vec2 m_FadeInfo; |
||||
#endif |
||||
|
||||
void main(){ |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
#ifdef COLOR_MAP |
||||
float alpha = texture2D(m_ColorMap,texCoord).a; |
||||
#else |
||||
float alpha = texture2D(m_DiffuseMap,texCoord).a; |
||||
#endif |
||||
if(alpha<=m_AlphaDiscardThreshold){ |
||||
discard; |
||||
} |
||||
|
||||
#endif |
||||
|
||||
float shadow = 1.0; |
||||
|
||||
#ifdef POINTLIGHT |
||||
shadow = getPointLightShadows(worldPos, m_LightPos, |
||||
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5, |
||||
projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5); |
||||
#else |
||||
#ifdef PSSM |
||||
shadow = getDirectionalLightShadows(m_Splits, shadowPosition, |
||||
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3, |
||||
projCoord0, projCoord1, projCoord2, projCoord3); |
||||
#else |
||||
//spotlight |
||||
shadow = getSpotLightShadows(m_ShadowMap0,projCoord0); |
||||
#endif |
||||
#endif |
||||
|
||||
#ifdef FADE |
||||
shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); |
||||
#endif |
||||
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); |
||||
|
||||
gl_FragColor = vec4(shadow, shadow, shadow, 1.0); |
||||
|
||||
} |
||||
|
||||
|
@ -1,31 +1,72 @@ |
||||
uniform mat4 m_LightViewProjectionMatrix; |
||||
uniform mat4 m_LightViewProjectionMatrix0; |
||||
uniform mat4 m_LightViewProjectionMatrix1; |
||||
uniform mat4 m_LightViewProjectionMatrix2; |
||||
uniform mat4 m_LightViewProjectionMatrix3; |
||||
|
||||
uniform mat4 g_WorldViewProjectionMatrix; |
||||
uniform mat4 g_WorldMatrix; |
||||
uniform mat4 g_ViewMatrix; |
||||
uniform vec3 m_LightPos; |
||||
|
||||
varying vec4 projCoord0; |
||||
varying vec4 projCoord1; |
||||
varying vec4 projCoord2; |
||||
varying vec4 projCoord3; |
||||
|
||||
#ifdef POINTLIGHT |
||||
uniform mat4 m_LightViewProjectionMatrix4; |
||||
uniform mat4 m_LightViewProjectionMatrix5; |
||||
varying vec4 projCoord4; |
||||
varying vec4 projCoord5; |
||||
varying vec4 worldPos; |
||||
#endif |
||||
|
||||
#ifdef PSSM |
||||
varying float shadowPosition; |
||||
#endif |
||||
varying vec3 lightVec; |
||||
|
||||
varying vec4 projCoord; |
||||
varying vec2 texCoord; |
||||
|
||||
attribute vec3 inPosition; |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
attribute vec2 inTexCoord; |
||||
#endif |
||||
|
||||
const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0, |
||||
0.0, 0.5, 0.0, 0.0, |
||||
0.0, 0.0, 0.5, 0.0, |
||||
0.5, 0.5, 0.5, 1.0); |
||||
|
||||
|
||||
void main(){ |
||||
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0); |
||||
|
||||
#ifndef POINTLIGHT |
||||
#ifdef PSSM |
||||
shadowPosition = gl_Position.z; |
||||
#endif |
||||
vec4 worldPos=vec4(0.0); |
||||
#endif |
||||
// get the vertex in world space |
||||
vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0); |
||||
|
||||
// convert vertex to light viewProj space |
||||
//projCoord = biasMat * (m_LightViewProjectionMatrix * worldPos); |
||||
vec4 coord = m_LightViewProjectionMatrix * worldPos; |
||||
projCoord = biasMat * coord; |
||||
//projCoord.z /= gl_DepthRange.far; |
||||
//projCoord = (m_LightViewProjectionMatrix * worldPos); |
||||
//projCoord /= projCoord.w; |
||||
//projCoord.xy = projCoord.xy * vec2(0.5, -0.5) + vec2(0.5); |
||||
|
||||
// bias from [-1, 1] to [0, 1] for sampling shadow map |
||||
//projCoord = (projCoord.xyzw * vec4(0.5)) + vec4(0.5); |
||||
worldPos = g_WorldMatrix * vec4(inPosition, 1.0); |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
texCoord = inTexCoord; |
||||
#endif |
||||
// populate the light view matrices array and convert vertex to light viewProj space |
||||
projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos; |
||||
projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos; |
||||
projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos; |
||||
projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos; |
||||
#ifdef POINTLIGHT |
||||
projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos; |
||||
projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos; |
||||
#else |
||||
|
||||
vec4 vLightPos = g_ViewMatrix * vec4(m_LightPos,1.0); |
||||
vec4 vPos = g_ViewMatrix * worldPos; |
||||
lightVec = vLightPos.xyz - vPos.xyz; |
||||
#endif |
||||
} |
@ -0,0 +1,72 @@ |
||||
#import "Common/ShaderLib/Shadows15.glsllib" |
||||
|
||||
out vec4 outFragColor; |
||||
|
||||
#ifdef PSSM |
||||
in float shadowPosition; |
||||
#endif |
||||
|
||||
in vec4 projCoord0; |
||||
in vec4 projCoord1; |
||||
in vec4 projCoord2; |
||||
in vec4 projCoord3; |
||||
|
||||
#ifdef POINTLIGHT |
||||
in vec4 projCoord4; |
||||
in vec4 projCoord5; |
||||
in vec4 worldPos; |
||||
uniform vec3 m_LightPos; |
||||
#endif |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
#ifdef COLOR_MAP |
||||
uniform sampler2D m_ColorMap; |
||||
#else |
||||
uniform sampler2D m_DiffuseMap; |
||||
#endif |
||||
uniform float m_AlphaDiscardThreshold; |
||||
varying vec2 texCoord; |
||||
#endif |
||||
|
||||
#ifdef FADE |
||||
uniform vec2 m_FadeInfo; |
||||
#endif |
||||
|
||||
void main(){ |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
#ifdef COLOR_MAP |
||||
float alpha = texture2D(m_ColorMap,texCoord).a; |
||||
#else |
||||
float alpha = texture2D(m_DiffuseMap,texCoord).a; |
||||
#endif |
||||
|
||||
if(alpha < m_AlphaDiscardThreshold){ |
||||
discard; |
||||
} |
||||
#endif |
||||
|
||||
float shadow = 1.0; |
||||
#ifdef POINTLIGHT |
||||
shadow = getPointLightShadows(worldPos, m_LightPos, |
||||
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5, |
||||
projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5); |
||||
#else |
||||
#ifdef PSSM |
||||
shadow = getDirectionalLightShadows(m_Splits, shadowPosition, |
||||
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3, |
||||
projCoord0, projCoord1, projCoord2, projCoord3); |
||||
#else |
||||
//spotlight |
||||
shadow = getSpotLightShadows(m_ShadowMap0,projCoord0); |
||||
#endif |
||||
#endif |
||||
|
||||
#ifdef FADE |
||||
shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); |
||||
#endif |
||||
|
||||
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); |
||||
outFragColor = vec4(shadow, shadow, shadow, 1.0); |
||||
} |
||||
|
@ -1,96 +0,0 @@ |
||||
#import "Common/ShaderLib/PssmShadows.glsllib" |
||||
|
||||
#ifdef PSSM |
||||
varying float shadowPosition; |
||||
#endif |
||||
|
||||
varying vec4 projCoord0; |
||||
varying vec4 projCoord1; |
||||
varying vec4 projCoord2; |
||||
varying vec4 projCoord3; |
||||
|
||||
#ifdef POINTLIGHT |
||||
varying vec4 projCoord4; |
||||
varying vec4 projCoord5; |
||||
uniform vec3 m_LightPos; |
||||
varying vec4 worldPos; |
||||
#endif |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
#ifdef COLOR_MAP |
||||
uniform sampler2D m_ColorMap; |
||||
#else |
||||
uniform sampler2D m_DiffuseMap; |
||||
#endif |
||||
uniform float m_AlphaDiscardThreshold; |
||||
varying vec2 texCoord; |
||||
#endif |
||||
|
||||
#ifdef FADE |
||||
uniform vec2 m_FadeInfo; |
||||
#endif |
||||
|
||||
void main(){ |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
#ifdef COLOR_MAP |
||||
float alpha = texture2D(m_ColorMap,texCoord).a; |
||||
#else |
||||
float alpha = texture2D(m_DiffuseMap,texCoord).a; |
||||
#endif |
||||
if(alpha<=m_AlphaDiscardThreshold){ |
||||
discard; |
||||
} |
||||
|
||||
#endif |
||||
|
||||
float shadow = 1.0; |
||||
#ifdef PSSM |
||||
if(shadowPosition < m_Splits.x){ |
||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0); |
||||
}else if( shadowPosition < m_Splits.y){ |
||||
shadowBorderScale = 0.5; |
||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1); |
||||
}else if( shadowPosition < m_Splits.z){ |
||||
shadowBorderScale = 0.25; |
||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2); |
||||
}else if( shadowPosition < m_Splits.w){ |
||||
shadowBorderScale = 0.125; |
||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3); |
||||
} |
||||
#endif |
||||
|
||||
#ifdef POINTLIGHT |
||||
vec3 vect = worldPos.xyz - m_LightPos; |
||||
vec3 absv= abs(vect); |
||||
float maxComp = max(absv.x,max(absv.y,absv.z)); |
||||
if(maxComp == absv.y){ |
||||
if(vect.y < 0.0){ |
||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0); |
||||
}else{ |
||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1); |
||||
} |
||||
}else if(maxComp == absv.z){ |
||||
if(vect.z < 0.0){ |
||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2); |
||||
}else{ |
||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3); |
||||
} |
||||
}else if(maxComp == absv.x){ |
||||
if(vect.x < 0.0){ |
||||
shadow = GETSHADOW(m_ShadowMap4, projCoord4); |
||||
}else{ |
||||
shadow = GETSHADOW(m_ShadowMap5, projCoord5); |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#ifdef FADE |
||||
shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); |
||||
#endif |
||||
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); |
||||
|
||||
gl_FragColor = vec4(shadow, shadow, shadow, 1.0); |
||||
|
||||
} |
||||
|
@ -1,85 +0,0 @@ |
||||
MaterialDef Post Shadow { |
||||
|
||||
MaterialParameters { |
||||
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 |
||||
|
||||
Float PCFEdge |
||||
|
||||
Float ShadowMapSize |
||||
} |
||||
|
||||
Technique { |
||||
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM.vert |
||||
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM15.frag |
||||
|
||||
WorldParameters { |
||||
WorldViewProjectionMatrix |
||||
WorldMatrix |
||||
} |
||||
|
||||
Defines { |
||||
HARDWARE_SHADOWS : HardwareShadows |
||||
FILTER_MODE : FilterMode |
||||
PCFEDGE : PCFEdge |
||||
SHADOWMAP_SIZE : ShadowMapSize |
||||
FADE : FadeInfo |
||||
PSSM : Splits |
||||
POINTLIGHT : LightViewProjectionMatrix5 |
||||
} |
||||
|
||||
RenderState { |
||||
Blend Modulate |
||||
DepthWrite Off |
||||
PolyOffset -0.1 0 |
||||
} |
||||
} |
||||
|
||||
Technique { |
||||
VertexShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.vert |
||||
FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.frag |
||||
|
||||
WorldParameters { |
||||
WorldViewProjectionMatrix |
||||
WorldMatrix |
||||
} |
||||
|
||||
Defines { |
||||
HARDWARE_SHADOWS : HardwareShadows |
||||
FILTER_MODE : FilterMode |
||||
PCFEDGE : PCFEdge |
||||
SHADOWMAP_SIZE : ShadowMapSize |
||||
FADE : FadeInfo |
||||
PSSM : Splits |
||||
POINTLIGHT : LightViewProjectionMatrix5 |
||||
} |
||||
|
||||
RenderState { |
||||
Blend Modulate |
||||
DepthWrite Off |
||||
PolyOffset -0.1 0 |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,62 +0,0 @@ |
||||
uniform mat4 m_LightViewProjectionMatrix0; |
||||
uniform mat4 m_LightViewProjectionMatrix1; |
||||
uniform mat4 m_LightViewProjectionMatrix2; |
||||
uniform mat4 m_LightViewProjectionMatrix3; |
||||
|
||||
uniform mat4 g_WorldViewProjectionMatrix; |
||||
uniform mat4 g_WorldMatrix; |
||||
|
||||
varying vec4 projCoord0; |
||||
varying vec4 projCoord1; |
||||
varying vec4 projCoord2; |
||||
varying vec4 projCoord3; |
||||
|
||||
#ifdef POINTLIGHT |
||||
uniform mat4 m_LightViewProjectionMatrix4; |
||||
uniform mat4 m_LightViewProjectionMatrix5; |
||||
varying vec4 projCoord4; |
||||
varying vec4 projCoord5; |
||||
varying vec4 worldPos; |
||||
#endif |
||||
|
||||
#ifdef PSSM |
||||
varying float shadowPosition; |
||||
#endif |
||||
|
||||
varying vec2 texCoord; |
||||
|
||||
attribute vec3 inPosition; |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
attribute vec2 inTexCoord; |
||||
#endif |
||||
|
||||
const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0, |
||||
0.0, 0.5, 0.0, 0.0, |
||||
0.0, 0.0, 0.5, 0.0, |
||||
0.5, 0.5, 0.5, 1.0); |
||||
|
||||
|
||||
void main(){ |
||||
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0); |
||||
|
||||
#ifdef PSSM |
||||
shadowPosition = gl_Position.z; |
||||
vec4 worldPos=vec4(0.0); |
||||
#endif |
||||
// get the vertex in world space |
||||
worldPos = g_WorldMatrix * vec4(inPosition, 1.0); |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
texCoord = inTexCoord; |
||||
#endif |
||||
// populate the light view matrices array and convert vertex to light viewProj space |
||||
projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos; |
||||
projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos; |
||||
projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos; |
||||
projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos; |
||||
#ifdef POINTLIGHT |
||||
projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos; |
||||
projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos; |
||||
#endif |
||||
} |
@ -1,105 +0,0 @@ |
||||
#import "Common/ShaderLib/PssmShadows15.glsllib" |
||||
|
||||
out vec4 outFragColor; |
||||
|
||||
#ifdef PSSM |
||||
in float shadowPosition; |
||||
#endif |
||||
|
||||
in vec4 projCoord0; |
||||
in vec4 projCoord1; |
||||
in vec4 projCoord2; |
||||
in vec4 projCoord3; |
||||
|
||||
#ifdef POINTLIGHT |
||||
in vec4 projCoord4; |
||||
in vec4 projCoord5; |
||||
uniform vec3 m_LightPos; |
||||
in vec4 worldPos; |
||||
#endif |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
#ifdef COLOR_MAP |
||||
uniform sampler2D m_ColorMap; |
||||
#else |
||||
uniform sampler2D m_DiffuseMap; |
||||
#endif |
||||
uniform float m_AlphaDiscardThreshold; |
||||
varying vec2 texCoord; |
||||
#endif |
||||
|
||||
#ifdef FADE |
||||
uniform vec2 m_FadeInfo; |
||||
#endif |
||||
|
||||
void main(){ |
||||
|
||||
#ifdef DISCARD_ALPHA |
||||
#ifdef COLOR_MAP |
||||
float alpha = texture2D(m_ColorMap,texCoord).a; |
||||
#else |
||||
float alpha = texture2D(m_DiffuseMap,texCoord).a; |
||||
#endif |
||||
|
||||
if(alpha < m_AlphaDiscardThreshold){ |
||||
discard; |
||||
} |
||||
#endif |
||||
|
||||
float shadow = 1.0; |
||||
#ifdef PSSM |
||||
if(shadowPosition < m_Splits.x){ |
||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0); |
||||
}else if( shadowPosition < m_Splits.y){ |
||||
shadowBorderScale = 0.5; |
||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1); |
||||
}else if( shadowPosition < m_Splits.z){ |
||||
shadowBorderScale = 0.25; |
||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2); |
||||
}else if( shadowPosition < m_Splits.w){ |
||||
shadowBorderScale = 0.125; |
||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3); |
||||
} |
||||
#endif |
||||
|
||||
|
||||
#ifdef POINTLIGHT |
||||
vec3 vect = worldPos.xyz - m_LightPos; |
||||
vec3 absv= abs(vect); |
||||
float maxComp = max(absv.x,max(absv.y,absv.z)); |
||||
if(maxComp == absv.y){ |
||||
if(vect.y < 0.0){ |
||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0); |
||||
outFragColor = vec4(projCoord0.z); |
||||
}else{ |
||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1); |
||||
outFragColor = vec4(projCoord1.z); |
||||
} |
||||
}else if(maxComp == absv.z){ |
||||
if(vect.z < 0.0){ |
||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2); |
||||
outFragColor =vec4(projCoord2.z); |
||||
}else{ |
||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3); |
||||
outFragColor = vec4(projCoord3.z); |
||||
} |
||||
}else if(maxComp == absv.x){ |
||||
if(vect.x < 0.0){ |
||||
shadow = GETSHADOW(m_ShadowMap4, projCoord4); |
||||
outFragColor = vec4(projCoord4.z); |
||||
}else{ |
||||
shadow = GETSHADOW(m_ShadowMap5, projCoord5); |
||||
outFragColor = vec4(projCoord5.z); |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
#ifdef FADE |
||||
shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); |
||||
#endif |
||||
|
||||
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity); |
||||
outFragColor = vec4(shadow, shadow, shadow, 1.0); |
||||
|
||||
} |
||||
|
@ -0,0 +1,233 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 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 com.jme3.shadow; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.export.InputCapsule; |
||||
import com.jme3.export.JmeExporter; |
||||
import com.jme3.export.JmeImporter; |
||||
import com.jme3.export.OutputCapsule; |
||||
import com.jme3.material.Material; |
||||
import com.jme3.math.Matrix4f; |
||||
import com.jme3.math.Vector4f; |
||||
import com.jme3.post.Filter; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.RenderQueue; |
||||
import com.jme3.texture.FrameBuffer; |
||||
import java.io.IOException; |
||||
|
||||
/** |
||||
* |
||||
* Generic abstract filter that holds common implementations for the different |
||||
* shadow filtesr |
||||
* |
||||
* @author Rémy Bouquet aka Nehon |
||||
*/ |
||||
public abstract class AbstractShadowFilter<T extends AbstractShadowRenderer> extends Filter { |
||||
|
||||
protected T shadowRenderer; |
||||
protected ViewPort viewPort; |
||||
|
||||
/** |
||||
* Abstract class constructor |
||||
* |
||||
* @param manager the application asset manager |
||||
* @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, |
||||
* etc...) |
||||
* @param nbShadowMaps the number of shadow maps rendered (the more shadow |
||||
* maps the more quality, the less fps). |
||||
* @param shadowRenderer the shadowRenderer to use for this Filter |
||||
*/ |
||||
@SuppressWarnings("all") |
||||
protected AbstractShadowFilter(AssetManager manager, int shadowMapSize, T shadowRenderer) { |
||||
super("Post Shadow"); |
||||
material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md"); |
||||
this.shadowRenderer = shadowRenderer; |
||||
this.shadowRenderer.setPostShadowMaterial(material); |
||||
} |
||||
|
||||
@Override |
||||
protected Material getMaterial() { |
||||
return material; |
||||
} |
||||
|
||||
@Override |
||||
protected boolean isRequiresDepthTexture() { |
||||
return true; |
||||
} |
||||
|
||||
public Material getShadowMaterial() { |
||||
return material; |
||||
} |
||||
Vector4f tmpv = new Vector4f(); |
||||
|
||||
@Override |
||||
protected void preFrame(float tpf) { |
||||
shadowRenderer.preFrame(tpf); |
||||
material.setMatrix4("ViewProjectionMatrixInverse", viewPort.getCamera().getViewProjectionMatrix().invert()); |
||||
Matrix4f m = viewPort.getCamera().getViewProjectionMatrix(); |
||||
material.setVector4("ViewProjectionMatrixRow2", tmpv.set(m.m20, m.m21, m.m22, m.m23)); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
protected void postQueue(RenderQueue queue) { |
||||
shadowRenderer.postQueue(queue); |
||||
} |
||||
|
||||
@Override |
||||
protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { |
||||
shadowRenderer.setPostShadowParams(); |
||||
} |
||||
|
||||
@Override |
||||
protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { |
||||
shadowRenderer.needsfallBackMaterial = true; |
||||
shadowRenderer.initialize(renderManager, vp); |
||||
this.viewPort = vp; |
||||
} |
||||
|
||||
/** |
||||
* returns the shdaow intensity |
||||
* |
||||
* @see #setShadowIntensity(float shadowIntensity) |
||||
* @return shadowIntensity |
||||
*/ |
||||
public float getShadowIntensity() { |
||||
return shadowRenderer.getShadowIntensity(); |
||||
} |
||||
|
||||
/** |
||||
* Set the shadowIntensity, the value should be between 0 and 1, a 0 value |
||||
* gives a bright and invisilble shadow, a 1 value gives a pitch black |
||||
* shadow, default is 0.7 |
||||
* |
||||
* @param shadowIntensity the darkness of the shadow |
||||
*/ |
||||
final public void setShadowIntensity(float shadowIntensity) { |
||||
shadowRenderer.setShadowIntensity(shadowIntensity); |
||||
} |
||||
|
||||
/** |
||||
* returns the edges thickness <br> |
||||
* |
||||
* @see #setEdgesThickness(int edgesThickness) |
||||
* @return edgesThickness |
||||
*/ |
||||
public int getEdgesThickness() { |
||||
return shadowRenderer.getEdgesThickness(); |
||||
} |
||||
|
||||
/** |
||||
* Sets the shadow edges thickness. default is 1, setting it to lower values |
||||
* can help to reduce the jagged effect of the shadow edges |
||||
* |
||||
* @param edgesThickness |
||||
*/ |
||||
public void setEdgesThickness(int edgesThickness) { |
||||
shadowRenderer.setEdgesThickness(edgesThickness); |
||||
} |
||||
|
||||
/** |
||||
* returns true if the PssmRenderer flushed the shadow queues |
||||
* |
||||
* @return flushQueues |
||||
*/ |
||||
public boolean isFlushQueues() { |
||||
return shadowRenderer.isFlushQueues(); |
||||
} |
||||
|
||||
/** |
||||
* Set this to false if you want to use several PssmRederers to have |
||||
* multiple shadows cast by multiple light sources. Make sure the last |
||||
* PssmRenderer in the stack DO flush the queues, but not the others |
||||
* |
||||
* @param flushQueues |
||||
*/ |
||||
public void setFlushQueues(boolean flushQueues) { |
||||
shadowRenderer.setFlushQueues(flushQueues); |
||||
} |
||||
|
||||
/** |
||||
* sets the shadow compare mode see {@link CompareMode} for more info |
||||
* |
||||
* @param compareMode |
||||
*/ |
||||
final public void setShadowCompareMode(CompareMode compareMode) { |
||||
shadowRenderer.setShadowCompareMode(compareMode); |
||||
} |
||||
|
||||
/** |
||||
* returns the shadow compare mode |
||||
* |
||||
* @see CompareMode |
||||
* @return the shadowCompareMode |
||||
*/ |
||||
public CompareMode getShadowCompareMode() { |
||||
return shadowRenderer.getShadowCompareMode(); |
||||
} |
||||
|
||||
/** |
||||
* Sets the filtering mode for shadow edges see {@link EdgeFilteringMode} |
||||
* for more info |
||||
* |
||||
* @param filterMode |
||||
*/ |
||||
final public void setEdgeFilteringMode(EdgeFilteringMode filterMode) { |
||||
shadowRenderer.setEdgeFilteringMode(filterMode); |
||||
} |
||||
|
||||
/** |
||||
* returns the the edge filtering mode |
||||
* |
||||
* @see EdgeFilteringMode |
||||
* @return |
||||
*/ |
||||
public EdgeFilteringMode getEdgeFilteringMode() { |
||||
return shadowRenderer.getEdgeFilteringMode(); |
||||
} |
||||
|
||||
@Override |
||||
public void write(JmeExporter ex) throws IOException { |
||||
super.write(ex); |
||||
OutputCapsule oc = ex.getCapsule(this); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void read(JmeImporter im) throws IOException { |
||||
super.read(im); |
||||
InputCapsule ic = im.getCapsule(this); |
||||
|
||||
} |
||||
} |
@ -0,0 +1,563 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 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 com.jme3.shadow; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.material.Material; |
||||
import com.jme3.math.ColorRGBA; |
||||
import com.jme3.math.Matrix4f; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.post.SceneProcessor; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.Caps; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.Renderer; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.renderer.queue.RenderQueue; |
||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.debug.WireFrustum; |
||||
import com.jme3.shadow.PssmShadowRenderer.FilterMode; |
||||
import com.jme3.texture.FrameBuffer; |
||||
import com.jme3.texture.Image.Format; |
||||
import com.jme3.texture.Texture.MagFilter; |
||||
import com.jme3.texture.Texture.MinFilter; |
||||
import com.jme3.texture.Texture.ShadowCompareMode; |
||||
import com.jme3.texture.Texture2D; |
||||
import com.jme3.ui.Picture; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* abstract shadow renderer that holds commons feature to have for a shadow renderer |
||||
* @author Rémy Bouquet aka Nehon |
||||
*/ |
||||
public abstract class AbstractShadowRenderer implements SceneProcessor { |
||||
|
||||
protected int nbShadowMaps = 1; |
||||
protected float shadowMapSize; |
||||
protected float shadowIntensity = 0.7f; |
||||
protected RenderManager renderManager; |
||||
protected ViewPort viewPort; |
||||
protected FrameBuffer[] shadowFB; |
||||
protected Texture2D[] shadowMaps; |
||||
protected Texture2D dummyTex; |
||||
protected Material preshadowMat; |
||||
protected Material postshadowMat; |
||||
protected Matrix4f[] lightViewProjectionsMatrices; |
||||
protected boolean noOccluders = false; |
||||
protected AssetManager assetManager; |
||||
protected boolean debug = false; |
||||
protected float edgesThickness = 1.0f; |
||||
protected EdgeFilteringMode edgeFilteringMode; |
||||
protected CompareMode shadowCompareMode; |
||||
protected Picture[] dispPic; |
||||
protected boolean flushQueues = true; |
||||
// define if the fallback material should be used.
|
||||
protected boolean needsfallBackMaterial = false; |
||||
//Name of the post material technique
|
||||
protected String postTechniqueName = "PostShadow"; |
||||
//flags to know when to change params in the materials
|
||||
//a list of material of the post shadow queue geometries.
|
||||
protected List<Material> matCache = new ArrayList<Material>(); |
||||
|
||||
/** |
||||
* Create an abstract shadow renderer, this is to be called in extending classes |
||||
* @param assetManager the application asset manager |
||||
* @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, |
||||
* etc...) |
||||
* @param nbShadowMaps the number of shadow maps rendered (the more shadow |
||||
* maps the more quality, the less fps). |
||||
*/ |
||||
protected AbstractShadowRenderer(AssetManager assetManager, int shadowMapSize, int nbShadowMaps) { |
||||
|
||||
this.assetManager = assetManager; |
||||
this.postshadowMat = new Material(assetManager, "Common/MatDefs/Shadow/PostShadow.j3md"); |
||||
this.nbShadowMaps = nbShadowMaps; |
||||
this.shadowMapSize = shadowMapSize; |
||||
shadowFB = new FrameBuffer[nbShadowMaps]; |
||||
shadowMaps = new Texture2D[nbShadowMaps]; |
||||
dispPic = new Picture[nbShadowMaps]; |
||||
lightViewProjectionsMatrices = new Matrix4f[nbShadowMaps]; |
||||
|
||||
//DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
|
||||
dummyTex = new Texture2D(shadowMapSize, shadowMapSize, Format.RGBA8); |
||||
|
||||
preshadowMat = new Material(assetManager, "Common/MatDefs/Shadow/PreShadow.j3md"); |
||||
postshadowMat.setFloat("ShadowMapSize", shadowMapSize); |
||||
|
||||
for (int i = 0; i < nbShadowMaps; i++) { |
||||
lightViewProjectionsMatrices[i] = new Matrix4f(); |
||||
shadowFB[i] = new FrameBuffer(shadowMapSize, shadowMapSize, 1); |
||||
shadowMaps[i] = new Texture2D(shadowMapSize, shadowMapSize, Format.Depth); |
||||
|
||||
shadowFB[i].setDepthTexture(shadowMaps[i]); |
||||
|
||||
//DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
|
||||
shadowFB[i].setColorTexture(dummyTex); |
||||
|
||||
postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]); |
||||
|
||||
//quads for debuging purpose
|
||||
dispPic[i] = new Picture("Picture" + i); |
||||
dispPic[i].setTexture(assetManager, shadowMaps[i], false); |
||||
} |
||||
|
||||
setShadowCompareMode(CompareMode.Hardware); |
||||
setEdgeFilteringMode(EdgeFilteringMode.Bilinear); |
||||
setShadowIntensity(0.7f); |
||||
|
||||
} |
||||
|
||||
/** |
||||
* set the post shadow material for this renderer |
||||
* @param postShadowMat |
||||
*/ |
||||
protected final void setPostShadowMaterial(Material postShadowMat) { |
||||
this.postshadowMat = postShadowMat; |
||||
postshadowMat.setFloat("ShadowMapSize", shadowMapSize); |
||||
for (int i = 0; i < nbShadowMaps; i++) { |
||||
postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]); |
||||
} |
||||
setShadowCompareMode(shadowCompareMode); |
||||
setEdgeFilteringMode(edgeFilteringMode); |
||||
setShadowIntensity(shadowIntensity); |
||||
} |
||||
|
||||
/** |
||||
* Sets the filtering mode for shadow edges see {@link FilterMode} for more |
||||
* info |
||||
* |
||||
* @param filterMode |
||||
*/ |
||||
final public void setEdgeFilteringMode(EdgeFilteringMode filterMode) { |
||||
if (filterMode == null) { |
||||
throw new NullPointerException(); |
||||
} |
||||
|
||||
if (this.edgeFilteringMode == filterMode) { |
||||
return; |
||||
} |
||||
|
||||
this.edgeFilteringMode = filterMode; |
||||
postshadowMat.setInt("FilterMode", filterMode.getMaterialParamValue()); |
||||
postshadowMat.setFloat("PCFEdge", edgesThickness); |
||||
if (shadowCompareMode == CompareMode.Hardware) { |
||||
for (Texture2D shadowMap : shadowMaps) { |
||||
if (filterMode == EdgeFilteringMode.Bilinear) { |
||||
shadowMap.setMagFilter(MagFilter.Bilinear); |
||||
shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps); |
||||
} else { |
||||
shadowMap.setMagFilter(MagFilter.Nearest); |
||||
shadowMap.setMinFilter(MinFilter.NearestNoMipMaps); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* returns the the edge filtering mode |
||||
* |
||||
* @see EdgeFilteringMode |
||||
* @return |
||||
*/ |
||||
public EdgeFilteringMode getEdgeFilteringMode() { |
||||
return edgeFilteringMode; |
||||
} |
||||
|
||||
/** |
||||
* sets the shadow compare mode see {@link CompareMode} for more info |
||||
* |
||||
* @param compareMode |
||||
*/ |
||||
final public void setShadowCompareMode(CompareMode compareMode) { |
||||
if (compareMode == null) { |
||||
throw new NullPointerException(); |
||||
} |
||||
|
||||
if (this.shadowCompareMode == compareMode) { |
||||
return; |
||||
} |
||||
|
||||
this.shadowCompareMode = compareMode; |
||||
for (Texture2D shadowMap : shadowMaps) { |
||||
if (compareMode == CompareMode.Hardware) { |
||||
shadowMap.setShadowCompareMode(ShadowCompareMode.LessOrEqual); |
||||
if (edgeFilteringMode == EdgeFilteringMode.Bilinear) { |
||||
shadowMap.setMagFilter(MagFilter.Bilinear); |
||||
shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps); |
||||
} else { |
||||
shadowMap.setMagFilter(MagFilter.Nearest); |
||||
shadowMap.setMinFilter(MinFilter.NearestNoMipMaps); |
||||
} |
||||
} else { |
||||
shadowMap.setShadowCompareMode(ShadowCompareMode.Off); |
||||
shadowMap.setMagFilter(MagFilter.Nearest); |
||||
shadowMap.setMinFilter(MinFilter.NearestNoMipMaps); |
||||
} |
||||
} |
||||
postshadowMat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware); |
||||
} |
||||
|
||||
/** |
||||
* returns the shadow compare mode |
||||
* |
||||
* @see CompareMode |
||||
* @return the shadowCompareMode |
||||
*/ |
||||
public CompareMode getShadowCompareMode() { |
||||
return shadowCompareMode; |
||||
} |
||||
|
||||
//debug function that create a displayable frustrum
|
||||
protected Geometry createFrustum(Vector3f[] pts, int i) { |
||||
WireFrustum frustum = new WireFrustum(pts); |
||||
Geometry frustumMdl = new Geometry("f", frustum); |
||||
frustumMdl.setCullHint(Spatial.CullHint.Never); |
||||
frustumMdl.setShadowMode(ShadowMode.Off); |
||||
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||
mat.getAdditionalRenderState().setWireframe(true); |
||||
frustumMdl.setMaterial(mat); |
||||
switch (i) { |
||||
case 0: |
||||
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Pink); |
||||
break; |
||||
case 1: |
||||
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Red); |
||||
break; |
||||
case 2: |
||||
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Green); |
||||
break; |
||||
case 3: |
||||
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Blue); |
||||
break; |
||||
default: |
||||
frustumMdl.getMaterial().setColor("Color", ColorRGBA.White); |
||||
break; |
||||
} |
||||
|
||||
frustumMdl.updateGeometricState(); |
||||
return frustumMdl; |
||||
} |
||||
|
||||
public void initialize(RenderManager rm, ViewPort vp) { |
||||
renderManager = rm; |
||||
viewPort = vp; |
||||
//checking for caps to chosse the appropriate post material technique
|
||||
if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) { |
||||
postTechniqueName = "PostShadow15"; |
||||
} else { |
||||
postTechniqueName = "PostShadow"; |
||||
} |
||||
} |
||||
|
||||
public boolean isInitialized() { |
||||
return viewPort != null; |
||||
} |
||||
|
||||
/** |
||||
* This mehtod is called once per frame. |
||||
* it is responsible for updating the shadow cams according to the light view. |
||||
* @param viewCam the scene cam |
||||
*/ |
||||
protected abstract void updateShadowCams(Camera viewCam); |
||||
|
||||
/** |
||||
* this method must return the geomtryList that contains the oclluders to be rendered in the shadow map |
||||
* @param shadowMapIndex the index of the shadow map being rendered |
||||
* @param sceneOccluders the occluders of the whole scene |
||||
* @param sceneReceivers the recievers of the whole scene |
||||
* @return |
||||
*/ |
||||
protected abstract GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers); |
||||
|
||||
/** |
||||
* return the shadow camera to use for rendering the shadow map according the given index |
||||
* @param shadowMapIndex the index of the shadow map being rendered |
||||
* @return the shadowCam |
||||
*/ |
||||
protected abstract Camera getShadowCam(int shadowMapIndex); |
||||
|
||||
/** |
||||
* responsible for displaying the frustum of the shadow cam for debug purpose |
||||
* @param shadowMapIndex |
||||
*/ |
||||
protected void doDisplayFrustumDebug(int shadowMapIndex) { |
||||
} |
||||
|
||||
@SuppressWarnings("fallthrough") |
||||
public void postQueue(RenderQueue rq) { |
||||
GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast); |
||||
GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive); |
||||
if (receivers.size() == 0 || occluders.size() == 0) { |
||||
return; |
||||
} |
||||
|
||||
updateShadowCams(viewPort.getCamera()); |
||||
|
||||
Renderer r = renderManager.getRenderer(); |
||||
renderManager.setForcedMaterial(preshadowMat); |
||||
renderManager.setForcedTechnique("PreShadow"); |
||||
|
||||
for (int shadowMapIndex = 0; shadowMapIndex < nbShadowMaps; shadowMapIndex++) { |
||||
|
||||
if (debugfrustums) { |
||||
doDisplayFrustumDebug(shadowMapIndex); |
||||
} |
||||
renderShadowMap(shadowMapIndex, occluders, receivers); |
||||
|
||||
} |
||||
|
||||
debugfrustums = false; |
||||
if (flushQueues) { |
||||
occluders.clear(); |
||||
} |
||||
//restore setting for future rendering
|
||||
r.setFrameBuffer(viewPort.getOutputFrameBuffer()); |
||||
renderManager.setForcedMaterial(null); |
||||
renderManager.setForcedTechnique(null); |
||||
renderManager.setCamera(viewPort.getCamera(), false); |
||||
|
||||
} |
||||
|
||||
protected void renderShadowMap(int shadowMapIndex, GeometryList occluders, GeometryList receivers) { |
||||
GeometryList mapOccluders = getOccludersToRender(shadowMapIndex, occluders, receivers); |
||||
Camera shadowCam = getShadowCam(shadowMapIndex); |
||||
|
||||
//saving light view projection matrix for this split
|
||||
lightViewProjectionsMatrices[shadowMapIndex].set(shadowCam.getViewProjectionMatrix()); |
||||
renderManager.setCamera(shadowCam, false); |
||||
|
||||
renderManager.getRenderer().setFrameBuffer(shadowFB[shadowMapIndex]); |
||||
renderManager.getRenderer().clearBuffers(false, true, false); |
||||
|
||||
// render shadow casters to shadow map
|
||||
viewPort.getQueue().renderShadowQueue(mapOccluders, renderManager, shadowCam, true); |
||||
} |
||||
boolean debugfrustums = false; |
||||
|
||||
public void displayFrustum() { |
||||
debugfrustums = true; |
||||
} |
||||
|
||||
//debug only : displays depth shadow maps
|
||||
protected void displayShadowMap(Renderer r) { |
||||
Camera cam = viewPort.getCamera(); |
||||
renderManager.setCamera(cam, true); |
||||
int h = cam.getHeight(); |
||||
for (int i = 0; i < dispPic.length; i++) { |
||||
dispPic[i].setPosition((128 * i) + (150 + 64 * (i + 1)), h / 20f); |
||||
dispPic[i].setWidth(128); |
||||
dispPic[i].setHeight(128); |
||||
dispPic[i].updateGeometricState(); |
||||
renderManager.renderGeometry(dispPic[i]); |
||||
} |
||||
renderManager.setCamera(cam, false); |
||||
} |
||||
|
||||
/** |
||||
* For dubuging purpose Allow to "snapshot" the current frustrum to the |
||||
* scene |
||||
*/ |
||||
public void displayDebug() { |
||||
debug = true; |
||||
} |
||||
|
||||
public void postFrame(FrameBuffer out) { |
||||
|
||||
if (debug) { |
||||
displayShadowMap(renderManager.getRenderer()); |
||||
} |
||||
if (!noOccluders) { |
||||
//setting params to recieving geometry list
|
||||
setMatParams(); |
||||
|
||||
Camera cam = viewPort.getCamera(); |
||||
//some materials in the scene does not have a post shadow technique so we're using the fall back material
|
||||
if (needsfallBackMaterial) { |
||||
renderManager.setForcedMaterial(postshadowMat); |
||||
} |
||||
|
||||
//forcing the post shadow technique and render state
|
||||
renderManager.setForcedTechnique(postTechniqueName); |
||||
|
||||
//rendering the post shadow pass
|
||||
viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, cam, flushQueues); |
||||
|
||||
//resetting renderManager settings
|
||||
renderManager.setForcedTechnique(null); |
||||
renderManager.setForcedMaterial(null); |
||||
renderManager.setCamera(cam, false); |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* This method is called once per frame and is responsible of setting the material |
||||
* parameters than sub class may need to set on the post material |
||||
* @param material the materail to use for the post shadow pass |
||||
*/ |
||||
protected abstract void setMaterialParameters(Material material); |
||||
|
||||
private void setMatParams() { |
||||
|
||||
GeometryList l = viewPort.getQueue().getShadowQueueContent(ShadowMode.Receive); |
||||
|
||||
//iteration throught all the geometries of the list to gather the materials
|
||||
|
||||
matCache.clear(); |
||||
for (int i = 0; i < l.size(); i++) { |
||||
Material mat = l.get(i).getMaterial(); |
||||
//checking if the material has the post technique and adding it to the material cache
|
||||
if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) { |
||||
if (!matCache.contains(mat)) { |
||||
matCache.add(mat); |
||||
} |
||||
} else { |
||||
needsfallBackMaterial = true; |
||||
} |
||||
} |
||||
|
||||
//iterating through the mat cache and setting the parameters
|
||||
for (Material mat : matCache) { |
||||
|
||||
mat.setFloat("ShadowMapSize", shadowMapSize); |
||||
|
||||
for (int j = 0; j < nbShadowMaps; j++) { |
||||
mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]); |
||||
} |
||||
for (int j = 0; j < nbShadowMaps; j++) { |
||||
mat.setTexture("ShadowMap" + j, shadowMaps[j]); |
||||
} |
||||
mat.setBoolean("HardwareShadows", shadowCompareMode == CompareMode.Hardware); |
||||
mat.setInt("FilterMode", edgeFilteringMode.getMaterialParamValue()); |
||||
mat.setFloat("PCFEdge", edgesThickness); |
||||
mat.setFloat("ShadowIntensity", shadowIntensity); |
||||
|
||||
setMaterialParameters(mat); |
||||
} |
||||
|
||||
//At least one material of the receiving geoms does not support the post shadow techniques
|
||||
//so we fall back to the forced material solution (transparent shadows won't be supported for these objects)
|
||||
if (needsfallBackMaterial) { |
||||
setPostShadowParams(); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* for internal use only |
||||
*/ |
||||
protected void setPostShadowParams() { |
||||
setMaterialParameters(postshadowMat); |
||||
for (int j = 0; j < nbShadowMaps; j++) { |
||||
postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]); |
||||
postshadowMat.setTexture("ShadowMap" + j, shadowMaps[j]); |
||||
} |
||||
} |
||||
|
||||
public void preFrame(float tpf) { |
||||
} |
||||
|
||||
public void cleanup() { |
||||
} |
||||
|
||||
public void reshape(ViewPort vp, int w, int h) { |
||||
} |
||||
|
||||
/** |
||||
* returns the shdaow intensity |
||||
* |
||||
* @see #setShadowIntensity(float shadowIntensity) |
||||
* @return shadowIntensity |
||||
*/ |
||||
public float getShadowIntensity() { |
||||
return shadowIntensity; |
||||
} |
||||
|
||||
/** |
||||
* Set the shadowIntensity, the value should be between 0 and 1, a 0 value |
||||
* gives a bright and invisilble shadow, a 1 value gives a pitch black |
||||
* shadow, default is 0.7 |
||||
* |
||||
* @param shadowIntensity the darkness of the shadow |
||||
*/ |
||||
final public void setShadowIntensity(float shadowIntensity) { |
||||
this.shadowIntensity = shadowIntensity; |
||||
postshadowMat.setFloat("ShadowIntensity", shadowIntensity); |
||||
} |
||||
|
||||
/** |
||||
* returns the edges thickness |
||||
* |
||||
* @see #setEdgesThickness(int edgesThickness) |
||||
* @return edgesThickness |
||||
*/ |
||||
public int getEdgesThickness() { |
||||
return (int) (edgesThickness * 10); |
||||
} |
||||
|
||||
/** |
||||
* Sets the shadow edges thickness. default is 1, setting it to lower values |
||||
* can help to reduce the jagged effect of the shadow edges |
||||
* |
||||
* @param edgesThickness |
||||
*/ |
||||
public void setEdgesThickness(int edgesThickness) { |
||||
this.edgesThickness = Math.max(1, Math.min(edgesThickness, 10)); |
||||
this.edgesThickness *= 0.1f; |
||||
postshadowMat.setFloat("PCFEdge", edgesThickness); |
||||
} |
||||
|
||||
/** |
||||
* returns true if the PssmRenderer flushed the shadow queues |
||||
* |
||||
* @return flushQueues |
||||
*/ |
||||
public boolean isFlushQueues() { |
||||
return flushQueues; |
||||
} |
||||
|
||||
/** |
||||
* Set this to false if you want to use several PssmRederers to have |
||||
* multiple shadows cast by multiple light sources. Make sure the last |
||||
* PssmRenderer in the stack DO flush the queues, but not the others |
||||
* |
||||
* @param flushQueues |
||||
*/ |
||||
public void setFlushQueues(boolean flushQueues) { |
||||
this.flushQueues = flushQueues; |
||||
} |
||||
} |
@ -0,0 +1,48 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 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 com.jme3.shadow; |
||||
|
||||
/** |
||||
* Specifies the shadow comparison mode |
||||
*/ |
||||
public enum CompareMode { |
||||
|
||||
/** |
||||
* Shadow depth comparisons are done by using shader code |
||||
*/ |
||||
Software, |
||||
/** |
||||
* Shadow depth comparisons are done by using the GPU's dedicated shadowing |
||||
* pipeline. |
||||
*/ |
||||
Hardware; |
||||
} |
@ -0,0 +1,174 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 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 com.jme3.shadow; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.export.InputCapsule; |
||||
import com.jme3.export.JmeExporter; |
||||
import com.jme3.export.JmeImporter; |
||||
import com.jme3.export.OutputCapsule; |
||||
import com.jme3.light.DirectionalLight; |
||||
import com.jme3.material.Material; |
||||
import java.io.IOException; |
||||
|
||||
/** |
||||
* |
||||
* This Filter does basically the same as a DirectionalLightShadowRenderer |
||||
* except it renders the post shadow pass as a fulscreen quad pass instead of a |
||||
* geometry pass. It's mostly faster than PssmShadowRenderer as long as you have |
||||
* more than a about ten shadow recieving objects. The expense is the draw back |
||||
* that the shadow Recieve mode set on spatial is ignored. So basically all and |
||||
* only objects that render depth in the scene receive shadows. See this post |
||||
* for more details |
||||
* http://jmonkeyengine.org/groups/general-2/forum/topic/silly-question-about-shadow-rendering/#post-191599
|
||||
* |
||||
* API is basically the same as the PssmShadowRenderer; |
||||
* |
||||
* @author Rémy Bouquet aka Nehon |
||||
*/ |
||||
public class DirectionalLightShadowFilter extends AbstractShadowFilter<DirectionalLightShadowRenderer> { |
||||
|
||||
|
||||
|
||||
/** |
||||
* Creates a DirectionalLightShadowFilter Shadow Filter More info on the |
||||
* technique at <a |
||||
* href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a>
|
||||
* |
||||
* @param assetManager the application asset manager |
||||
* @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, |
||||
* etc...) |
||||
* @param nbSplits the number of shadow maps rendered (the more shadow maps |
||||
* the more quality, the less fps). |
||||
*/ |
||||
public DirectionalLightShadowFilter(AssetManager assetManager, int shadowMapSize, int nbSplits) { |
||||
super(assetManager, shadowMapSize, new DirectionalLightShadowRenderer(assetManager, shadowMapSize, nbSplits)); |
||||
} |
||||
|
||||
/** |
||||
* return the light used to cast shadows |
||||
* |
||||
* @return the DirectionalLight |
||||
*/ |
||||
public DirectionalLight getLight() { |
||||
return shadowRenderer.getLight(); |
||||
} |
||||
|
||||
/** |
||||
* Sets the light to use to cast shadows |
||||
* |
||||
* @param light a DirectionalLight |
||||
*/ |
||||
public void setLight(DirectionalLight light) { |
||||
shadowRenderer.setLight(light); |
||||
} |
||||
|
||||
/** |
||||
* returns the labda parameter |
||||
* |
||||
* @see #setLambda(float lambda) |
||||
* @return lambda |
||||
*/ |
||||
public float getLambda() { |
||||
return shadowRenderer.getLambda(); |
||||
} |
||||
|
||||
/** |
||||
* Adjust the repartition of the different shadow maps in the shadow extend |
||||
* usualy goes from 0.0 to 1.0 a low value give a more linear repartition |
||||
* resulting in a constant quality in the shadow over the extends, but near |
||||
* shadows could look very jagged a high value give a more logarithmic |
||||
* repartition resulting in a high quality for near shadows, but the quality |
||||
* quickly decrease over the extend. the default value is set to 0.65f |
||||
* (theoric optimal value). |
||||
* |
||||
* @param lambda the lambda value. |
||||
*/ |
||||
public void setLambda(float lambda) { |
||||
shadowRenderer.setLambda(lambda); |
||||
} |
||||
|
||||
/** |
||||
* How far the shadows are rendered in the view |
||||
* |
||||
* @see setShadowZExtend(float zFar) |
||||
* @return shadowZExtend |
||||
*/ |
||||
public float getShadowZExtend() { |
||||
return shadowRenderer.getShadowZExtend(); |
||||
} |
||||
|
||||
/** |
||||
* Set the distance from the eye where the shadows will be rendered default |
||||
* value is dynamicaly computed to the shadow casters/receivers union bound |
||||
* zFar, capped to view frustum far value. |
||||
* |
||||
* @param zFar the zFar values that override the computed one |
||||
*/ |
||||
public void setShadowZExtend(float zFar) { |
||||
shadowRenderer.setShadowZExtend(zFar); |
||||
} |
||||
|
||||
/** |
||||
* Define the length over which the shadow will fade out when using a |
||||
* shadowZextend |
||||
* |
||||
* @param length the fade length in world units |
||||
*/ |
||||
public void setShadowZFadeLength(float length) { |
||||
shadowRenderer.setShadowZFadeLength(length); |
||||
} |
||||
|
||||
/** |
||||
* get the length over which the shadow will fade out when using a |
||||
* shadowZextend |
||||
* |
||||
* @return the fade length in world units |
||||
*/ |
||||
public float getShadowZFadeLength() { |
||||
return shadowRenderer.getShadowZFadeLength(); |
||||
} |
||||
|
||||
@Override |
||||
public void write(JmeExporter ex) throws IOException { |
||||
super.write(ex); |
||||
OutputCapsule oc = ex.getCapsule(this); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void read(JmeImporter im) throws IOException { |
||||
super.read(im); |
||||
InputCapsule ic = im.getCapsule(this); |
||||
|
||||
} |
||||
} |
@ -0,0 +1,257 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 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 com.jme3.shadow; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.light.DirectionalLight; |
||||
import com.jme3.material.Material; |
||||
import com.jme3.math.ColorRGBA; |
||||
import com.jme3.math.Vector2f; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.renderer.queue.OpaqueComparator; |
||||
import com.jme3.scene.Node; |
||||
|
||||
/** |
||||
* DirectionalLightShadowRenderer renderer use Parrallel Split Shadow Mapping |
||||
* technique (pssm)<br> It splits the view frustum in several parts and compute |
||||
* a shadow map for each one.<br> splits are distributed so that the closer they |
||||
* are from the camera, the smaller they are to maximize the resolution used of |
||||
* the shadow map.<br> This result in a better quality shadow than standard |
||||
* shadow mapping.<br> for more informations on this read this <a |
||||
* href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a><br>
|
||||
* <p/> |
||||
* @author Rémy Bouquet aka Nehon |
||||
*/ |
||||
public class DirectionalLightShadowRenderer extends AbstractShadowRenderer { |
||||
|
||||
protected float lambda = 0.65f; |
||||
protected float zFarOverride = 0; |
||||
protected Camera shadowCam; |
||||
protected ColorRGBA splits; |
||||
protected GeometryList splitOccluders = new GeometryList(new OpaqueComparator()); |
||||
protected float[] splitsArray; |
||||
protected DirectionalLight light; |
||||
protected Vector3f[] points = new Vector3f[8]; |
||||
//Holding the info for fading shadows in the far distance
|
||||
protected Vector2f fadeInfo; |
||||
protected float fadeLength; |
||||
|
||||
|
||||
/** |
||||
* Create a DirectionalLightShadowRenderer More info on the technique at <a |
||||
* href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a>
|
||||
* |
||||
* @param assetManager the application asset manager |
||||
* @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, etc...) |
||||
* @param nbSplits the number of shadow maps rendered (the more shadow maps |
||||
* the more quality, the less fps). |
||||
*/ |
||||
public DirectionalLightShadowRenderer(AssetManager assetManager, int shadowMapSize, int nbSplits) { |
||||
super(assetManager, shadowMapSize, nbSplits); |
||||
|
||||
nbShadowMaps = Math.max(Math.min(nbSplits, 4), 1); |
||||
splits = new ColorRGBA(); |
||||
splitsArray = new float[nbSplits + 1]; |
||||
shadowCam = new Camera(shadowMapSize, shadowMapSize); |
||||
shadowCam.setParallelProjection(true); |
||||
|
||||
for (int i = 0; i < points.length; i++) { |
||||
points[i] = new Vector3f(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* return the light used to cast shadows |
||||
* |
||||
* @return the DirectionalLight |
||||
*/ |
||||
public DirectionalLight getLight() { |
||||
return light; |
||||
} |
||||
|
||||
/** |
||||
* Sets the light to use to cast shadows |
||||
* |
||||
* @param light a DirectionalLight |
||||
*/ |
||||
public void setLight(DirectionalLight light) { |
||||
this.light = light; |
||||
} |
||||
|
||||
@Override |
||||
protected void updateShadowCams(Camera viewCam) { |
||||
|
||||
float zFar = zFarOverride; |
||||
if (zFar == 0) { |
||||
zFar = viewCam.getFrustumFar(); |
||||
} |
||||
|
||||
//We prevent computing the frustum points and splits with zeroed or negative near clip value
|
||||
float frustumNear = Math.max(viewCam.getFrustumNear(), 0.001f); |
||||
ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, 1.0f, points); |
||||
|
||||
//shadowCam.setDirection(direction);
|
||||
shadowCam.getRotation().lookAt(light.getDirection(), shadowCam.getUp()); |
||||
shadowCam.update(); |
||||
shadowCam.updateViewProjection(); |
||||
|
||||
PssmShadowUtil.updateFrustumSplits(splitsArray, frustumNear, zFar, lambda); |
||||
|
||||
|
||||
switch (splitsArray.length) { |
||||
case 5: |
||||
splits.a = splitsArray[4]; |
||||
case 4: |
||||
splits.b = splitsArray[3]; |
||||
case 3: |
||||
splits.g = splitsArray[2]; |
||||
case 2: |
||||
case 1: |
||||
splits.r = splitsArray[1]; |
||||
break; |
||||
} |
||||
|
||||
} |
||||
|
||||
@Override |
||||
protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers) { |
||||
|
||||
// update frustum points based on current camera and split
|
||||
ShadowUtil.updateFrustumPoints(viewPort.getCamera(), splitsArray[shadowMapIndex], splitsArray[shadowMapIndex + 1], 1.0f, points); |
||||
|
||||
//Updating shadow cam with curent split frustra
|
||||
ShadowUtil.updateShadowCamera(sceneOccluders, sceneReceivers, shadowCam, points, splitOccluders); |
||||
|
||||
return splitOccluders; |
||||
} |
||||
|
||||
@Override |
||||
protected Camera getShadowCam(int shadowMapIndex) { |
||||
return shadowCam; |
||||
} |
||||
|
||||
@Override |
||||
protected void doDisplayFrustumDebug(int shadowMapIndex) { |
||||
((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, shadowMapIndex)); |
||||
ShadowUtil.updateFrustumPoints2(shadowCam, points); |
||||
((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, shadowMapIndex)); |
||||
} |
||||
|
||||
@Override |
||||
protected void setMaterialParameters(Material material) { |
||||
material.setColor("Splits", splits); |
||||
} |
||||
|
||||
/** |
||||
* returns the labda parameter see #setLambda(float lambda) |
||||
* |
||||
* @return lambda |
||||
*/ |
||||
public float getLambda() { |
||||
return lambda; |
||||
} |
||||
|
||||
/* |
||||
* Adjust the repartition of the different shadow maps in the shadow extend |
||||
* usualy goes from 0.0 to 1.0 |
||||
* a low value give a more linear repartition resulting in a constant quality in the shadow over the extends, but near shadows could look very jagged |
||||
* a high value give a more logarithmic repartition resulting in a high quality for near shadows, but the quality quickly decrease over the extend. |
||||
* the default value is set to 0.65f (theoric optimal value). |
||||
* @param lambda the lambda value. |
||||
*/ |
||||
public void setLambda(float lambda) { |
||||
this.lambda = lambda; |
||||
} |
||||
|
||||
/** |
||||
* How far the shadows are rendered in the view |
||||
* |
||||
* @see #setShadowZExtend(float zFar) |
||||
* @return shadowZExtend |
||||
*/ |
||||
public float getShadowZExtend() { |
||||
return zFarOverride; |
||||
} |
||||
|
||||
/** |
||||
* Set the distance from the eye where the shadows will be rendered default |
||||
* value is dynamicaly computed to the shadow casters/receivers union bound |
||||
* zFar, capped to view frustum far value. |
||||
* |
||||
* @param zFar the zFar values that override the computed one |
||||
*/ |
||||
public void setShadowZExtend(float zFar) { |
||||
if (fadeInfo != null) { |
||||
fadeInfo.set(zFar - fadeLength, 1f / fadeLength); |
||||
} |
||||
this.zFarOverride = zFar; |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Define the length over which the shadow will fade out when using a |
||||
* shadowZextend This is useful to make dynamic shadows fade into baked |
||||
* shadows in the distance. |
||||
* |
||||
* @param length the fade length in world units |
||||
*/ |
||||
public void setShadowZFadeLength(float length) { |
||||
if (length == 0) { |
||||
fadeInfo = null; |
||||
fadeLength = 0; |
||||
postshadowMat.clearParam("FadeInfo"); |
||||
} else { |
||||
if (zFarOverride == 0) { |
||||
fadeInfo = new Vector2f(0, 0); |
||||
} else { |
||||
fadeInfo = new Vector2f(zFarOverride - length, 1.0f / length); |
||||
} |
||||
fadeLength = length; |
||||
postshadowMat.setVector2("FadeInfo", fadeInfo); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* get the length over which the shadow will fade out when using a |
||||
* shadowZextend |
||||
* |
||||
* @return the fade length in world units |
||||
*/ |
||||
public float getShadowZFadeLength() { |
||||
if (fadeInfo != null) { |
||||
return zFarOverride - fadeInfo.x; |
||||
} |
||||
return 0f; |
||||
} |
||||
} |
@ -0,0 +1,143 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 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 com.jme3.shadow; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.export.InputCapsule; |
||||
import com.jme3.export.JmeExporter; |
||||
import com.jme3.export.JmeImporter; |
||||
import com.jme3.export.OutputCapsule; |
||||
import com.jme3.light.SpotLight; |
||||
import java.io.IOException; |
||||
|
||||
/** |
||||
* |
||||
* This Filter does basically the same as a SpotLightShadowRenderer |
||||
* except it renders the post shadow pass as a fulscreen quad pass instead of a |
||||
* geometry pass. It's mostly faster than PssmShadowRenderer as long as you have |
||||
* more than a about ten shadow recieving objects. The expense is the draw back |
||||
* that the shadow Recieve mode set on spatial is ignored. So basically all and |
||||
* only objects that render depth in the scene receive shadows. See this post |
||||
* for more details |
||||
* http://jmonkeyengine.org/groups/general-2/forum/topic/silly-question-about-shadow-rendering/#post-191599
|
||||
* |
||||
* API is basically the same as the PssmShadowRenderer; |
||||
* |
||||
* @author Rémy Bouquet aka Nehon |
||||
*/ |
||||
public class SpotLightShadowFilter extends AbstractShadowFilter<SpotLightShadowRenderer> { |
||||
|
||||
|
||||
/** |
||||
* Creates a SpotLight Shadow Filter |
||||
* @param assetManager the application asset manager |
||||
* @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, |
||||
* etc...) |
||||
* the more quality, the less fps). |
||||
*/ |
||||
public SpotLightShadowFilter(AssetManager assetManager, int shadowMapSize) { |
||||
super(assetManager, shadowMapSize, new SpotLightShadowRenderer(assetManager, shadowMapSize)); |
||||
} |
||||
|
||||
/** |
||||
* return the light used to cast shadows |
||||
* |
||||
* @return the SpotLight |
||||
*/ |
||||
public SpotLight getLight() { |
||||
return shadowRenderer.getLight(); |
||||
} |
||||
|
||||
/** |
||||
* Sets the light to use to cast shadows |
||||
* |
||||
* @param light a SpotLight |
||||
*/ |
||||
public void setLight(SpotLight light) { |
||||
shadowRenderer.setLight(light); |
||||
} |
||||
|
||||
/** |
||||
* How far the shadows are rendered in the view |
||||
* |
||||
* @see setShadowZExtend(float zFar) |
||||
* @return shadowZExtend |
||||
*/ |
||||
public float getShadowZExtend() { |
||||
return shadowRenderer.getShadowZExtend(); |
||||
} |
||||
|
||||
/** |
||||
* Set the distance from the eye where the shadows will be rendered default |
||||
* value is dynamicaly computed to the shadow casters/receivers union bound |
||||
* zFar, capped to view frustum far value. |
||||
* |
||||
* @param zFar the zFar values that override the computed one |
||||
*/ |
||||
public void setShadowZExtend(float zFar) { |
||||
shadowRenderer.setShadowZExtend(zFar); |
||||
} |
||||
|
||||
/** |
||||
* Define the length over which the shadow will fade out when using a |
||||
* shadowZextend |
||||
* |
||||
* @param length the fade length in world units |
||||
*/ |
||||
public void setShadowZFadeLength(float length) { |
||||
shadowRenderer.setShadowZFadeLength(length); |
||||
} |
||||
|
||||
/** |
||||
* get the length over which the shadow will fade out when using a |
||||
* shadowZextend |
||||
* |
||||
* @return the fade length in world units |
||||
*/ |
||||
public float getShadowZFadeLength() { |
||||
return shadowRenderer.getShadowZFadeLength(); |
||||
} |
||||
|
||||
@Override |
||||
public void write(JmeExporter ex) throws IOException { |
||||
super.write(ex); |
||||
OutputCapsule oc = ex.getCapsule(this); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void read(JmeImporter im) throws IOException { |
||||
super.read(im); |
||||
InputCapsule ic = im.getCapsule(this); |
||||
|
||||
} |
||||
} |
@ -0,0 +1,211 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 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 com.jme3.shadow; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.light.DirectionalLight; |
||||
import com.jme3.light.SpotLight; |
||||
import com.jme3.material.Material; |
||||
import com.jme3.math.ColorRGBA; |
||||
import com.jme3.math.FastMath; |
||||
import com.jme3.math.Vector2f; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.math.Vector4f; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.renderer.queue.OpaqueComparator; |
||||
import com.jme3.scene.Node; |
||||
|
||||
/** |
||||
* SpotLightShadowRenderer renderer use Parrallel Split Shadow Mapping technique |
||||
* (pssm)<br> It splits the view frustum in several parts and compute a shadow |
||||
* map for each one.<br> splits are distributed so that the closer they are from |
||||
* the camera, the smaller they are to maximize the resolution used of the |
||||
* shadow map.<br> This result in a better quality shadow than standard shadow |
||||
* mapping.<br> for more informations on this read this <a |
||||
* href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a><br>
|
||||
* <p/> |
||||
* @author Rémy Bouquet aka Nehon |
||||
*/ |
||||
public class SpotLightShadowRenderer extends AbstractShadowRenderer { |
||||
|
||||
protected float zFarOverride = 0; |
||||
protected Camera shadowCam; |
||||
protected GeometryList mapOccluders = new GeometryList(new OpaqueComparator()); |
||||
protected SpotLight light; |
||||
protected Vector3f[] points = new Vector3f[8]; |
||||
//Holding the info for fading shadows in the far distance
|
||||
protected Vector2f fadeInfo; |
||||
protected float fadeLength; |
||||
|
||||
/** |
||||
* Create a SpotLightShadowRenderer This use standard shadow mapping |
||||
* |
||||
* @param assetManager the application asset manager |
||||
* @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048, |
||||
* etc...) the more quality, the less fps). |
||||
*/ |
||||
public SpotLightShadowRenderer(AssetManager assetManager, int shadowMapSize) { |
||||
super(assetManager, shadowMapSize, 1); |
||||
|
||||
shadowCam = new Camera(shadowMapSize, shadowMapSize); |
||||
|
||||
for (int i = 0; i < points.length; i++) { |
||||
points[i] = new Vector3f(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* return the light used to cast shadows |
||||
* |
||||
* @return the SpotLight |
||||
*/ |
||||
public SpotLight getLight() { |
||||
return light; |
||||
} |
||||
|
||||
/** |
||||
* Sets the light to use to cast shadows |
||||
* |
||||
* @param light a SpotLight |
||||
*/ |
||||
public void setLight(SpotLight light) { |
||||
this.light = light; |
||||
} |
||||
|
||||
@Override |
||||
protected void updateShadowCams(Camera viewCam) { |
||||
|
||||
float zFar = zFarOverride; |
||||
if (zFar == 0) { |
||||
zFar = viewCam.getFrustumFar(); |
||||
} |
||||
|
||||
//We prevent computing the frustum points and splits with zeroed or negative near clip value
|
||||
float frustumNear = Math.max(viewCam.getFrustumNear(), 0.001f); |
||||
ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, 1.0f, points); |
||||
//shadowCam.setDirection(direction);
|
||||
|
||||
shadowCam.setFrustumPerspective(light.getSpotOuterAngle() * FastMath.RAD_TO_DEG * 2.0f, 1, 1f, light.getSpotRange()); |
||||
shadowCam.getRotation().lookAt(light.getDirection(), shadowCam.getUp()); |
||||
shadowCam.setLocation(light.getPosition()); |
||||
|
||||
shadowCam.update(); |
||||
shadowCam.updateViewProjection(); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers) { |
||||
ShadowUtil.getOccludersInCamFrustum(sceneOccluders, shadowCam, mapOccluders); |
||||
return mapOccluders; |
||||
} |
||||
|
||||
@Override |
||||
protected Camera getShadowCam(int shadowMapIndex) { |
||||
return shadowCam; |
||||
} |
||||
|
||||
@Override |
||||
protected void doDisplayFrustumDebug(int shadowMapIndex) { |
||||
Vector3f[] points2 = points.clone(); |
||||
|
||||
((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, shadowMapIndex)); |
||||
ShadowUtil.updateFrustumPoints2(shadowCam, points2); |
||||
((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points2, shadowMapIndex)); |
||||
} |
||||
|
||||
@Override |
||||
protected void setMaterialParameters(Material material) { |
||||
} |
||||
|
||||
/** |
||||
* How far the shadows are rendered in the view |
||||
* |
||||
* @see #setShadowZExtend(float zFar) |
||||
* @return shadowZExtend |
||||
*/ |
||||
public float getShadowZExtend() { |
||||
return zFarOverride; |
||||
} |
||||
|
||||
/** |
||||
* Set the distance from the eye where the shadows will be rendered default |
||||
* value is dynamicaly computed to the shadow casters/receivers union bound |
||||
* zFar, capped to view frustum far value. |
||||
* |
||||
* @param zFar the zFar values that override the computed one |
||||
*/ |
||||
public void setShadowZExtend(float zFar) { |
||||
if (fadeInfo != null) { |
||||
fadeInfo.set(zFar - fadeLength, 1f / fadeLength); |
||||
} |
||||
this.zFarOverride = zFar; |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Define the length over which the shadow will fade out when using a |
||||
* shadowZextend This is useful to make dynamic shadows fade into baked |
||||
* shadows in the distance. |
||||
* |
||||
* @param length the fade length in world units |
||||
*/ |
||||
public void setShadowZFadeLength(float length) { |
||||
if (length == 0) { |
||||
fadeInfo = null; |
||||
fadeLength = 0; |
||||
postshadowMat.clearParam("FadeInfo"); |
||||
} else { |
||||
if (zFarOverride == 0) { |
||||
fadeInfo = new Vector2f(0, 0); |
||||
} else { |
||||
fadeInfo = new Vector2f(zFarOverride - length, 1.0f / length); |
||||
} |
||||
fadeLength = length; |
||||
postshadowMat.setVector2("FadeInfo", fadeInfo); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* get the length over which the shadow will fade out when using a |
||||
* shadowZextend |
||||
* |
||||
* @return the fade length in world units |
||||
*/ |
||||
public float getShadowZFadeLength() { |
||||
if (fadeInfo != null) { |
||||
return zFarOverride - fadeInfo.x; |
||||
} |
||||
return 0f; |
||||
} |
||||
} |
@ -0,0 +1,154 @@ |
||||
/* |
||||
* To change this template, choose Tools | Templates |
||||
* and open the template in the editor. |
||||
*/ |
||||
package jme3test.light; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.font.BitmapFont; |
||||
import com.jme3.font.BitmapText; |
||||
import com.jme3.input.InputManager; |
||||
import com.jme3.input.KeyInput; |
||||
import com.jme3.input.controls.ActionListener; |
||||
import com.jme3.input.controls.KeyTrigger; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.shadow.AbstractShadowFilter; |
||||
import com.jme3.shadow.AbstractShadowRenderer; |
||||
import com.jme3.shadow.CompareMode; |
||||
import com.jme3.shadow.EdgeFilteringMode; |
||||
|
||||
/** |
||||
* |
||||
* @author Nehon |
||||
*/ |
||||
public class ShadowTestUIManager implements ActionListener { |
||||
|
||||
private BitmapText shadowTypeText; |
||||
private BitmapText shadowCompareText; |
||||
private BitmapText shadowFilterText; |
||||
private BitmapText shadowIntensityText; |
||||
private final static String TYPE_TEXT = "(Space) Shadow type : "; |
||||
private final static String COMPARE_TEXT = "(enter) Shadow compare "; |
||||
private final static String FILTERING_TEXT = "(f) Edge filtering : "; |
||||
private final static String INTENSITY_TEXT = "(t:up, g:down) Shadow intensity : "; |
||||
private boolean hardwareShadows = true; |
||||
private AbstractShadowRenderer plsr; |
||||
private AbstractShadowFilter plsf; |
||||
private ViewPort viewPort; |
||||
private int filteringIndex = 0; |
||||
private int renderModeIndex = 0; |
||||
|
||||
|
||||
public ShadowTestUIManager(AssetManager assetManager,AbstractShadowRenderer plsr, AbstractShadowFilter plsf, |
||||
Node guiNode, InputManager inputManager, ViewPort viewPort) { |
||||
this.plsr = plsr; |
||||
this.plsf = plsf; |
||||
this.viewPort = viewPort; |
||||
BitmapFont guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); |
||||
shadowTypeText = createText(guiFont); |
||||
shadowCompareText = createText(guiFont); |
||||
shadowFilterText = createText(guiFont); |
||||
shadowIntensityText = createText(guiFont); |
||||
|
||||
shadowTypeText.setText(TYPE_TEXT + "Processor"); |
||||
shadowCompareText.setText(COMPARE_TEXT + (hardwareShadows ? "Hardware" : "Software")); |
||||
shadowFilterText.setText(FILTERING_TEXT + plsr.getEdgeFilteringMode().toString()); |
||||
shadowIntensityText.setText(INTENSITY_TEXT + plsr.getShadowIntensity()); |
||||
|
||||
shadowTypeText.setLocalTranslation(10, viewPort.getCamera().getHeight() - 20, 0); |
||||
shadowCompareText.setLocalTranslation(10, viewPort.getCamera().getHeight() - 40, 0); |
||||
shadowFilterText.setLocalTranslation(10, viewPort.getCamera().getHeight() - 60, 0); |
||||
shadowIntensityText.setLocalTranslation(10, viewPort.getCamera().getHeight() - 80, 0); |
||||
|
||||
guiNode.attachChild(shadowTypeText); |
||||
guiNode.attachChild(shadowCompareText); |
||||
guiNode.attachChild(shadowFilterText); |
||||
guiNode.attachChild(shadowIntensityText); |
||||
|
||||
inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE)); |
||||
inputManager.addMapping("changeFiltering", new KeyTrigger(KeyInput.KEY_F)); |
||||
inputManager.addMapping("ShadowUp", new KeyTrigger(KeyInput.KEY_T)); |
||||
inputManager.addMapping("ShadowDown", new KeyTrigger(KeyInput.KEY_G)); |
||||
inputManager.addMapping("ThicknessUp", new KeyTrigger(KeyInput.KEY_Y)); |
||||
inputManager.addMapping("ThicknessDown", new KeyTrigger(KeyInput.KEY_H)); |
||||
inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN)); |
||||
|
||||
|
||||
inputManager.addListener(this, "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown", "changeFiltering"); |
||||
|
||||
} |
||||
|
||||
|
||||
public void onAction(String name, boolean keyPressed, float tpf) { |
||||
if (name.equals("toggle") && keyPressed) { |
||||
renderModeIndex += 1; |
||||
renderModeIndex %= 3; |
||||
|
||||
switch (renderModeIndex) { |
||||
case 0: |
||||
viewPort.addProcessor(plsr); |
||||
shadowTypeText.setText(TYPE_TEXT + "Processor"); |
||||
break; |
||||
case 1: |
||||
viewPort.removeProcessor(plsr); |
||||
plsf.setEnabled(true); |
||||
shadowTypeText.setText(TYPE_TEXT + "Filter"); |
||||
break; |
||||
case 2: |
||||
plsf.setEnabled(false); |
||||
shadowTypeText.setText(TYPE_TEXT + "None"); |
||||
break; |
||||
} |
||||
|
||||
|
||||
|
||||
} else if (name.equals("toggleHW") && keyPressed) { |
||||
hardwareShadows = !hardwareShadows; |
||||
plsr.setShadowCompareMode(hardwareShadows ? CompareMode.Hardware : CompareMode.Software); |
||||
plsf.setShadowCompareMode(hardwareShadows ? CompareMode.Hardware : CompareMode.Software); |
||||
|
||||
shadowCompareText.setText(COMPARE_TEXT + (hardwareShadows ? "Hardware" : "Software")); |
||||
} |
||||
|
||||
|
||||
if (name.equals("changeFiltering") && keyPressed) { |
||||
filteringIndex = plsr.getEdgeFilteringMode().ordinal(); |
||||
filteringIndex = (filteringIndex + 1) % EdgeFilteringMode.values().length; |
||||
EdgeFilteringMode m = EdgeFilteringMode.values()[filteringIndex]; |
||||
plsr.setEdgeFilteringMode(m); |
||||
plsf.setEdgeFilteringMode(m); |
||||
shadowFilterText.setText(FILTERING_TEXT + m.toString()); |
||||
} |
||||
|
||||
if (name.equals("ShadowUp") && keyPressed) { |
||||
plsr.setShadowIntensity(plsr.getShadowIntensity() + 0.1f); |
||||
plsf.setShadowIntensity(plsf.getShadowIntensity() + 0.1f); |
||||
|
||||
shadowIntensityText.setText(INTENSITY_TEXT + plsr.getShadowIntensity()); |
||||
} |
||||
if (name.equals("ShadowDown") && keyPressed) { |
||||
plsr.setShadowIntensity(plsr.getShadowIntensity() - 0.1f); |
||||
plsf.setShadowIntensity(plsf.getShadowIntensity() - 0.1f); |
||||
shadowIntensityText.setText(INTENSITY_TEXT + plsr.getShadowIntensity()); |
||||
} |
||||
if (name.equals("ThicknessUp") && keyPressed) { |
||||
plsr.setEdgesThickness(plsr.getEdgesThickness() + 1); |
||||
plsf.setEdgesThickness(plsf.getEdgesThickness() + 1); |
||||
System.out.println("Shadow thickness : " + plsr.getEdgesThickness()); |
||||
} |
||||
if (name.equals("ThicknessDown") && keyPressed) { |
||||
plsr.setEdgesThickness(plsr.getEdgesThickness() - 1); |
||||
plsf.setEdgesThickness(plsf.getEdgesThickness() - 1); |
||||
System.out.println("Shadow thickness : " + plsr.getEdgesThickness()); |
||||
} |
||||
|
||||
} |
||||
|
||||
private BitmapText createText(BitmapFont guiFont) { |
||||
BitmapText t = new BitmapText(guiFont, false); |
||||
t.setSize(guiFont.getCharSet().getRenderedSize() * 0.75f); |
||||
return t; |
||||
} |
||||
} |
@ -0,0 +1,311 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 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.font.BitmapText; |
||||
import com.jme3.input.KeyInput; |
||||
import com.jme3.input.controls.ActionListener; |
||||
import com.jme3.input.controls.KeyTrigger; |
||||
import com.jme3.light.AmbientLight; |
||||
import com.jme3.light.DirectionalLight; |
||||
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.post.FilterPostProcessor; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.control.AbstractControl; |
||||
import com.jme3.scene.control.Control; |
||||
import com.jme3.scene.shape.Box; |
||||
import com.jme3.scene.shape.Sphere; |
||||
import com.jme3.shadow.CompareMode; |
||||
import com.jme3.shadow.DirectionalLightShadowFilter; |
||||
import com.jme3.shadow.DirectionalLightShadowRenderer; |
||||
import com.jme3.shadow.EdgeFilteringMode; |
||||
import com.jme3.shadow.PssmShadowRenderer.FilterMode; |
||||
import com.jme3.texture.Texture; |
||||
import com.jme3.texture.Texture.WrapMode; |
||||
import com.jme3.util.SkyFactory; |
||||
import com.jme3.util.TangentBinormalGenerator; |
||||
|
||||
public class TestDirectionalLightShadow extends SimpleApplication implements ActionListener { |
||||
|
||||
private Spatial[] obj; |
||||
private Material[] mat; |
||||
private boolean hardwareShadows = false; |
||||
private DirectionalLightShadowRenderer dlsr; |
||||
private DirectionalLightShadowFilter dlsf; |
||||
private Geometry ground; |
||||
private Material matGroundU; |
||||
private Material matGroundL; |
||||
|
||||
public static void main(String[] args) { |
||||
TestDirectionalLightShadow app = new TestDirectionalLightShadow(); |
||||
app.start(); |
||||
} |
||||
|
||||
public void loadScene() { |
||||
obj = new Spatial[2]; |
||||
mat = new Material[2]; |
||||
mat[0] = assetManager.loadMaterial("Common/Materials/RedColor.j3m"); |
||||
mat[1] = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m"); |
||||
mat[1].setBoolean("UseMaterialColors", true); |
||||
mat[1].setColor("Ambient", ColorRGBA.White.mult(0.5f)); |
||||
mat[1].setColor("Diffuse", ColorRGBA.White.clone()); |
||||
|
||||
|
||||
obj[0] = new Geometry("sphere", new Sphere(30, 30, 2)); |
||||
obj[0].setShadowMode(ShadowMode.CastAndReceive); |
||||
obj[1] = new Geometry("cube", new Box(1.0f, 1.0f, 1.0f)); |
||||
obj[1].setShadowMode(ShadowMode.CastAndReceive); |
||||
TangentBinormalGenerator.generate(obj[1]); |
||||
TangentBinormalGenerator.generate(obj[0]); |
||||
|
||||
|
||||
for (int i = 0; i < 60; i++) { |
||||
Spatial t = obj[FastMath.nextRandomInt(0, obj.length - 1)].clone(false); |
||||
t.setLocalScale(FastMath.nextRandomFloat() * 10f); |
||||
t.setMaterial(mat[FastMath.nextRandomInt(0, mat.length - 1)]); |
||||
rootNode.attachChild(t); |
||||
t.setLocalTranslation(FastMath.nextRandomFloat() * 200f, FastMath.nextRandomFloat() * 30f + 20, 30f * (i + 2f)); |
||||
} |
||||
|
||||
Box b = new Box(new Vector3f(0, 10, 550), 1000, 2, 1000); |
||||
b.scaleTextureCoordinates(new Vector2f(10, 10)); |
||||
ground = new Geometry("soil", b); |
||||
matGroundU = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); |
||||
matGroundU.setColor("Color", ColorRGBA.Green); |
||||
|
||||
|
||||
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); |
||||
|
||||
ground.setMaterial(matGroundL); |
||||
|
||||
ground.setShadowMode(ShadowMode.CastAndReceive); |
||||
rootNode.attachChild(ground); |
||||
|
||||
l = new DirectionalLight(); |
||||
//new Vector3f(-0.5973172f, -0.56583486f, 0.8846725f).normalizeLocal()
|
||||
l.setDirection(new Vector3f(-1, -1, -1)); |
||||
rootNode.addLight(l); |
||||
|
||||
|
||||
AmbientLight al = new AmbientLight(); |
||||
al.setColor(ColorRGBA.White.mult(0.5f)); |
||||
rootNode.addLight(al); |
||||
|
||||
Spatial sky = SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false); |
||||
sky.setLocalScale(350); |
||||
|
||||
rootNode.attachChild(sky); |
||||
} |
||||
DirectionalLight l; |
||||
|
||||
@Override |
||||
public void simpleInitApp() { |
||||
// put the camera in a bad position
|
||||
cam.setLocation(new Vector3f(65.25412f, 44.38738f, 9.087874f)); |
||||
cam.setRotation(new Quaternion(0.078139365f, 0.050241485f, -0.003942559f, 0.9956679f)); |
||||
|
||||
flyCam.setMoveSpeed(100); |
||||
|
||||
loadScene(); |
||||
|
||||
dlsr = new DirectionalLightShadowRenderer(assetManager, 1024, 3); |
||||
dlsr.setLight(l); |
||||
dlsr.setLambda(0.55f); |
||||
dlsr.setShadowIntensity(0.6f); |
||||
dlsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest); |
||||
//dlsr.displayFrustum();
|
||||
viewPort.addProcessor(dlsr); |
||||
|
||||
dlsf = new DirectionalLightShadowFilter(assetManager, 1024, 3); |
||||
dlsf.setLight(l); |
||||
dlsf.setLambda(0.55f); |
||||
dlsf.setShadowIntensity(0.6f); |
||||
dlsf.setEdgeFilteringMode(EdgeFilteringMode.Nearest); |
||||
dlsf.setEnabled(false); |
||||
|
||||
FilterPostProcessor fpp = new FilterPostProcessor(assetManager); |
||||
fpp.addFilter(dlsf); |
||||
|
||||
viewPort.addProcessor(fpp); |
||||
|
||||
initInputs(); |
||||
} |
||||
|
||||
private void initInputs() { |
||||
|
||||
inputManager.addMapping("ThicknessUp", new KeyTrigger(KeyInput.KEY_Y)); |
||||
inputManager.addMapping("ThicknessDown", new KeyTrigger(KeyInput.KEY_H)); |
||||
inputManager.addMapping("lambdaUp", new KeyTrigger(KeyInput.KEY_U)); |
||||
inputManager.addMapping("lambdaDown", new KeyTrigger(KeyInput.KEY_J)); |
||||
inputManager.addMapping("switchGroundMat", new KeyTrigger(KeyInput.KEY_M)); |
||||
inputManager.addMapping("splits", new KeyTrigger(KeyInput.KEY_X)); |
||||
|
||||
inputManager.addMapping("up", new KeyTrigger(KeyInput.KEY_NUMPAD8)); |
||||
inputManager.addMapping("down", new KeyTrigger(KeyInput.KEY_NUMPAD2)); |
||||
inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_NUMPAD6)); |
||||
inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_NUMPAD4)); |
||||
inputManager.addMapping("fwd", new KeyTrigger(KeyInput.KEY_PGUP)); |
||||
inputManager.addMapping("back", new KeyTrigger(KeyInput.KEY_PGDN)); |
||||
|
||||
|
||||
inputManager.addListener(this, "lambdaUp", "lambdaDown", "ThicknessUp", "ThicknessDown", |
||||
"switchGroundMat", "splits", "up", "down", "right", "left", "fwd", "back"); |
||||
|
||||
ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, dlsr, dlsf, guiNode, inputManager, viewPort); |
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
public void onAction(String name, boolean keyPressed, float tpf) { |
||||
|
||||
|
||||
|
||||
if (name.equals("lambdaUp") && keyPressed) { |
||||
dlsr.setLambda(dlsr.getLambda() + 0.01f); |
||||
dlsf.setLambda(dlsr.getLambda() + 0.01f); |
||||
System.out.println("Lambda : " + dlsr.getLambda()); |
||||
} else if (name.equals("lambdaDown") && keyPressed) { |
||||
dlsr.setLambda(dlsr.getLambda() - 0.01f); |
||||
dlsf.setLambda(dlsr.getLambda() - 0.01f); |
||||
System.out.println("Lambda : " + dlsr.getLambda()); |
||||
} |
||||
|
||||
if (name.equals("ShadowUp") && keyPressed) { |
||||
dlsr.setShadowIntensity(dlsr.getShadowIntensity() + 0.1f); |
||||
dlsf.setShadowIntensity(dlsr.getShadowIntensity() + 0.1f); |
||||
System.out.println("Shadow intensity : " + dlsr.getShadowIntensity()); |
||||
} |
||||
if (name.equals("ShadowDown") && keyPressed) { |
||||
dlsr.setShadowIntensity(dlsr.getShadowIntensity() - 0.1f); |
||||
dlsf.setShadowIntensity(dlsr.getShadowIntensity() - 0.1f); |
||||
System.out.println("Shadow intensity : " + dlsr.getShadowIntensity()); |
||||
} |
||||
if (name.equals("ThicknessUp") && keyPressed) { |
||||
dlsr.setEdgesThickness(dlsr.getEdgesThickness() + 1); |
||||
dlsf.setEdgesThickness(dlsr.getEdgesThickness() + 1); |
||||
System.out.println("Shadow thickness : " + dlsr.getEdgesThickness()); |
||||
} |
||||
if (name.equals("ThicknessDown") && keyPressed) { |
||||
dlsr.setEdgesThickness(dlsr.getEdgesThickness() - 1); |
||||
dlsf.setEdgesThickness(dlsr.getEdgesThickness() - 1); |
||||
System.out.println("Shadow thickness : " + dlsr.getEdgesThickness()); |
||||
} |
||||
if (name.equals("switchGroundMat") && keyPressed) { |
||||
if (ground.getMaterial() == matGroundL) { |
||||
ground.setMaterial(matGroundU); |
||||
} else { |
||||
ground.setMaterial(matGroundL); |
||||
} |
||||
} |
||||
|
||||
if (name.equals("up")) { |
||||
up = keyPressed; |
||||
} |
||||
if (name.equals("down")) { |
||||
down = keyPressed; |
||||
} |
||||
if (name.equals("right")) { |
||||
right = keyPressed; |
||||
} |
||||
if (name.equals("left")) { |
||||
left = keyPressed; |
||||
} |
||||
if (name.equals("fwd")) { |
||||
fwd = keyPressed; |
||||
} |
||||
if (name.equals("back")) { |
||||
back = keyPressed; |
||||
} |
||||
|
||||
} |
||||
boolean up = false; |
||||
boolean down = false; |
||||
boolean left = false; |
||||
boolean right = false; |
||||
boolean fwd = false; |
||||
boolean back = false; |
||||
float time = 0; |
||||
float s = 1f; |
||||
|
||||
@Override |
||||
public void simpleUpdate(float tpf) { |
||||
if (up) { |
||||
Vector3f v = l.getDirection(); |
||||
v.y += tpf / s; |
||||
setDir(v); |
||||
} |
||||
if (down) { |
||||
Vector3f v = l.getDirection(); |
||||
v.y -= tpf / s; |
||||
setDir(v); |
||||
} |
||||
if (right) { |
||||
Vector3f v = l.getDirection(); |
||||
v.x += tpf / s; |
||||
setDir(v); |
||||
} |
||||
if (left) { |
||||
Vector3f v = l.getDirection(); |
||||
v.x -= tpf / s; |
||||
setDir(v); |
||||
} |
||||
if (fwd) { |
||||
Vector3f v = l.getDirection(); |
||||
v.z += tpf / s; |
||||
setDir(v); |
||||
} |
||||
if (back) { |
||||
Vector3f v = l.getDirection(); |
||||
v.z -= tpf / s; |
||||
setDir(v); |
||||
} |
||||
|
||||
} |
||||
|
||||
private void setDir(Vector3f v) { |
||||
l.setDirection(v); |
||||
} |
||||
} |
@ -0,0 +1,198 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 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.input.KeyInput; |
||||
import com.jme3.input.controls.ActionListener; |
||||
import com.jme3.input.controls.KeyTrigger; |
||||
import com.jme3.light.AmbientLight; |
||||
import com.jme3.light.DirectionalLight; |
||||
import com.jme3.light.SpotLight; |
||||
import com.jme3.material.Material; |
||||
import com.jme3.math.*; |
||||
import com.jme3.post.FilterPostProcessor; |
||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.shape.Box; |
||||
import com.jme3.scene.shape.Sphere; |
||||
import com.jme3.shader.VarType; |
||||
import com.jme3.shadow.CompareMode; |
||||
import com.jme3.shadow.EdgeFilteringMode; |
||||
import com.jme3.shadow.SpotLightShadowFilter; |
||||
import com.jme3.shadow.SpotLightShadowRenderer; |
||||
import com.jme3.texture.Texture.WrapMode; |
||||
import com.jme3.util.TangentBinormalGenerator; |
||||
|
||||
public class TestSpotLightShadows extends SimpleApplication { |
||||
|
||||
private Vector3f lightTarget = new Vector3f(12, 3.5f, 30); |
||||
|
||||
public static void main(String[] args) { |
||||
TestSpotLightShadows app = new TestSpotLightShadows(); |
||||
app.start(); |
||||
} |
||||
SpotLight spot; |
||||
Geometry lightMdl; |
||||
|
||||
public void setupLighting() { |
||||
AmbientLight al = new AmbientLight(); |
||||
al.setColor(ColorRGBA.White.mult(0.3f)); |
||||
rootNode.addLight(al); |
||||
|
||||
rootNode.setShadowMode(ShadowMode.CastAndReceive); |
||||
|
||||
spot = new SpotLight(); |
||||
|
||||
spot.setSpotRange(1000); |
||||
spot.setSpotInnerAngle(5f * FastMath.DEG_TO_RAD); |
||||
spot.setSpotOuterAngle(10 * FastMath.DEG_TO_RAD); |
||||
spot.setPosition(new Vector3f(70.70334f, 34.013165f, 27.1017f)); |
||||
spot.setDirection(lightTarget.subtract(spot.getPosition())); |
||||
spot.setColor(ColorRGBA.White.mult(2)); |
||||
rootNode.addLight(spot); |
||||
|
||||
|
||||
// PointLight pl=new PointLight();
|
||||
// pl.setPosition(new Vector3f(77.70334f, 34.013165f, 27.1017f));
|
||||
// pl.setRadius(1000);
|
||||
// pl.setColor(ColorRGBA.White.mult(2));
|
||||
// rootNode.addLight(pl);
|
||||
lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f)); |
||||
lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m")); |
||||
lightMdl.setLocalTranslation(new Vector3f(77.70334f, 34.013165f, 27.1017f)); |
||||
lightMdl.setLocalScale(5); |
||||
rootNode.attachChild(lightMdl); |
||||
|
||||
// DirectionalLight dl = new DirectionalLight();
|
||||
// dl.setDirection(lightTarget.subtract(new Vector3f(77.70334f, 34.013165f, 27.1017f)));
|
||||
// dl.setColor(ColorRGBA.White.mult(0.7f));
|
||||
// rootNode.addLight(dl);
|
||||
|
||||
|
||||
final SpotLightShadowRenderer slsr = new SpotLightShadowRenderer(assetManager, 512); |
||||
slsr.setLight(spot); |
||||
slsr.setShadowIntensity(0.5f); |
||||
slsr.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON); |
||||
viewPort.addProcessor(slsr); |
||||
|
||||
SpotLightShadowFilter slsf = new SpotLightShadowFilter(assetManager, 512); |
||||
slsf.setLight(spot); |
||||
slsf.setShadowIntensity(0.5f); |
||||
slsf.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON); |
||||
slsf.setEnabled(false); |
||||
|
||||
FilterPostProcessor fpp = new FilterPostProcessor(assetManager); |
||||
fpp.addFilter(slsf); |
||||
viewPort.addProcessor(fpp); |
||||
|
||||
ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, slsr, slsf, guiNode, inputManager, viewPort); |
||||
|
||||
inputManager.addListener(new ActionListener() { |
||||
public void onAction(String name, boolean isPressed, float tpf) { |
||||
if (name.equals("stop") && isPressed) { |
||||
stop = !stop; |
||||
// slsr.displayFrustum();
|
||||
System.out.println("pos : " + spot.getPosition()); |
||||
System.out.println("dir : " + spot.getDirection()); |
||||
} |
||||
} |
||||
}, "stop"); |
||||
|
||||
inputManager.addMapping("stop", new KeyTrigger(KeyInput.KEY_1)); |
||||
|
||||
} |
||||
|
||||
public void setupFloor() { |
||||
Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m"); |
||||
mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat); |
||||
mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat); |
||||
mat.setBoolean("UseMaterialColors", true); |
||||
mat.setColor("Diffuse", ColorRGBA.White.clone()); |
||||
mat.setColor("Ambient", ColorRGBA.White.clone()); |
||||
// mat.setColor("Specular", ColorRGBA.White.clone());
|
||||
// mat.getTextureParam("ParallaxMap").getTextureValue().setWrap(WrapMode.Repeat);
|
||||
mat.setFloat("Shininess", 0); |
||||
// mat.setBoolean("VertexLighting", true);
|
||||
|
||||
|
||||
Box floor = new Box(Vector3f.ZERO, 50, 1f, 50); |
||||
TangentBinormalGenerator.generate(floor); |
||||
floor.scaleTextureCoordinates(new Vector2f(5, 5)); |
||||
Geometry floorGeom = new Geometry("Floor", floor); |
||||
floorGeom.setMaterial(mat); |
||||
floorGeom.setShadowMode(ShadowMode.Receive); |
||||
rootNode.attachChild(floorGeom); |
||||
} |
||||
|
||||
public void setupSignpost() { |
||||
Spatial signpost = assetManager.loadModel("Models/Sign Post/Sign Post.mesh.xml"); |
||||
Material mat = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m"); |
||||
// mat.setBoolean("VertexLighting", true);
|
||||
signpost.setMaterial(mat); |
||||
signpost.rotate(0, FastMath.HALF_PI, 0); |
||||
signpost.setLocalTranslation(12, 3.5f, 30); |
||||
signpost.setLocalScale(4); |
||||
signpost.setShadowMode(ShadowMode.CastAndReceive); |
||||
TangentBinormalGenerator.generate(signpost); |
||||
rootNode.attachChild(signpost); |
||||
} |
||||
|
||||
@Override |
||||
public void simpleInitApp() { |
||||
cam.setLocation(new Vector3f(27.492603f, 29.138166f, -13.232513f)); |
||||
cam.setRotation(new Quaternion(0.25168246f, -0.10547892f, 0.02760565f, 0.96164864f)); |
||||
flyCam.setMoveSpeed(30); |
||||
|
||||
setupLighting(); |
||||
setupFloor(); |
||||
setupSignpost(); |
||||
|
||||
|
||||
} |
||||
float angle; |
||||
boolean stop = true; |
||||
|
||||
@Override |
||||
public void simpleUpdate(float tpf) { |
||||
if (!stop) { |
||||
super.simpleUpdate(tpf); |
||||
angle += tpf; |
||||
angle %= FastMath.TWO_PI; |
||||
|
||||
spot.setPosition(new Vector3f(FastMath.cos(angle) * 30f, 34.013165f, FastMath.sin(angle) * 30f)); |
||||
lightMdl.setLocalTranslation(spot.getPosition()); |
||||
spot.setDirection(lightTarget.subtract(spot.getPosition())); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue