Shadows : There is now an alternative to the PssmRenderer : the PssmFilter that has to be used as any other filter. It does the same ass the PssmRenderer except the post shadow pass is done in screen space making it run very faster on scene that have a lot of shadow recieving Geometries.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9787 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
rem..om 12 years ago
parent f0d4f86dd8
commit cbafa1852b
  1. 68
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag
  2. 73
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md
  3. 10
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.vert
  4. 84
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag
  5. 12
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.vert
  6. 202
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag
  7. 181
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag
  8. 151
      engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib
  9. 164
      engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib
  10. 262
      engine/src/core/com/jme3/shadow/PssmShadowFilter.java
  11. 108
      engine/src/core/com/jme3/shadow/PssmShadowRenderer.java
  12. 86
      engine/src/test/jme3test/light/TestPssmShadow.java
  13. 182
      engine/src/test/jme3test/light/TestShadowsPerf.java

@ -0,0 +1,68 @@
#import "Common/ShaderLib/PssmShadows.glsllib"
uniform sampler2D m_Texture;
uniform sampler2D m_DepthTexture;
uniform mat4 m_ViewProjectionMatrixInverse;
uniform vec4 m_ViewProjectionMatrixRow2;
varying vec2 texCoord;
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);
uniform mat4 m_LightViewProjectionMatrix0;
uniform mat4 m_LightViewProjectionMatrix1;
uniform mat4 m_LightViewProjectionMatrix2;
uniform mat4 m_LightViewProjectionMatrix3;
vec3 getPosition(in float depth, in vec2 uv){
vec4 pos = vec4(uv, depth, 1.0) * 2.0 - 1.0;
pos = m_ViewProjectionMatrixInverse * pos;
return pos.xyz / pos.w;
}
void main(){
float depth = texture2D(m_DepthTexture,texCoord).r;
vec4 color = texture2D(m_Texture,texCoord);
//Discard shadow computation on the sky
if(depth == 1.0){
gl_FragColor = color;
return;
}
// get the vertex in world space
vec4 worldPos = vec4(getPosition(depth,texCoord),1.0);
// populate the light view matrices array and convert vertex to light viewProj space
vec4 projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos;
vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
float shadow = 0.0;
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);
}
shadow= shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
gl_FragColor = color * vec4(shadow, shadow, shadow, 1.0);
}

@ -0,0 +1,73 @@
MaterialDef Post Shadow {
MaterialParameters {
Int FilterMode
Boolean HardwareShadows
Texture2D ShadowMap0
Texture2D ShadowMap1
Texture2D ShadowMap2
Texture2D ShadowMap3
Float ShadowIntensity
Vector4 Splits
Matrix4 LightViewProjectionMatrix0
Matrix4 LightViewProjectionMatrix1
Matrix4 LightViewProjectionMatrix2
Matrix4 LightViewProjectionMatrix3
Float PCFEdge
Float ShadowMapSize
Matrix4 ViewProjectionMatrixInverse
Vector4 ViewProjectionMatrixRow2
Int NumSamples
Int NumSamplesDepth
Texture2D Texture
Texture2D DepthTexture
}
Technique {
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.vert
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.frag
WorldParameters {
WorldViewProjectionMatrix
}
Defines {
RESOLVE_MS : NumSamples
RESOLVE_DEPTH_MS : NumSamplesDepth
HARDWARE_SHADOWS : HardwareShadows
FILTER_MODE : FilterMode
PCFEDGE : PCFEdge
SHADOWMAP_SIZE : ShadowMapSize
}
}
Technique {
VertexShader GLSL100: Common/MatDefs/Shadow/PostShadowFilter.vert
FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowFilter.frag
WorldParameters {
WorldViewProjectionMatrix
}
Defines {
HARDWARE_SHADOWS : HardwareShadows
FILTER_MODE : FilterMode
PCFEDGE : PCFEdge
SHADOWMAP_SIZE : ShadowMapSize
}
}
}

@ -0,0 +1,10 @@
uniform mat4 g_WorldViewProjectionMatrix;
attribute vec4 inPosition;
attribute vec2 inTexCoord;
varying vec2 texCoord;
void main() {
gl_Position = inPosition * 2.0 - 1.0; //vec4(pos, 0.0, 1.0);
texCoord = inTexCoord;
}

@ -0,0 +1,84 @@
#import "Common/ShaderLib/MultiSample.glsllib"
#import "Common/ShaderLib/PssmShadows15.glsllib"
uniform COLORTEXTURE m_Texture;
uniform DEPTHTEXTURE m_DepthTexture;
uniform mat4 m_ViewProjectionMatrixInverse;
uniform vec4 m_ViewProjectionMatrixRow2;
in vec2 texCoord;
out vec4 outFragColor;
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);
uniform mat4 m_LightViewProjectionMatrix0;
uniform mat4 m_LightViewProjectionMatrix1;
uniform mat4 m_LightViewProjectionMatrix2;
uniform mat4 m_LightViewProjectionMatrix3;
vec3 getPosition(in float depth, in vec2 uv){
vec4 pos = vec4(uv, depth, 1.0) * 2.0 - 1.0;
pos = m_ViewProjectionMatrixInverse * pos;
return pos.xyz / pos.w;
}
vec4 main_multiSample(in int numSample){
float depth = fetchTextureSample(m_DepthTexture,texCoord,numSample).r;//getDepth(m_DepthTexture,texCoord).r;
vec4 color = fetchTextureSample(m_Texture,texCoord,numSample);
//Discard shadow computation on the sky
if(depth == 1.0){
return color;
}
// get the vertex in world space
vec4 worldPos = vec4(getPosition(depth,texCoord),1.0);
// populate the light view matrices array and convert vertex to light viewProj space
vec4 projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos;
vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
float shadow = 0.0;
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);
}
shadow= shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
return color * vec4(shadow, shadow, shadow, 1.0);
}
void main(){
#ifdef RESOLVE_MS
vec4 color = vec4(0.0);
for (int i = 0; i < m_NumSamples; i++){
color += main_multiSample(i);
}
outFragColor = color / m_NumSamples;
#else
outFragColor = main_multiSample(0);
#endif
}

@ -0,0 +1,12 @@
uniform mat4 g_WorldViewProjectionMatrix;
in vec4 inPosition;
in vec2 inTexCoord;
out vec2 texCoord;
void main() {
vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy;
gl_Position = vec4(pos, 0.0, 1.0);
texCoord = inTexCoord;
}

@ -1,176 +1,11 @@
#ifdef HARDWARE_SHADOWS
#define SHADOWMAP sampler2DShadow
#define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
#else
#define SHADOWMAP sampler2D
#define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
#endif
#if FILTER_MODE == 0
#define GETSHADOW Shadow_DoShadowCompare
#define KERNEL 1.0
#elif FILTER_MODE == 1
#ifdef HARDWARE_SHADOWS
#define GETSHADOW Shadow_DoShadowCompare
#else
#define GETSHADOW Shadow_DoBilinear_2x2
#endif
#define KERNEL 1.0
#elif FILTER_MODE == 2
#define GETSHADOW Shadow_DoDither_2x2
#define KERNEL 1.0
#elif FILTER_MODE == 3
#define GETSHADOW Shadow_DoPCF
#define KERNEL 4.0
#elif FILTER_MODE == 4
#define GETSHADOW Shadow_DoPCFPoisson
#define KERNEL 4
#elif FILTER_MODE == 5
#define GETSHADOW Shadow_DoPCF
#define KERNEL 8.0
#endif
uniform SHADOWMAP m_ShadowMap0;
uniform SHADOWMAP m_ShadowMap1;
uniform SHADOWMAP m_ShadowMap2;
uniform SHADOWMAP m_ShadowMap3;
uniform vec4 m_Splits;
uniform float m_ShadowIntensity;
#import "Common/ShaderLib/PssmShadows.glsllib"
varying float shadowPosition;
varying vec4 projCoord0;
varying vec4 projCoord1;
varying vec4 projCoord2;
varying vec4 projCoord3;
varying float shadowPosition;
const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
float scale = 1.0;
float Shadow_DoShadowCompareOffset(in SHADOWMAP tex, in vec4 projCoord, in vec2 offset){
vec4 coord = vec4(projCoord.xy + offset.xy * pixSize2, projCoord.zw);
return SHADOWCOMPARE(tex, coord);
}
float Shadow_DoShadowCompare(in SHADOWMAP tex, vec4 projCoord){
return SHADOWCOMPARE(tex, projCoord);
}
float Shadow_BorderCheck(in vec2 coord){
// Fastest, "hack" method (uses 4-5 instructions)
vec4 t = vec4(coord.xy, 0.0, 1.0);
t = step(t.wwxy, t.xyzz);
return dot(t,t);
}
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
float shadow = 0.0;
vec2 o = mod(floor(gl_FragCoord.xy), 2.0);
shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, 1.5) + o);
shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, 1.5) + o);
shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, -0.5) + o);
shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, -0.5) + o);
shadow *= 0.25 ;
return shadow;
}
float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
vec4 gather = vec4(0.0);
gather.x = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 0.0));
gather.y = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 0.0));
gather.z = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 1.0));
gather.w = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 1.0));
vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
vec2 mx = mix( gather.xz, gather.yw, f.x );
return mix( mx.x, mx.y, f.y );
}
float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
float bound = KERNEL * 0.5 - 0.5;
bound *= PCFEDGE;
for (float y = -bound; y <= bound; y += PCFEDGE){
for (float x = -bound; x <= bound; x += PCFEDGE){
shadow += clamp(Shadow_DoShadowCompareOffset(tex,projCoord,vec2(x,y)) +
border,
0.0, 1.0);
}
}
shadow = shadow / (KERNEL * KERNEL);
return shadow;
}
//12 tap poisson disk
/*
vec2 poissonDisk[12] =vec2[12]( vec2(-0.1711046, -0.425016),
vec2(-0.7829809, 0.2162201),
vec2(-0.2380269, -0.8835521),
vec2(0.4198045, 0.1687819),
vec2(-0.684418, -0.3186957),
vec2(0.6026866, -0.2587841),
vec2(-0.2412762, 0.3913516),
vec2(0.4720655, -0.7664126),
vec2(0.9571564, 0.2680693),
vec2(-0.5238616, 0.802707),
vec2(0.5653144, 0.60262),
vec2(0.0123658, 0.8627419));
*/
const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016);
const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201);
const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521);
const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819);
const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957);
const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841);
const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516);
const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126);
const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693);
const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707);
const vec2 poissonDisk10 = vec2(0.5653144, 0.60262);
const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419);
float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
vec2 texelSize = vec2( 4.0 * PCFEDGE * scale);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk0 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk1 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk2 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk3 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk4 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk5 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk6 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk7 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk8 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk9 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk10 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk11 * texelSize);
shadow = shadow * 0.08333333333;//this is divided by 12
return shadow;
}
#ifdef DISCARD_ALPHA
#ifdef COLOR_MAP
uniform sampler2D m_ColorMap;
@ -194,37 +29,22 @@ void main(){
}
#endif
vec4 shadowPerSplit = vec4(0.0);
float shadow;
//shadowPosition
float shadow = 0.0;
if(shadowPosition < m_Splits.x){
shadow= GETSHADOW(m_ShadowMap0, projCoord0);
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
}else if( shadowPosition < m_Splits.y){
scale = 0.5;
shadowBorderScale = 0.5;
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
}else if( shadowPosition < m_Splits.z){
scale = 0.25;
shadow= GETSHADOW(m_ShadowMap2, projCoord2);
shadowBorderScale = 0.25;
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
}else if( shadowPosition < m_Splits.w){
scale = 0.125;
shadow= GETSHADOW(m_ShadowMap3, projCoord3);
shadowBorderScale = 0.125;
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
}
/*
shadowPerSplit.x = GETSHADOW(m_ShadowMap0, projCoord0);
shadowPerSplit.y = GETSHADOW(m_ShadowMap1, projCoord1);
shadowPerSplit.z = GETSHADOW(m_ShadowMap2, projCoord2);
shadowPerSplit.w = GETSHADOW(m_ShadowMap3, projCoord3);
*/
/*
vec4 less = step( shadowPosition, m_Splits );
vec4 more = vec4(1.0) - step( shadowPosition, vec4(0.0, m_Splits.xyz) );
float shadow = dot(shadowPerSplit, less * more );
*/
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
gl_FragColor = vec4(shadow, shadow, shadow, 1.0);

@ -1,172 +1,11 @@
// Because gpu_shader5 is actually where those
// gather functions are declared to work on shadowmaps
#extension GL_ARB_gpu_shader5 : enable
#ifdef HARDWARE_SHADOWS
#define SHADOWMAP sampler2DShadow
#define SHADOWCOMPAREOFFSET(tex,coord,offset) textureProjOffset(tex, coord, offset)
#define SHADOWCOMPARE(tex,coord) textureProj(tex, coord)
#define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z)
#else
#define SHADOWMAP sampler2D
#define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z, textureProjOffset(tex, coord, offset).r)
#define SHADOWCOMPARE(tex,coord) step(coord.z, textureProj(tex, coord).r)
#define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy))
#endif
#if FILTER_MODE == 0
#define GETSHADOW SHADOWCOMPARE
#define KERNEL 1
#elif FILTER_MODE == 1
#ifdef HARDWARE_SHADOWS
#define GETSHADOW SHADOWCOMPARE
#else
#define GETSHADOW Shadow_DoBilinear_2x2
#endif
#define KERNEL 1
#elif FILTER_MODE == 2
#define GETSHADOW Shadow_DoDither_2x2
#define KERNEL 1
#elif FILTER_MODE == 3
#define GETSHADOW Shadow_DoPCF
#define KERNEL 4
#elif FILTER_MODE == 4
#define GETSHADOW Shadow_DoPCFPoisson
#define KERNEL 4
#elif FILTER_MODE == 5
#define GETSHADOW Shadow_DoPCF
#define KERNEL 8
#endif
#import "Common/ShaderLib/PssmShadows15.glsllib"
out vec4 outFragColor;
uniform SHADOWMAP m_ShadowMap0;
uniform SHADOWMAP m_ShadowMap1;
uniform SHADOWMAP m_ShadowMap2;
uniform SHADOWMAP m_ShadowMap3;
uniform vec4 m_Splits;
uniform float m_ShadowIntensity;
in float shadowPosition;
in vec4 projCoord0;
in vec4 projCoord1;
in vec4 projCoord2;
in vec4 projCoord3;
in float shadowPosition;
const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
float scale = 1.0;
float Shadow_BorderCheck(in vec2 coord){
// Fastest, "hack" method (uses 4-5 instructions)
vec4 t = vec4(coord.xy, 0.0, 1.0);
t = step(t.wwxy, t.xyzz);
return dot(t,t);
}
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
vec2 pixSize = pixSize2 * scale;
float shadow = 0.0;
ivec2 o = ivec2(mod(floor(gl_FragCoord.xy), 2.0));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, 1.5)+o), projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, 1.5)+o), projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, -0.5)+o), projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, -0.5)+o), projCoord.zw));
shadow *= 0.25;
return shadow;
}
float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
#ifdef GL_ARB_gpu_shader5
vec4 coord = vec4(projCoord.xyz / projCoord.www,0.0);
vec4 gather = SHADOWGATHER(tex, coord);
#else
vec4 gather = vec4(0.0);
gather.x = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 0));
gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0));
gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1));
gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
#endif
vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
vec2 mx = mix( gather.xz, gather.yw, f.x );
return mix( mx.x, mx.y, f.y );
}
float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
vec2 pixSize = pixSize2 * scale;
float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
float bound = KERNEL * 0.5 - 0.5;
bound *= PCFEDGE;
for (float y = -bound; y <= bound; y += PCFEDGE){
for (float x = -bound; x <= bound; x += PCFEDGE){
vec4 coord = vec4(projCoord.xy + vec2(x,y) * pixSize, projCoord.zw);
shadow += SHADOWCOMPARE(tex, coord);
}
}
shadow = shadow / (KERNEL * KERNEL);
return shadow;
}
//12 tap poisson disk
const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016);
const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201);
const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521);
const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819);
const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957);
const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841);
const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516);
const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126);
const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693);
const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707);
const vec2 poissonDisk10 = vec2(0.5653144, 0.60262);
const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419);
float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
//failed attempt to rotate the poisson disk to add jitter
//vec2 jitterFactor = vec2(sin(projCoord.x),cos(projCoord.x));// * 2.0f - 1.0f;
vec2 texelSize = pixSize2 * 4.0 * PCFEDGE * scale;
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk0 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk1 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk2 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk3 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk4 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk5 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk6 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk7 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk8 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk9 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk10 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk11 * texelSize, projCoord.zw));
shadow = shadow * 0.08333333333;//this is divided by 12
return shadow;
}
#ifdef DISCARD_ALPHA
#ifdef COLOR_MAP
@ -179,9 +18,7 @@ float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
#endif
void main(){
float shadow = 0.0;
#ifdef DISCARD_ALPHA
#ifdef COLOR_MAP
float alpha = texture2D(m_ColorMap,texCoord).a;
@ -193,20 +30,24 @@ void main(){
discard;
}
#endif
float shadow = 0.0;
if(shadowPosition < m_Splits.x){
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
}else if( shadowPosition < m_Splits.y){
scale = 0.5;
shadowBorderScale = 0.5;
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
}else if( shadowPosition < m_Splits.z){
scale = 0.25;
shadowBorderScale = 0.25;
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
}else if( shadowPosition < m_Splits.w){
scale = 0.125;
shadowBorderScale = 0.125;
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
}
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
shadow= shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
outFragColor = vec4(shadow, shadow, shadow, 1.0);
}

@ -0,0 +1,151 @@
#ifdef HARDWARE_SHADOWS
#define SHADOWMAP sampler2DShadow
#define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
#else
#define SHADOWMAP sampler2D
#define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
#endif
#if FILTER_MODE == 0
#define GETSHADOW Shadow_DoShadowCompare
#define KERNEL 1.0
#elif FILTER_MODE == 1
#ifdef HARDWARE_SHADOWS
#define GETSHADOW Shadow_DoShadowCompare
#else
#define GETSHADOW Shadow_DoBilinear_2x2
#endif
#define KERNEL 1.0
#elif FILTER_MODE == 2
#define GETSHADOW Shadow_DoDither_2x2
#define KERNEL 1.0
#elif FILTER_MODE == 3
#define GETSHADOW Shadow_DoPCF
#define KERNEL 4.0
#elif FILTER_MODE == 4
#define GETSHADOW Shadow_DoPCFPoisson
#define KERNEL 4
#elif FILTER_MODE == 5
#define GETSHADOW Shadow_DoPCF
#define KERNEL 8.0
#endif
uniform SHADOWMAP m_ShadowMap0;
uniform SHADOWMAP m_ShadowMap1;
uniform SHADOWMAP m_ShadowMap2;
uniform SHADOWMAP m_ShadowMap3;
uniform vec4 m_Splits;
uniform float m_ShadowIntensity;
const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
float shadowBorderScale = 1.0;
float Shadow_DoShadowCompareOffset(in SHADOWMAP tex, in vec4 projCoord, in vec2 offset){
vec4 coord = vec4(projCoord.xy + offset.xy * pixSize2 * shadowBorderScale, projCoord.zw);
return SHADOWCOMPARE(tex, coord);
}
float Shadow_DoShadowCompare(in SHADOWMAP tex, vec4 projCoord){
return SHADOWCOMPARE(tex, projCoord);
}
float Shadow_BorderCheck(in vec2 coord){
// Fastest, "hack" method (uses 4-5 instructions)
vec4 t = vec4(coord.xy, 0.0, 1.0);
t = step(t.wwxy, t.xyzz);
return dot(t,t);
}
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
float shadow = 0.0;
vec2 o = mod(floor(gl_FragCoord.xy), 2.0);
shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, 1.5) + o);
shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, 1.5) + o);
shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, -0.5) + o);
shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2( 0.5, -0.5) + o);
shadow *= 0.25 ;
return shadow;
}
float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
vec4 gather = vec4(0.0);
gather.x = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 0.0));
gather.y = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 0.0));
gather.z = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 1.0));
gather.w = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 1.0));
vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
vec2 mx = mix( gather.xz, gather.yw, f.x );
return mix( mx.x, mx.y, f.y );
}
float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
float bound = KERNEL * 0.5 - 0.5;
bound *= PCFEDGE;
for (float y = -bound; y <= bound; y += PCFEDGE){
for (float x = -bound; x <= bound; x += PCFEDGE){
shadow += clamp(Shadow_DoShadowCompareOffset(tex,projCoord,vec2(x,y)) +
border,
0.0, 1.0);
}
}
shadow = shadow / (KERNEL * KERNEL);
return shadow;
}
//12 tap poisson disk
const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016);
const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201);
const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521);
const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819);
const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957);
const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841);
const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516);
const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126);
const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693);
const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707);
const vec2 poissonDisk10 = vec2(0.5653144, 0.60262);
const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419);
float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
vec2 texelSize = vec2( 4.0 * PCFEDGE * shadowBorderScale);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk0 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk1 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk2 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk3 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk4 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk5 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk6 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk7 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk8 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk9 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk10 * texelSize);
shadow += Shadow_DoShadowCompareOffset(tex, projCoord , poissonDisk11 * texelSize);
shadow = shadow * 0.08333333333;//this is divided by 12
return shadow;
}

@ -0,0 +1,164 @@
// Because gpu_shader5 is actually where those
// gather functions are declared to work on shadowmaps
#extension GL_ARB_gpu_shader5 : enable
#ifdef HARDWARE_SHADOWS
#define SHADOWMAP sampler2DShadow
#define SHADOWCOMPAREOFFSET(tex,coord,offset) textureProjOffset(tex, coord, offset)
#define SHADOWCOMPARE(tex,coord) textureProj(tex, coord)
#define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z)
#else
#define SHADOWMAP sampler2D
#define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z, textureProjOffset(tex, coord, offset).r)
#define SHADOWCOMPARE(tex,coord) step(coord.z, textureProj(tex, coord).r)
#define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy))
#endif
#if FILTER_MODE == 0
#define GETSHADOW SHADOWCOMPARE
#define KERNEL 1
#elif FILTER_MODE == 1
#ifdef HARDWARE_SHADOWS
#define GETSHADOW SHADOWCOMPARE
#else
#define GETSHADOW Shadow_DoBilinear_2x2
#endif
#define KERNEL 1
#elif FILTER_MODE == 2
#define GETSHADOW Shadow_DoDither_2x2
#define KERNEL 1
#elif FILTER_MODE == 3
#define GETSHADOW Shadow_DoPCF
#define KERNEL 4
#elif FILTER_MODE == 4
#define GETSHADOW Shadow_DoPCFPoisson
#define KERNEL 4
#elif FILTER_MODE == 5
#define GETSHADOW Shadow_DoPCF
#define KERNEL 8
#endif
uniform SHADOWMAP m_ShadowMap0;
uniform SHADOWMAP m_ShadowMap1;
uniform SHADOWMAP m_ShadowMap2;
uniform SHADOWMAP m_ShadowMap3;
uniform vec4 m_Splits;
uniform float m_ShadowIntensity;
const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
float shadowBorderScale = 1.0;
float Shadow_BorderCheck(in vec2 coord){
// Fastest, "hack" method (uses 4-5 instructions)
vec4 t = vec4(coord.xy, 0.0, 1.0);
t = step(t.wwxy, t.xyzz);
return dot(t,t);
}
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
vec2 pixSize = pixSize2 * shadowBorderScale;
float shadow = 0.0;
ivec2 o = ivec2(mod(floor(gl_FragCoord.xy), 2.0));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, 1.5)+o), projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, 1.5)+o), projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2(-1.5, -0.5)+o), projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy+pixSize*(vec2( 0.5, -0.5)+o), projCoord.zw));
shadow *= 0.25;
return shadow;
}
float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
#ifdef GL_ARB_gpu_shader5
vec4 coord = vec4(projCoord.xyz / projCoord.www,0.0);
vec4 gather = SHADOWGATHER(tex, coord);
#else
vec4 gather = vec4(0.0);
gather.x = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 0));
gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0));
gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1));
gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
#endif
vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
vec2 mx = mix( gather.xz, gather.yw, f.x );
return mix( mx.x, mx.y, f.y );
}
float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
vec2 pixSize = pixSize2 * shadowBorderScale;
float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
float bound = KERNEL * 0.5 - 0.5;
bound *= PCFEDGE;
for (float y = -bound; y <= bound; y += PCFEDGE){
for (float x = -bound; x <= bound; x += PCFEDGE){
vec4 coord = vec4(projCoord.xy + vec2(x,y) * pixSize, projCoord.zw);
shadow += SHADOWCOMPARE(tex, coord);
}
}
shadow = shadow / (KERNEL * KERNEL);
return shadow;
}
//12 tap poisson disk
const vec2 poissonDisk0 = vec2(-0.1711046, -0.425016);
const vec2 poissonDisk1 = vec2(-0.7829809, 0.2162201);
const vec2 poissonDisk2 = vec2(-0.2380269, -0.8835521);
const vec2 poissonDisk3 = vec2(0.4198045, 0.1687819);
const vec2 poissonDisk4 = vec2(-0.684418, -0.3186957);
const vec2 poissonDisk5 = vec2(0.6026866, -0.2587841);
const vec2 poissonDisk6 = vec2(-0.2412762, 0.3913516);
const vec2 poissonDisk7 = vec2(0.4720655, -0.7664126);
const vec2 poissonDisk8 = vec2(0.9571564, 0.2680693);
const vec2 poissonDisk9 = vec2(-0.5238616, 0.802707);
const vec2 poissonDisk10 = vec2(0.5653144, 0.60262);
const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419);
float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0){
return 1.0;
}
vec2 texelSize = pixSize2 * 4.0 * PCFEDGE * shadowBorderScale;
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk0 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk1 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk2 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk3 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk4 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk5 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk6 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk7 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk8 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk9 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk10 * texelSize, projCoord.zw));
shadow += SHADOWCOMPARE(tex, vec4(projCoord.xy + poissonDisk11 * texelSize, projCoord.zw));
//this is divided by 12
return shadow * 0.08333333333;
}

@ -0,0 +1,262 @@
/*
* Copyright (c) 2009-2010 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.Vector3f;
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.shadow.PssmShadowRenderer.CompareMode;
import com.jme3.shadow.PssmShadowRenderer.FilterMode;
import com.jme3.texture.FrameBuffer;
import java.io.IOException;
/**
*
* This Filter does basically the same as a PssmShadowRenderer 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 PssmShadowFilter extends Filter {
private PssmShadowRenderer pssmRenderer;
private ViewPort viewPort;
/**
* Creates a PSSM 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 manager the application asset manager
* @param size 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 PssmShadowFilter(AssetManager manager, int size, int nbSplits) {
super("Post Shadow");
material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md");
pssmRenderer = new PssmShadowRenderer(manager, size, nbSplits, material);
pssmRenderer.needsfallBackMaterial = true;
}
@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) {
pssmRenderer.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) {
pssmRenderer.postQueue(queue);
}
@Override
protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
pssmRenderer.setPostShadowParams();
}
@Override
protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
pssmRenderer.initialize(renderManager, vp);
this.viewPort = vp;
}
/**
* returns the light direction used by the processor
* @return
*/
public Vector3f getDirection() {
return pssmRenderer.getDirection();
}
/**
* Sets the light direction to use to compute shadows
* @param direction
*/
public void setDirection(Vector3f direction) {
pssmRenderer.setDirection(direction);
}
/**
* returns the labda parameter<br>
* see {@link setLambda(float lambda)}
* @return lambda
*/
public float getLambda() {
return pssmRenderer.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) {
pssmRenderer.setLambda(lambda);
}
/**
* How far the shadows are rendered in the view
* see {@link setShadowZExtend(float zFar)}
* @return shadowZExtend
*/
public float getShadowZExtend() {
return pssmRenderer.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) {
pssmRenderer.setShadowZExtend(zFar);
}
/**
* returns the shdaow intensity<br>
* see {@link setShadowIntensity(float shadowIntensity)}
* @return shadowIntensity
*/
public float getShadowIntensity() {
return pssmRenderer.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) {
pssmRenderer.setShadowIntensity(shadowIntensity);
}
/**
* returns the edges thickness <br>
* see {@link setEdgesThickness(int edgesThickness)}
* @return edgesThickness
*/
public int getEdgesThickness() {
return pssmRenderer.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) {
pssmRenderer.setEdgesThickness(edgesThickness);
}
/**
* returns true if the PssmRenderer flushed the shadow queues
* @return flushQueues
*/
public boolean isFlushQueues() {
return pssmRenderer.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) {
pssmRenderer.setFlushQueues(flushQueues);
}
/**
* sets the shadow compare mode see {@link CompareMode} for more info
* @param compareMode
*/
final public void setCompareMode(CompareMode compareMode) {
pssmRenderer.setCompareMode(compareMode);
}
/**
* Sets the filtering mode for shadow edges see {@link FilterMode} for more info
* @param filterMode
*/
final public void setFilterMode(FilterMode filterMode) {
pssmRenderer.setFilterMode(filterMode);
}
@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);
}
}

@ -31,7 +31,6 @@ package com.jme3.shadow;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Matrix4f;
import com.jme3.math.Vector3f;
@ -48,7 +47,6 @@ 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.shader.VarType;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture.MagFilter;
@ -56,6 +54,8 @@ 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;
/**
* PssmShadow renderer use Parrallel Split Shadow Mapping technique (pssm)<br>
@ -99,13 +99,12 @@ public class PssmShadowRenderer implements SceneProcessor {
* 8x8 percentage-closer filtering is used. Shadows will be smoother
* at the cost of performance
*/
PCFPOISSON,
PCFPOISSON,
/**
* 8x8 percentage-closer filtering is used. Shadows will be smoother
* at the cost of performance
*/
PCF8
}
/**
@ -151,9 +150,16 @@ public class PssmShadowRenderer implements SceneProcessor {
private Vector3f[] points = new Vector3f[8];
private boolean flushQueues = true;
// define if the fallback material should be used.
private boolean needsfallBackMaterial = false;
protected boolean needsfallBackMaterial = false;
//Name of the post material technique
private String postTechniqueName = "PostShadow";
//flags to know when to change params in the materials
private boolean applyHWShadows = true;
private boolean applyFilterMode = true;
private boolean applyPCFEdge = true;
private boolean applyShadowIntensity = true;
//a list of material of the post shadow queue geometries.
private List<Material> matCache = new ArrayList<Material>();
/**
* Create a PSSM Shadow Renderer
@ -165,7 +171,6 @@ public class PssmShadowRenderer implements SceneProcessor {
*/
public PssmShadowRenderer(AssetManager manager, int size, int nbSplits) {
this(manager, size, nbSplits, new Material(manager, "Common/MatDefs/Shadow/PostShadowPSSM.j3md"));
}
/**
@ -174,10 +179,12 @@ public class PssmShadowRenderer implements SceneProcessor {
* @param manager the application asset manager
* @param size 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).
* @param postShadowMat the material used for post shadows if you need to override it *
* @param filterPass set this to true if you want the post shadow pass to be done in a Filter un screen space.
* @param postShadowMat the material used for post shadows if you need to override it
*/
//TODO remove the postShadowMat when we have shader injection....or remove this todo if we are in 2020.
public PssmShadowRenderer(AssetManager manager, int size, int nbSplits, Material postShadowMat) {
protected PssmShadowRenderer(AssetManager manager, int size, int nbSplits, Material postShadowMat) {
this.postshadowMat = postShadowMat;
assetManager = manager;
nbSplits = Math.max(Math.min(nbSplits, 4), 1);
this.nbSplits = nbSplits;
@ -194,9 +201,8 @@ public class PssmShadowRenderer implements SceneProcessor {
dummyTex = new Texture2D(size, size, Format.RGBA8);
preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");
this.postshadowMat = postShadowMat;
postshadowMat.setFloat("ShadowMapSize", size);
postshadowMat.setFloat("ShadowMapSize", size);
for (int i = 0; i < nbSplits; i++) {
lightViewProjectionsMatrices[i] = new Matrix4f();
shadowFB[i] = new FrameBuffer(size, size, 1);
@ -254,6 +260,7 @@ public class PssmShadowRenderer implements SceneProcessor {
}
}
}
applyFilterMode = true;
}
/**
@ -287,6 +294,7 @@ public class PssmShadowRenderer implements SceneProcessor {
}
}
postshadowMat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
applyHWShadows = true;
}
//debug function that create a displayable frustrum
@ -429,12 +437,12 @@ public class PssmShadowRenderer implements SceneProcessor {
}
//debug only : displays depth shadow maps
private void displayShadowMap(Renderer r) {
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].setPosition((128 * i) + (150 + 64 * (i + 1)), h / 20f);
dispPic[i].setWidth(128);
dispPic[i].setHeight(128);
dispPic[i].updateGeometricState();
@ -451,10 +459,15 @@ public class PssmShadowRenderer implements SceneProcessor {
}
public void postFrame(FrameBuffer out) {
Camera cam = viewPort.getCamera();
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);
@ -472,50 +485,79 @@ public class PssmShadowRenderer implements SceneProcessor {
renderManager.setCamera(cam, false);
}
if (debug) {
displayShadowMap(renderManager.getRenderer());
}
}
private void setMatParams() {
GeometryList l = viewPort.getQueue().getShadowQueueContent(ShadowMode.Receive);
//iteratin throught all the geometries of the list to set the material params
//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 setting the params.
//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) {
if (mat.getParam("Splits") == null) {
mat.setColor("Splits", splits);
postshadowMat.setColor("Splits", splits);
}
if (mat.getParam("ShadowMapSize") == null) {
mat.setFloat("ShadowMapSize", shadowMapSize);
}
for (int j = 0; j < nbSplits; j++) {
mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
}
if (mat.getParam("ShadowMap0") == null) {
for (int j = 0; j < nbSplits; j++) {
mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
mat.setTexture("ShadowMap" + j, shadowMaps[j]);
}
}
if (applyHWShadows || mat.getParam("HardwareShadows") == null) {
mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
applyHWShadows = false;
}
if (applyFilterMode || mat.getParam("FilterMode") == null) {
mat.setInt("FilterMode", filterMode.ordinal());
applyFilterMode = false;
}
if (mat.getParam("PCFEdge") == null || applyPCFEdge) {
mat.setFloat("PCFEdge", edgesThickness);
applyPCFEdge = false;
}
if (mat.getParam("ShadowIntensity") == null || applyShadowIntensity) {
mat.setFloat("ShadowIntensity", shadowIntensity);
if(mat.getParam("ShadowMapSize") == null){
mat.setFloat("ShadowMapSize", shadowMapSize);
}
} else {
needsfallBackMaterial = true;
applyShadowIntensity = false;
}
}
}
//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) {
postshadowMat.setColor("Splits", splits);
for (int j = 0; j < nbSplits; j++) {
postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
}
setPostShadowParams();
}
}
protected void setPostShadowParams() {
postshadowMat.setColor("Splits", splits);
for (int j = 0; j < nbSplits; j++) {
postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
}
}
public void preFrame(float tpf) {
}
@ -583,6 +625,7 @@ public class PssmShadowRenderer implements SceneProcessor {
final public void setShadowIntensity(float shadowIntensity) {
this.shadowIntensity = shadowIntensity;
postshadowMat.setFloat("ShadowIntensity", shadowIntensity);
applyShadowIntensity = true;
}
/**
@ -602,6 +645,7 @@ public class PssmShadowRenderer implements SceneProcessor {
this.edgesThickness = Math.max(1, Math.min(edgesThickness, 10));
this.edgesThickness *= 0.1f;
postshadowMat.setFloat("PCFEdge", edgesThickness);
applyPCFEdge = true;
}
/**

@ -44,6 +44,8 @@ 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.post.filters.FXAAFilter;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
@ -53,6 +55,7 @@ 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.PssmShadowFilter;
import com.jme3.shadow.PssmShadowRenderer;
import com.jme3.shadow.PssmShadowRenderer.CompareMode;
import com.jme3.shadow.PssmShadowRenderer.FilterMode;
@ -65,13 +68,12 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
private Spatial[] obj;
private Material[] mat;
private boolean renderShadows = true;
private boolean hardwareShadows = false;
private PssmShadowRenderer pssmRenderer;
private PssmShadowFilter pssmFilter;
private Geometry ground;
private Material matGroundU;
private Material matGroundL;
private Material matGroundU;
private Material matGroundL;
public static void main(String[] args) {
TestPssmShadow app = new TestPssmShadow();
@ -109,15 +111,15 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
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);
@ -146,16 +148,35 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
loadScene();
pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 3);
// pssmRenderer.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
pssmRenderer.setDirection(new Vector3f(0.5973172f, -0.16583486f, 0.7846725f).normalizeLocal());
pssmRenderer.setLambda(0.55f);
pssmRenderer.setShadowIntensity(0.6f);
pssmRenderer.setCompareMode(CompareMode.Software);
pssmRenderer.setFilterMode(FilterMode.Dither);
pssmRenderer.displayDebug();
// pssmRenderer.displayDebug();
viewPort.addProcessor(pssmRenderer);
pssmFilter = new PssmShadowFilter(assetManager, 1024, 3);
//pssmFilter.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
pssmRenderer.setDirection(new Vector3f(0.5973172f, -0.16583486f, 0.7846725f).normalizeLocal());
pssmFilter.setLambda(0.55f);
pssmFilter.setShadowIntensity(0.6f);
pssmFilter.setCompareMode(CompareMode.Software);
pssmFilter.setFilterMode(FilterMode.Dither);
pssmFilter.setEnabled(false);
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
// fpp.setNumSamples(4);
fpp.addFilter(pssmFilter);
viewPort.addProcessor(fpp);
initInputs();
}
BitmapText infoText;
BitmapText infoText;
private void initInputs() {
/** Write text on the screen (HUD) */
@ -174,7 +195,7 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
inputManager.addMapping("lambdaDown", new KeyTrigger(KeyInput.KEY_J));
inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN));
inputManager.addMapping("switchGroundMat", new KeyTrigger(KeyInput.KEY_M));
inputManager.addListener(this, "lambdaUp", "lambdaDown", "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown","changeFiltering","switchGroundMat");
inputManager.addListener(this, "lambdaUp", "lambdaDown", "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown", "changeFiltering", "switchGroundMat");
}
private void print(String str) {
@ -212,60 +233,83 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
}
};
int filteringIndex = 2;
int renderModeIndex = 0;
public void onAction(String name, boolean keyPressed, float tpf) {
if (name.equals("toggle") && keyPressed) {
if (renderShadows) {
renderShadows = false;
viewPort.removeProcessor(pssmRenderer);
} else {
renderShadows = true;
viewPort.addProcessor(pssmRenderer);
renderModeIndex += 1;
renderModeIndex %= 3;
switch (renderModeIndex) {
case 0:
viewPort.addProcessor(pssmRenderer);
break;
case 1:
viewPort.removeProcessor(pssmRenderer);
pssmFilter.setEnabled(true);
break;
case 2:
pssmFilter.setEnabled(false);
break;
}
} else if (name.equals("toggleHW") && keyPressed) {
hardwareShadows = !hardwareShadows;
pssmRenderer.setCompareMode(hardwareShadows ? CompareMode.Hardware : CompareMode.Software);
pssmFilter.setCompareMode(hardwareShadows ? CompareMode.Hardware : CompareMode.Software);
System.out.println("HW Shadows: " + hardwareShadows);
}
//
// renderShadows = true;
// viewPort.addProcessor(pssmRenderer);
if (name.equals("changeFiltering") && keyPressed) {
filteringIndex = (filteringIndex + 1) % FilterMode.values().length;
FilterMode m = FilterMode.values()[filteringIndex];
pssmRenderer.setFilterMode(m);
pssmFilter.setFilterMode(m);
print("Filter mode : " + m.toString());
}
if (name.equals("lambdaUp") && keyPressed) {
pssmRenderer.setLambda(pssmRenderer.getLambda() + 0.01f);
pssmFilter.setLambda(pssmRenderer.getLambda() + 0.01f);
System.out.println("Lambda : " + pssmRenderer.getLambda());
} else if (name.equals("lambdaDown") && keyPressed) {
pssmRenderer.setLambda(pssmRenderer.getLambda() - 0.01f);
pssmFilter.setLambda(pssmRenderer.getLambda() - 0.01f);
System.out.println("Lambda : " + pssmRenderer.getLambda());
}
if (name.equals("ShadowUp") && keyPressed) {
pssmRenderer.setShadowIntensity(pssmRenderer.getShadowIntensity() + 0.1f);
pssmFilter.setShadowIntensity(pssmRenderer.getShadowIntensity() + 0.1f);
System.out.println("Shadow intensity : " + pssmRenderer.getShadowIntensity());
}
if (name.equals("ShadowDown") && keyPressed) {
pssmRenderer.setShadowIntensity(pssmRenderer.getShadowIntensity() - 0.1f);
pssmFilter.setShadowIntensity(pssmRenderer.getShadowIntensity() - 0.1f);
System.out.println("Shadow intensity : " + pssmRenderer.getShadowIntensity());
}
if (name.equals("ThicknessUp") && keyPressed) {
pssmRenderer.setEdgesThickness(pssmRenderer.getEdgesThickness() + 1);
pssmFilter.setEdgesThickness(pssmRenderer.getEdgesThickness() + 1);
System.out.println("Shadow thickness : " + pssmRenderer.getEdgesThickness());
}
if (name.equals("ThicknessDown") && keyPressed) {
pssmRenderer.setEdgesThickness(pssmRenderer.getEdgesThickness() - 1);
pssmFilter.setEdgesThickness(pssmRenderer.getEdgesThickness() - 1);
System.out.println("Shadow thickness : " + pssmRenderer.getEdgesThickness());
}
if (name.equals("switchGroundMat") && keyPressed) {
if(ground.getMaterial() == matGroundL){
if (ground.getMaterial() == matGroundL) {
ground.setMaterial(matGroundU);
}else{
} else {
ground.setMaterial(matGroundL);
}
}
}
}
}

@ -0,0 +1,182 @@
/*
* Copyright (c) 2009-2010 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.PointLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.shadow.PssmShadowRenderer;
import com.jme3.shadow.PssmShadowRenderer.CompareMode;
import com.jme3.shadow.PssmShadowRenderer.FilterMode;
import com.jme3.util.TangentBinormalGenerator;
import java.util.logging.Level;
import java.util.logging.Logger;
public class TestShadowsPerf extends SimpleApplication {
float angle;
PointLight pl;
Spatial lightMdl;
public static void main(String[] args) {
TestShadowsPerf app = new TestShadowsPerf();
app.start();
}
Geometry sphere;
Material mat;
@Override
public void simpleInitApp() {
Logger.getLogger("com.jme3").setLevel(Level.SEVERE);
flyCam.setMoveSpeed(50);
flyCam.setEnabled(false);
viewPort.setBackgroundColor(ColorRGBA.DarkGray);
cam.setLocation(new Vector3f(-53.952988f, 27.15874f, -32.875023f));
cam.setRotation(new Quaternion(0.1564309f, 0.6910534f, -0.15713608f, 0.6879555f));
// cam.setLocation(new Vector3f(53.64627f, 130.56f, -11.247704f));
// cam.setRotation(new Quaternion(-6.5737107E-4f, 0.76819664f, -0.64021313f, -7.886125E-4f));
////
cam.setFrustumFar(500);
mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
Box b = new Box(Vector3f.ZERO, 800, 1, 700);
b.scaleTextureCoordinates(new Vector2f(50, 50));
Geometry ground = new Geometry("ground", b);
ground.setMaterial(mat);
rootNode.attachChild(ground);
ground.setShadowMode(ShadowMode.Receive);
Sphere sphMesh = new Sphere(32, 32, 1);
sphMesh.setTextureMode(Sphere.TextureMode.Projected);
sphMesh.updateGeometry(32, 32, 1, false, false);
TangentBinormalGenerator.generate(sphMesh);
sphere = new Geometry("Rock Ball", sphMesh);
sphere.setLocalTranslation(0, 5, 0);
sphere.setMaterial(mat);
sphere.setShadowMode(ShadowMode.CastAndReceive);
rootNode.attachChild(sphere);
DirectionalLight dl = new DirectionalLight();
dl.setDirection(new Vector3f(0, -1, 0).normalizeLocal());
dl.setColor(ColorRGBA.White);
rootNode.addLight(dl);
AmbientLight al = new AmbientLight();
al.setColor(ColorRGBA.White.mult(0.7f));
rootNode.addLight(al);
//rootNode.setShadowMode(ShadowMode.CastAndReceive);
createballs();
final PssmShadowRenderer pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 4);
viewPort.addProcessor(pssmRenderer);
//
// final PssmShadowFilter pssmRenderer = new PssmShadowFilter(assetManager, 1024, 4);
// FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
// fpp.addFilter(pssmRenderer);
// viewPort.addProcessor(fpp);
pssmRenderer.setDirection(dl.getDirection());
pssmRenderer.setLambda(0.55f);
pssmRenderer.setShadowIntensity(0.55f);
pssmRenderer.setCompareMode(CompareMode.Software);
pssmRenderer.setFilterMode(FilterMode.PCF4);
//pssmRenderer.displayDebug();
inputManager.addListener(new ActionListener() {
public void onAction(String name, boolean isPressed, float tpf) {
if (name.equals("display") && isPressed) {
//pssmRenderer.debugFrustrums();
System.out.println("tetetetet");
}
if (name.equals("add") && isPressed) {
createballs();
}
}
}, "display", "add");
inputManager.addMapping("display", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addMapping("add", new KeyTrigger(KeyInput.KEY_RETURN));
}
int val = 0;
private void createballs() {
System.out.println((frames / time) + ";" + val);
for (int i = val; i < val + 200; i++) {
Geometry s = sphere.clone().clone(false);
s.setMaterial(mat);
s.setLocalTranslation(i - 30, 5, (((i) * 2) % 40) - 50);
s.setShadowMode(ShadowMode.CastAndReceive);
rootNode.attachChild(s);
}
if (val == 300) {
//stop();
}
val += 1;
time = 0;
frames = 0;
}
float time;
float frames = 0;
@Override
public void simpleUpdate(float tpf) {
time += tpf;
frames++;
if (time > 1) {
//createballs();
}
}
}
Loading…
Cancel
Save