Shadow system refactoring.
- 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-0572b91ccdca
This commit is contained in:
parent
304ecb4bf7
commit
0dadaa80f5
@ -200,8 +200,8 @@ MaterialDef Phong Lighting {
|
||||
|
||||
|
||||
Technique PostShadow15{
|
||||
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM.vert
|
||||
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM15.frag
|
||||
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert
|
||||
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag
|
||||
|
||||
WorldParameters {
|
||||
WorldViewProjectionMatrix
|
||||
@ -228,8 +228,8 @@ MaterialDef Phong Lighting {
|
||||
}
|
||||
|
||||
Technique PostShadow{
|
||||
VertexShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.vert
|
||||
FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.frag
|
||||
VertexShader GLSL100: Common/MatDefs/Shadow/PostShadow.vert
|
||||
FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag
|
||||
|
||||
WorldParameters {
|
||||
WorldViewProjectionMatrix
|
||||
|
@ -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,8 +1,59 @@
|
||||
MaterialDef Post Shadow {
|
||||
|
||||
MaterialParameters {
|
||||
Texture2D ShadowMap
|
||||
Matrix4 LightViewProjectionMatrix
|
||||
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/PostShadow.vert
|
||||
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.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 {
|
||||
@ -15,7 +66,13 @@ MaterialDef Post Shadow {
|
||||
}
|
||||
|
||||
Defines {
|
||||
NO_SHADOW2DPROJ
|
||||
HARDWARE_SHADOWS : HardwareShadows
|
||||
FILTER_MODE : FilterMode
|
||||
PCFEDGE : PCFEdge
|
||||
SHADOWMAP_SIZE : ShadowMapSize
|
||||
FADE : FadeInfo
|
||||
PSSM : Splits
|
||||
POINTLIGHT : LightViewProjectionMatrix5
|
||||
}
|
||||
|
||||
RenderState {
|
||||
|
@ -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 projCoord;
|
||||
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 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);
|
||||
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);
|
||||
#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
|
||||
}
|
72
engine/src/core-data/Common/MatDefs/Shadow/PostShadow15.frag
Normal file
72
engine/src/core-data/Common/MatDefs/Shadow/PostShadow15.frag
Normal file
@ -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,4 +1,4 @@
|
||||
#import "Common/ShaderLib/PssmShadows.glsllib"
|
||||
#import "Common/ShaderLib/Shadows.glsllib"
|
||||
|
||||
uniform sampler2D m_Texture;
|
||||
uniform sampler2D m_DepthTexture;
|
||||
@ -59,46 +59,21 @@ void main(){
|
||||
#endif
|
||||
|
||||
float shadow = 1.0;
|
||||
#ifdef PSSM
|
||||
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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
|
||||
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
|
||||
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
|
||||
|
@ -11,8 +11,7 @@ MaterialDef Post Shadow {
|
||||
//pointLights
|
||||
Texture2D ShadowMap4
|
||||
Texture2D ShadowMap5
|
||||
Vector3 LightPos
|
||||
|
||||
|
||||
Float ShadowIntensity
|
||||
Vector4 Splits
|
||||
Vector2 FadeInfo
|
||||
@ -23,7 +22,8 @@ MaterialDef Post Shadow {
|
||||
Matrix4 LightViewProjectionMatrix3
|
||||
//pointLight
|
||||
Matrix4 LightViewProjectionMatrix4
|
||||
Matrix4 LightViewProjectionMatrix5
|
||||
Matrix4 LightViewProjectionMatrix5
|
||||
Vector3 LightPos
|
||||
|
||||
Float PCFEdge
|
||||
|
||||
@ -44,7 +44,7 @@ MaterialDef Post Shadow {
|
||||
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowFilter15.frag
|
||||
|
||||
WorldParameters {
|
||||
WorldViewProjectionMatrix
|
||||
WorldViewProjectionMatrix
|
||||
}
|
||||
|
||||
Defines {
|
||||
|
@ -1,5 +1,5 @@
|
||||
#import "Common/ShaderLib/MultiSample.glsllib"
|
||||
#import "Common/ShaderLib/PssmShadows15.glsllib"
|
||||
#import "Common/ShaderLib/Shadows15.glsllib"
|
||||
|
||||
|
||||
uniform COLORTEXTURE m_Texture;
|
||||
@ -59,53 +59,29 @@ vec4 main_multiSample(in int numSample){
|
||||
#endif
|
||||
|
||||
float shadow = 1.0;
|
||||
#ifdef PSSM
|
||||
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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
|
||||
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
|
||||
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);
|
||||
|
||||
return color * 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);
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
#define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
|
||||
#else
|
||||
#define SHADOWMAP sampler2D
|
||||
#define SHADOWCOMPARE(tex,coord) step(coord.z / coord.w, texture2DProj(tex, coord).r)
|
||||
#define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
|
||||
#endif
|
||||
|
||||
#if FILTER_MODE == 0
|
||||
@ -49,29 +49,31 @@ 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){
|
||||
float Shadow_DoShadowCompareOffset(SHADOWMAP tex, vec4 projCoord, 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){
|
||||
float Shadow_DoShadowCompare(SHADOWMAP tex, vec4 projCoord){
|
||||
return SHADOWCOMPARE(tex, projCoord);
|
||||
}
|
||||
|
||||
float Shadow_BorderCheck(in vec2 coord){
|
||||
#ifdef PSSM
|
||||
float Shadow_BorderCheck(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);
|
||||
#else
|
||||
//fix me this is a big fat hack to avoid issues whith point lights,
|
||||
//this function fail to return correct values, but it works with PSSM
|
||||
return 0.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
||||
float Shadow_Nearest(SHADOWMAP tex, vec4 projCoord){
|
||||
float border = Shadow_BorderCheck(projCoord.xy);
|
||||
if (border > 0.0){
|
||||
return 1.0;
|
||||
}
|
||||
return Shadow_DoShadowCompare(tex,projCoord);
|
||||
}
|
||||
|
||||
float Shadow_DoDither_2x2(SHADOWMAP tex, vec4 projCoord){
|
||||
float border = Shadow_BorderCheck(projCoord.xy);
|
||||
if (border > 0.0)
|
||||
return 1.0;
|
||||
@ -87,7 +89,7 @@ float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
||||
return shadow;
|
||||
}
|
||||
|
||||
float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
||||
float Shadow_DoBilinear_2x2(SHADOWMAP tex, vec4 projCoord){
|
||||
float border = Shadow_BorderCheck(projCoord.xy);
|
||||
if (border > 0.0)
|
||||
return 1.0;
|
||||
@ -102,7 +104,7 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
||||
return mix( mx.x, mx.y, f.y );
|
||||
}
|
||||
|
||||
float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
|
||||
float Shadow_DoPCF(SHADOWMAP tex, vec4 projCoord){
|
||||
float shadow = 0.0;
|
||||
float border = Shadow_BorderCheck(projCoord.xy);
|
||||
if (border > 0.0)
|
||||
@ -136,7 +138,7 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
|
||||
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_DoPCFPoisson(SHADOWMAP tex, vec4 projCoord){
|
||||
float shadow = 0.0;
|
||||
float border = Shadow_BorderCheck(projCoord.xy);
|
||||
if (border > 0.0)
|
||||
@ -161,3 +163,69 @@ float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
|
||||
return shadow;
|
||||
}
|
||||
|
||||
|
||||
#ifdef POINTLIGHT
|
||||
float getPointLightShadows(vec4 worldPos,vec3 lightPos,
|
||||
SHADOWMAP shadowMap0,SHADOWMAP shadowMap1,SHADOWMAP shadowMap2,SHADOWMAP shadowMap3,SHADOWMAP shadowMap4,SHADOWMAP shadowMap5,
|
||||
vec4 projCoord0,vec4 projCoord1,vec4 projCoord2,vec4 projCoord3,vec4 projCoord4,vec4 projCoord5){
|
||||
float shadow = 1.0;
|
||||
vec3 vect = worldPos.xyz - 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(shadowMap0, projCoord0 / projCoord0.w);
|
||||
}else{
|
||||
shadow = GETSHADOW(shadowMap1, projCoord1 / projCoord1.w);
|
||||
}
|
||||
}else if(maxComp == absv.z){
|
||||
if(vect.z < 0.0){
|
||||
shadow = GETSHADOW(shadowMap2, projCoord2 / projCoord2.w);
|
||||
}else{
|
||||
shadow = GETSHADOW(shadowMap3, projCoord3 / projCoord3.w);
|
||||
}
|
||||
}else if(maxComp == absv.x){
|
||||
if(vect.x < 0.0){
|
||||
shadow = GETSHADOW(shadowMap4, projCoord4 / projCoord4.w);
|
||||
}else{
|
||||
shadow = GETSHADOW(shadowMap5, projCoord5 / projCoord5.w);
|
||||
}
|
||||
}
|
||||
return shadow;
|
||||
}
|
||||
#else
|
||||
#ifdef PSSM
|
||||
float getDirectionalLightShadows(vec4 splits,float shadowPosition,
|
||||
SHADOWMAP shadowMap0,SHADOWMAP shadowMap1,SHADOWMAP shadowMap2,SHADOWMAP shadowMap3,
|
||||
vec4 projCoord0,vec4 projCoord1,vec4 projCoord2,vec4 projCoord3){
|
||||
float shadow = 1.0;
|
||||
if(shadowPosition < splits.x){
|
||||
shadow = GETSHADOW(shadowMap0, projCoord0 );
|
||||
}else if( shadowPosition < splits.y){
|
||||
shadowBorderScale = 0.5;
|
||||
shadow = GETSHADOW(shadowMap1, projCoord1);
|
||||
}else if( shadowPosition < splits.z){
|
||||
shadowBorderScale = 0.25;
|
||||
shadow = GETSHADOW(shadowMap2, projCoord2);
|
||||
}else if( shadowPosition < splits.w){
|
||||
shadowBorderScale = 0.125;
|
||||
shadow = GETSHADOW(shadowMap3, projCoord3);
|
||||
}
|
||||
return shadow;
|
||||
}
|
||||
#else
|
||||
float getSpotLightShadows(SHADOWMAP shadowMap, vec4 projCoord){
|
||||
float shadow = 1.0;
|
||||
projCoord /= projCoord.w;
|
||||
shadow = GETSHADOW(shadowMap, projCoord);
|
||||
|
||||
//a small falloff to make the shadow blend nicely into the not lighten
|
||||
//we translate the texture coordinate value to a -1,1 range so the length
|
||||
//of the texture coordinate vector is actually the radius of the lighten area on the ground
|
||||
projCoord = projCoord * 2.0 - 1.0;
|
||||
float fallOff = ( length(projCoord.xy) - 0.9 ) / 0.1;
|
||||
return mix(shadow,1.0,clamp(fallOff,0.0,1.0));
|
||||
|
||||
}
|
||||
#endif
|
||||
#endif
|
@ -9,18 +9,18 @@
|
||||
#define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z)
|
||||
#else
|
||||
#define SHADOWMAP sampler2D
|
||||
#define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z / coord.w, textureProjOffset(tex, coord, offset).r)
|
||||
#define SHADOWCOMPARE(tex,coord) step(coord.z / coord.w, textureProj(tex, coord).r)
|
||||
#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 GETSHADOW Shadow_Nearest
|
||||
#define KERNEL 1
|
||||
#elif FILTER_MODE == 1
|
||||
#ifdef HARDWARE_SHADOWS
|
||||
#define GETSHADOW SHADOWCOMPARE
|
||||
#define GETSHADOW Shadow_Nearest
|
||||
#else
|
||||
#define GETSHADOW Shadow_DoBilinear_2x2
|
||||
#endif
|
||||
@ -59,16 +59,18 @@ const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
|
||||
float shadowBorderScale = 1.0;
|
||||
|
||||
float Shadow_BorderCheck(in vec2 coord){
|
||||
#ifdef PSSM
|
||||
// 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);
|
||||
#else
|
||||
//fix me this is a big fat hack to avoid issues whith point lights,
|
||||
//this function fail to return correct values, but it works with PSSM
|
||||
return 0.0;
|
||||
#endif
|
||||
return dot(t,t);
|
||||
}
|
||||
|
||||
float Shadow_Nearest(in SHADOWMAP tex, in vec4 projCoord){
|
||||
float border = Shadow_BorderCheck(projCoord.xy);
|
||||
if (border > 0.0){
|
||||
return 1.0;
|
||||
}
|
||||
return SHADOWCOMPARE(tex,projCoord);
|
||||
}
|
||||
|
||||
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
||||
@ -109,9 +111,9 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
||||
return mix( mx.x, mx.y, f.y );
|
||||
}
|
||||
|
||||
float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
|
||||
|
||||
vec2 pixSize = pixSize2 * shadowBorderScale;
|
||||
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)
|
||||
@ -146,7 +148,7 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
|
||||
const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419);
|
||||
|
||||
|
||||
float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
|
||||
float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
|
||||
|
||||
float shadow = 0.0;
|
||||
float border = Shadow_BorderCheck(projCoord.xy);
|
||||
@ -173,4 +175,68 @@ float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
|
||||
return shadow * 0.08333333333;
|
||||
}
|
||||
|
||||
#ifdef POINTLIGHT
|
||||
float getPointLightShadows(in vec4 worldPos,in vec3 lightPos,
|
||||
in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3,in SHADOWMAP shadowMap4,in SHADOWMAP shadowMap5,
|
||||
in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3,in vec4 projCoord4,in vec4 projCoord5){
|
||||
float shadow = 1.0;
|
||||
vec3 vect = worldPos.xyz - 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(shadowMap0, projCoord0 / projCoord0.w);
|
||||
}else{
|
||||
shadow = GETSHADOW(shadowMap1, projCoord1 / projCoord1.w);
|
||||
}
|
||||
}else if(maxComp == absv.z){
|
||||
if(vect.z < 0.0){
|
||||
shadow = GETSHADOW(shadowMap2, projCoord2 / projCoord2.w);
|
||||
}else{
|
||||
shadow = GETSHADOW(shadowMap3, projCoord3 / projCoord3.w);
|
||||
}
|
||||
}else if(maxComp == absv.x){
|
||||
if(vect.x < 0.0){
|
||||
shadow = GETSHADOW(shadowMap4, projCoord4 / projCoord4.w);
|
||||
}else{
|
||||
shadow = GETSHADOW(shadowMap5, projCoord5 / projCoord5.w);
|
||||
}
|
||||
}
|
||||
return shadow;
|
||||
}
|
||||
#else
|
||||
#ifdef PSSM
|
||||
float getDirectionalLightShadows(in vec4 splits,in float shadowPosition,
|
||||
in SHADOWMAP shadowMap0,in SHADOWMAP shadowMap1,in SHADOWMAP shadowMap2,in SHADOWMAP shadowMap3,
|
||||
in vec4 projCoord0,in vec4 projCoord1,in vec4 projCoord2,in vec4 projCoord3){
|
||||
float shadow = 1.0;
|
||||
if(shadowPosition < splits.x){
|
||||
shadow = GETSHADOW(shadowMap0, projCoord0 );
|
||||
}else if( shadowPosition < splits.y){
|
||||
shadowBorderScale = 0.5;
|
||||
shadow = GETSHADOW(shadowMap1, projCoord1);
|
||||
}else if( shadowPosition < splits.z){
|
||||
shadowBorderScale = 0.25;
|
||||
shadow = GETSHADOW(shadowMap2, projCoord2);
|
||||
}else if( shadowPosition < splits.w){
|
||||
shadowBorderScale = 0.125;
|
||||
shadow = GETSHADOW(shadowMap3, projCoord3);
|
||||
}
|
||||
return shadow;
|
||||
}
|
||||
#else
|
||||
float getSpotLightShadows(in SHADOWMAP shadowMap,in vec4 projCoord){
|
||||
float shadow = 1.0;
|
||||
projCoord /= projCoord.w;
|
||||
shadow = GETSHADOW(shadowMap,projCoord);
|
||||
|
||||
//a small falloff to make the shadow blend nicely into the not lighten
|
||||
//we translate the texture coordinate value to a -1,1 range so the length
|
||||
//of the texture coordinate vector is actually the radius of the lighten area on the ground
|
||||
projCoord = projCoord * 2.0 - 1.0;
|
||||
float fallOff = ( length(projCoord.xy) - 0.9 ) / 0.1;
|
||||
return mix(shadow,1.0,clamp(fallOff,0.0,1.0));
|
||||
|
||||
}
|
||||
#endif
|
||||
#endif
|
233
engine/src/core/com/jme3/shadow/AbstractShadowFilter.java
Normal file
233
engine/src/core/com/jme3/shadow/AbstractShadowFilter.java
Normal file
@ -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);
|
||||
|
||||
}
|
||||
}
|
563
engine/src/core/com/jme3/shadow/AbstractShadowRenderer.java
Normal file
563
engine/src/core/com/jme3/shadow/AbstractShadowRenderer.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -52,7 +52,9 @@ import com.jme3.ui.Picture;
|
||||
* it's useful to render shadows in a small scene, but edges might look a bit jagged.
|
||||
*
|
||||
* @author Kirill Vainer
|
||||
* @deprecated use {@link DirectionalLightShadowRenderer} with one split.
|
||||
*/
|
||||
@Deprecated
|
||||
public class BasicShadowRenderer implements SceneProcessor {
|
||||
|
||||
private RenderManager renderManager;
|
||||
@ -79,7 +81,7 @@ public class BasicShadowRenderer implements SceneProcessor {
|
||||
shadowCam = new Camera(size, size);
|
||||
|
||||
preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");
|
||||
postshadowMat = new Material(manager, "Common/MatDefs/Shadow/PostShadow.j3md");
|
||||
postshadowMat = new Material(manager, "Common/MatDefs/Shadow/BasicPostShadow.j3md");
|
||||
postshadowMat.setTexture("ShadowMap", shadowMap);
|
||||
|
||||
dispPic.setTexture(manager, shadowMap, false);
|
||||
|
48
engine/src/core/com/jme3/shadow/CompareMode.java
Normal file
48
engine/src/core/com/jme3/shadow/CompareMode.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -31,45 +31,50 @@
|
||||
*/
|
||||
package com.jme3.shadow;
|
||||
|
||||
import com.jme3.light.DirectionalLight;
|
||||
import com.jme3.light.Light;
|
||||
import com.jme3.light.PointLight;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.renderer.Camera;
|
||||
|
||||
/**
|
||||
* Creates a camera according to a light
|
||||
* Handy to compute projection matrix of a light
|
||||
* @author Kirill Vainer
|
||||
* <code>ShadowEdgeFiltering</code> specifies how shadows are filtered
|
||||
*/
|
||||
public class ShadowCamera {
|
||||
|
||||
private Vector3f[] points = new Vector3f[8];
|
||||
private Light target;
|
||||
|
||||
public ShadowCamera(Light target) {
|
||||
this.target = target;
|
||||
for (int i = 0; i < points.length; i++) {
|
||||
points[i] = new Vector3f();
|
||||
}
|
||||
}
|
||||
public enum EdgeFilteringMode {
|
||||
|
||||
/**
|
||||
* Updates the camera view direction and position based on the light
|
||||
* Shadows are not filtered. Nearest sample is used, causing in blocky
|
||||
* shadows.
|
||||
*/
|
||||
public void updateLightCamera(Camera lightCam) {
|
||||
if (target.getType() == Light.Type.Directional) {
|
||||
DirectionalLight dl = (DirectionalLight) target;
|
||||
lightCam.setParallelProjection(true);
|
||||
lightCam.setLocation(Vector3f.ZERO);
|
||||
lightCam.lookAtDirection(dl.getDirection(), Vector3f.UNIT_Y);
|
||||
lightCam.setFrustum(-1, 1, -1, 1, 1, -1);
|
||||
} else {
|
||||
PointLight pl = (PointLight) target;
|
||||
lightCam.setParallelProjection(false);
|
||||
lightCam.setLocation(pl.getPosition());
|
||||
// direction will have to be calculated automatically
|
||||
lightCam.setFrustumPerspective(45, 1, 1, 300);
|
||||
}
|
||||
Nearest(0),
|
||||
/**
|
||||
* Bilinear filtering is used. Has the potential of being hardware
|
||||
* accelerated on some GPUs
|
||||
*/
|
||||
Bilinear(1),
|
||||
/**
|
||||
* Dither-based sampling is used, very cheap but can look bad at low
|
||||
* resolutions.
|
||||
*/
|
||||
Dither(2),
|
||||
/**
|
||||
* 4x4 percentage-closer filtering is used. Shadows will be smoother at the
|
||||
* cost of performance
|
||||
*/
|
||||
PCF4(3),
|
||||
/**
|
||||
* 8x8 percentage-closer filtering is used. Shadows will be smoother at the
|
||||
* cost of performance
|
||||
*/
|
||||
PCFPOISSON(4),
|
||||
/**
|
||||
* 8x8 percentage-closer filtering is used. Shadows will be smoother at the
|
||||
* cost of performance
|
||||
*/
|
||||
PCF8(5);
|
||||
|
||||
int materialParamValue;
|
||||
|
||||
private EdgeFilteringMode(int val) {
|
||||
materialParamValue = val;
|
||||
}
|
||||
|
||||
public int getMaterialParamValue() {
|
||||
return materialParamValue;
|
||||
}
|
||||
|
||||
}
|
@ -44,90 +44,44 @@ 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.PointLightShadowRenderer.CompareMode;
|
||||
import com.jme3.shadow.PointLightShadowRenderer.FilterMode;
|
||||
import com.jme3.texture.FrameBuffer;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* This Filter does basically the same as a PointLightShadowRenderer except it renders
|
||||
* the post shadow pass as a fulscreen quad pass instead of a geometry pass.
|
||||
* It's mostly faster than PointLightShadowRenderer 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
|
||||
*
|
||||
/**
|
||||
*
|
||||
* This Filter does basically the same as a PointLightShadowRenderer except it
|
||||
* renders the post shadow pass as a fulscreen quad pass instead of a geometry
|
||||
* pass. It's mostly faster than PointLightShadowRenderer 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 PointLightShadowFilter extends Filter {
|
||||
|
||||
private PointLightShadowRenderer plRenderer;
|
||||
private ViewPort viewPort;
|
||||
public class PointLightShadowFilter extends AbstractShadowFilter<PointLightShadowRenderer> {
|
||||
|
||||
/**
|
||||
* 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).
|
||||
* Creates a PointLightShadowFilter
|
||||
*
|
||||
* @param assetManager the application asset manager
|
||||
* @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048,
|
||||
* etc...)
|
||||
*/
|
||||
public PointLightShadowFilter(AssetManager manager, int size) {
|
||||
super("Post Shadow");
|
||||
material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md");
|
||||
plRenderer = new PointLightShadowRenderer(manager, size, material);
|
||||
plRenderer.needsfallBackMaterial = true;
|
||||
public PointLightShadowFilter(AssetManager assetManager, int shadowMapSize) {
|
||||
super(assetManager, shadowMapSize,new PointLightShadowRenderer(assetManager, shadowMapSize));
|
||||
}
|
||||
|
||||
@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) {
|
||||
plRenderer.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) {
|
||||
plRenderer.postQueue(queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
|
||||
plRenderer.setPostShadowParams();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
|
||||
plRenderer.initialize(renderManager, vp);
|
||||
this.viewPort = vp;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* gets the point light used to cast shadows with this processor
|
||||
*
|
||||
* @return the point light
|
||||
*/
|
||||
public PointLight getLight() {
|
||||
return plRenderer.getLight();
|
||||
return shadowRenderer.getLight();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -136,93 +90,7 @@ public class PointLightShadowFilter extends Filter {
|
||||
* @param light the point light
|
||||
*/
|
||||
public void setLight(PointLight light) {
|
||||
plRenderer.setLight(light);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the shdaow intensity
|
||||
* @see #setShadowIntensity(float shadowIntensity)
|
||||
* @return shadowIntensity
|
||||
*/
|
||||
public float getShadowIntensity() {
|
||||
return plRenderer.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) {
|
||||
plRenderer.setShadowIntensity(shadowIntensity);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the edges thickness <br>
|
||||
* @see #setEdgesThickness(int edgesThickness)
|
||||
* @return edgesThickness
|
||||
*/
|
||||
public int getEdgesThickness() {
|
||||
return plRenderer.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) {
|
||||
plRenderer.setEdgesThickness(edgesThickness);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if the PssmRenderer flushed the shadow queues
|
||||
* @return flushQueues
|
||||
*/
|
||||
public boolean isFlushQueues() {
|
||||
return plRenderer.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) {
|
||||
plRenderer.setFlushQueues(flushQueues);
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the shadow compare mode see {@link CompareMode} for more info
|
||||
* @param compareMode
|
||||
*/
|
||||
final public void setCompareMode(CompareMode compareMode) {
|
||||
plRenderer.setCompareMode(compareMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filtering mode for shadow edges see {@link FilterMode} for more info
|
||||
* @param filterMode
|
||||
*/
|
||||
final public void setFilterMode(FilterMode filterMode) {
|
||||
plRenderer.setFilterMode(filterMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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){
|
||||
plRenderer.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 plRenderer.getShadowZFadeLength();
|
||||
shadowRenderer.setLight(light);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,451 +34,47 @@ package com.jme3.shadow;
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.light.PointLight;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.Matrix4f;
|
||||
import com.jme3.math.Vector2f;
|
||||
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.OpaqueComparator;
|
||||
import com.jme3.renderer.queue.RenderQueue;
|
||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.Spatial;
|
||||
import com.jme3.scene.debug.WireFrustum;
|
||||
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;
|
||||
|
||||
/**
|
||||
* PssmShadow 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/>
|
||||
* PointLightShadowRenderer renders shadows for a point light
|
||||
*
|
||||
* @author Rémy Bouquet aka Nehon
|
||||
*/
|
||||
public class PointLightShadowRenderer implements SceneProcessor {
|
||||
public class PointLightShadowRenderer extends AbstractShadowRenderer {
|
||||
|
||||
/**
|
||||
* <code>FilterMode</code> specifies how shadows are filtered
|
||||
*/
|
||||
public enum FilterMode {
|
||||
|
||||
/**
|
||||
* Shadows are not filtered. Nearest sample is used, causing in blocky
|
||||
* shadows.
|
||||
*/
|
||||
Nearest,
|
||||
/**
|
||||
* Bilinear filtering is used. Has the potential of being hardware
|
||||
* accelerated on some GPUs
|
||||
*/
|
||||
Bilinear,
|
||||
/**
|
||||
* Dither-based sampling is used, very cheap but can look bad at low
|
||||
* resolutions.
|
||||
*/
|
||||
Dither,
|
||||
/**
|
||||
* 4x4 percentage-closer filtering is used. Shadows will be smoother at
|
||||
* the cost of performance
|
||||
*/
|
||||
PCF4,
|
||||
/**
|
||||
* 8x8 percentage-closer filtering is used. Shadows will be smoother at
|
||||
* the cost of performance
|
||||
*/
|
||||
PCFPOISSON,
|
||||
/**
|
||||
* 8x8 percentage-closer filtering is used. Shadows will be smoother at
|
||||
* the cost of performance
|
||||
*/
|
||||
PCF8
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
protected final int CAM_NUMBER = 6;
|
||||
public static final int CAM_NUMBER = 6;
|
||||
protected PointLight light;
|
||||
//common
|
||||
protected float shadowMapSize;
|
||||
protected float shadowIntensity = 0.7f;
|
||||
protected float zFarOverride = 0;
|
||||
protected RenderManager renderManager;
|
||||
protected ViewPort viewPort;
|
||||
protected FrameBuffer[] shadowFB;
|
||||
protected Texture2D[] shadowMaps;
|
||||
protected Texture2D dummyTex;
|
||||
protected Camera[] shadowCams;
|
||||
protected Material preshadowMat;
|
||||
protected Material postshadowMat;
|
||||
protected GeometryList splitOccluders = new GeometryList(new OpaqueComparator());
|
||||
protected Matrix4f[] lightViewProjectionsMatrices;
|
||||
protected boolean noOccluders = false;
|
||||
protected AssetManager assetManager;
|
||||
protected boolean debug = false;
|
||||
protected float edgesThickness = 1.0f;
|
||||
protected FilterMode filterMode;
|
||||
protected CompareMode compareMode;
|
||||
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
|
||||
protected boolean applyHWShadows = true;
|
||||
protected boolean applyFilterMode = true;
|
||||
protected boolean applyPCFEdge = true;
|
||||
protected boolean applyShadowIntensity = true;
|
||||
//a list of material of the post shadow queue geometries.
|
||||
protected List<Material> matCache = new ArrayList<Material>();
|
||||
//Holding the info for fading shadows in the far distance
|
||||
protected Vector2f fadeInfo;
|
||||
protected float fadeLength;
|
||||
protected boolean applyFadeInfo = false;
|
||||
protected GeometryList shadowMapOccluders = new GeometryList(new OpaqueComparator());
|
||||
private Geometry[] frustums = null;
|
||||
|
||||
/**
|
||||
* Create a PSSM Shadow Renderer 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>
|
||||
* Creates a PointLightShadowRenderer
|
||||
*
|
||||
* @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 nbSplits the number of shadow maps rendered (the more shadow maps
|
||||
* the more quality, the less fps).
|
||||
* @param assetManager the application asset manager
|
||||
* @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048,
|
||||
* etc...)
|
||||
*/
|
||||
public PointLightShadowRenderer(AssetManager manager, int size) {
|
||||
this(manager, size, new Material(manager, "Common/MatDefs/Shadow/PostShadowPSSM.j3md"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a PSSM Shadow Renderer 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).
|
||||
* @param postShadowMat the material used for post shadows if you need to
|
||||
* override it
|
||||
*/
|
||||
protected PointLightShadowRenderer(AssetManager manager, int size, Material postShadowMat) {
|
||||
|
||||
this.postshadowMat = postShadowMat;
|
||||
assetManager = manager;
|
||||
shadowMapSize = size;
|
||||
|
||||
shadowFB = new FrameBuffer[CAM_NUMBER];
|
||||
shadowMaps = new Texture2D[CAM_NUMBER];
|
||||
dispPic = new Picture[CAM_NUMBER];
|
||||
lightViewProjectionsMatrices = new Matrix4f[CAM_NUMBER];
|
||||
|
||||
//DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
|
||||
dummyTex = new Texture2D(size, size, Format.RGBA8);
|
||||
|
||||
preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");
|
||||
postshadowMat.setFloat("ShadowMapSize", size);
|
||||
|
||||
public PointLightShadowRenderer(AssetManager assetManager, int shadowMapSize) {
|
||||
super(assetManager, shadowMapSize, CAM_NUMBER);
|
||||
shadowCams = new Camera[CAM_NUMBER];
|
||||
|
||||
for (int i = 0; i < CAM_NUMBER; i++) {
|
||||
lightViewProjectionsMatrices[i] = new Matrix4f();
|
||||
shadowFB[i] = new FrameBuffer(size, size, 1);
|
||||
shadowMaps[i] = new Texture2D(size, size, 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(manager, shadowMaps[i], false);
|
||||
|
||||
shadowCams[i] = new Camera(size, size);
|
||||
}
|
||||
|
||||
setCompareMode(CompareMode.Hardware);
|
||||
setFilterMode(FilterMode.Bilinear);
|
||||
setShadowIntensity(0.7f);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filtering mode for shadow edges see {@link FilterMode} for more
|
||||
* info
|
||||
*
|
||||
* @param filterMode
|
||||
*/
|
||||
final public void setFilterMode(FilterMode filterMode) {
|
||||
if (filterMode == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
if (this.filterMode == filterMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.filterMode = filterMode;
|
||||
postshadowMat.setInt("FilterMode", filterMode.ordinal());
|
||||
postshadowMat.setFloat("PCFEdge", edgesThickness);
|
||||
if (compareMode == CompareMode.Hardware) {
|
||||
for (Texture2D shadowMap : shadowMaps) {
|
||||
if (filterMode == FilterMode.Bilinear) {
|
||||
shadowMap.setMagFilter(MagFilter.Bilinear);
|
||||
shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps);
|
||||
} else {
|
||||
shadowMap.setMagFilter(MagFilter.Nearest);
|
||||
shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
|
||||
}
|
||||
}
|
||||
}
|
||||
applyFilterMode = true;
|
||||
}
|
||||
|
||||
public FilterMode getFilterMode() {
|
||||
return filterMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the shadow compare mode see {@link CompareMode} for more info
|
||||
*
|
||||
* @param compareMode
|
||||
*/
|
||||
final public void setCompareMode(CompareMode compareMode) {
|
||||
if (compareMode == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
if (this.compareMode == compareMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.compareMode = compareMode;
|
||||
for (Texture2D shadowMap : shadowMaps) {
|
||||
if (compareMode == CompareMode.Hardware) {
|
||||
shadowMap.setShadowCompareMode(ShadowCompareMode.LessOrEqual);
|
||||
if (filterMode == FilterMode.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);
|
||||
applyHWShadows = true;
|
||||
}
|
||||
|
||||
//debug function that create a displayable frustrum
|
||||
private 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;
|
||||
case 4:
|
||||
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Yellow);
|
||||
break;
|
||||
case 5:
|
||||
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Gray);
|
||||
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";
|
||||
shadowCams[i] = new Camera(shadowMapSize, shadowMapSize);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return viewPort != null;
|
||||
}
|
||||
@Override
|
||||
protected void updateShadowCams(Camera viewCam) {
|
||||
|
||||
@SuppressWarnings("fallthrough")
|
||||
public void postQueue(RenderQueue rq) {
|
||||
if (light == null) {
|
||||
throw new IllegalStateException("The light can't be null for a " + this.getClass().getName());
|
||||
}
|
||||
GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast);
|
||||
if (occluders.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive);
|
||||
if (receivers.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Camera viewCam = viewPort.getCamera();
|
||||
|
||||
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);
|
||||
|
||||
updateShadowCams();
|
||||
|
||||
Renderer r = renderManager.getRenderer();
|
||||
renderManager.setForcedMaterial(preshadowMat);
|
||||
renderManager.setForcedTechnique("PreShadow");
|
||||
|
||||
for (int i = 0; i < CAM_NUMBER; i++) {
|
||||
|
||||
//Updating shadow cam with curent split frustra
|
||||
|
||||
ShadowUtil.getOccludersInCamFrustum(occluders, shadowCams[i], splitOccluders);
|
||||
|
||||
//saving light view projection matrix for this split
|
||||
lightViewProjectionsMatrices[i].set(shadowCams[i].getViewProjectionMatrix());
|
||||
renderManager.setCamera(shadowCams[i], false);
|
||||
|
||||
if (debug && frustums[i].getParent() == null) {
|
||||
((Node) viewPort.getScenes().get(0)).attachChild(frustums[i]);
|
||||
}
|
||||
|
||||
r.setFrameBuffer(shadowFB[i]);
|
||||
r.clearBuffers(false, true, false);
|
||||
|
||||
// System.out.println("Face " + i + " nb occl " + splitOccluders.size());
|
||||
// render shadow casters to shadow map
|
||||
viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCams[i], true);
|
||||
}
|
||||
if (flushQueues) {
|
||||
occluders.clear();
|
||||
}
|
||||
//restore setting for future rendering
|
||||
r.setFrameBuffer(viewPort.getOutputFrameBuffer());
|
||||
renderManager.setForcedMaterial(null);
|
||||
renderManager.setForcedTechnique(null);
|
||||
renderManager.setCamera(viewCam, false);
|
||||
|
||||
}
|
||||
|
||||
//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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateShadowCams() {
|
||||
|
||||
|
||||
//bottom
|
||||
shadowCams[0].setAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Z.mult(-1f), Vector3f.UNIT_Y.mult(-1f));
|
||||
@ -505,7 +101,22 @@ public class PointLightShadowRenderer implements SceneProcessor {
|
||||
shadowCams[i].updateViewProjection();
|
||||
}
|
||||
|
||||
if (debug && frustums == null) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers) {
|
||||
ShadowUtil.getOccludersInCamFrustum(sceneOccluders, shadowCams[shadowMapIndex], shadowMapOccluders);
|
||||
return shadowMapOccluders;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Camera getShadowCam(int shadowMapIndex) {
|
||||
return shadowCams[shadowMapIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDisplayFrustumDebug(int shadowMapIndex) {
|
||||
if (frustums == null) {
|
||||
frustums = new Geometry[CAM_NUMBER];
|
||||
Vector3f[] points = new Vector3f[8];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
@ -516,220 +127,14 @@ public class PointLightShadowRenderer implements SceneProcessor {
|
||||
frustums[i] = createFrustum(points, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
private Geometry[] frustums = null;
|
||||
|
||||
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) {
|
||||
|
||||
if (mat.getParam("ShadowMapSize") == null) {
|
||||
mat.setFloat("ShadowMapSize", shadowMapSize);
|
||||
}
|
||||
for (int j = 0; j < CAM_NUMBER; j++) {
|
||||
mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
|
||||
}
|
||||
mat.setVector3("LightPos", light.getPosition());
|
||||
if (mat.getParam("ShadowMap0") == null) {
|
||||
for (int j = 0; j < CAM_NUMBER; j++) {
|
||||
mat.setTexture("ShadowMap" + j, shadowMaps[j]);
|
||||
}
|
||||
}
|
||||
if (applyHWShadows || mat.getParam("HardwareShadows") == null) {
|
||||
mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
|
||||
}
|
||||
if (applyFilterMode || mat.getParam("FilterMode") == null) {
|
||||
mat.setInt("FilterMode", filterMode.ordinal());
|
||||
}
|
||||
if (mat.getParam("PCFEdge") == null || applyPCFEdge) {
|
||||
mat.setFloat("PCFEdge", edgesThickness);
|
||||
}
|
||||
|
||||
if (mat.getParam("ShadowIntensity") == null || applyShadowIntensity) {
|
||||
mat.setFloat("ShadowIntensity", shadowIntensity);
|
||||
}
|
||||
|
||||
if (fadeInfo != null && mat.getParam("FadeInfo") == null || applyFadeInfo) {
|
||||
mat.setVector2("FadeInfo", fadeInfo);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
applyHWShadows = false;
|
||||
applyFilterMode = false;
|
||||
applyPCFEdge = false;
|
||||
applyShadowIntensity = false;
|
||||
applyFadeInfo = 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) {
|
||||
setPostShadowParams();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void setPostShadowParams() {
|
||||
for (int j = 0; j < CAM_NUMBER; j++) {
|
||||
postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
|
||||
}
|
||||
postshadowMat.setVector3("LightPos", light.getPosition());
|
||||
}
|
||||
|
||||
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);
|
||||
applyShadowIntensity = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
applyPCFEdge = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
if (frustums[shadowMapIndex].getParent() == null) {
|
||||
((Node) viewPort.getScenes().get(0)).attachChild(frustums[shadowMapIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@Override
|
||||
protected void setMaterialParameters(Material material) {
|
||||
material.setVector3("LightPos", light.getPosition());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -748,6 +153,5 @@ public class PointLightShadowRenderer implements SceneProcessor {
|
||||
*/
|
||||
public void setLight(PointLight light) {
|
||||
this.light = light;
|
||||
updateShadowCams();
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,9 @@ import java.io.IOException;
|
||||
* API is basically the same as the PssmShadowRenderer;
|
||||
*
|
||||
* @author Rémy Bouquet aka Nehon
|
||||
* @deprecated use {@link DirectionalLightShadowFilter}
|
||||
*/
|
||||
@Deprecated
|
||||
public class PssmShadowFilter extends Filter {
|
||||
|
||||
private PssmShadowRenderer pssmRenderer;
|
||||
|
@ -48,6 +48,7 @@ import com.jme3.renderer.queue.OpaqueComparator;
|
||||
import com.jme3.renderer.queue.RenderQueue;
|
||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.Spatial;
|
||||
import com.jme3.scene.debug.WireFrustum;
|
||||
import com.jme3.texture.FrameBuffer;
|
||||
@ -66,17 +67,21 @@ import java.util.List;
|
||||
* 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>
|
||||
* 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
|
||||
* @deprecated use {@link DirectionalLightShadowRenderer}
|
||||
*/
|
||||
@Deprecated
|
||||
public class PssmShadowRenderer implements SceneProcessor {
|
||||
|
||||
/**
|
||||
* <code>FilterMode</code> specifies how shadows are filtered
|
||||
* @deprecated use {@link EdgeFilteringMode}
|
||||
*/
|
||||
public enum FilterMode {
|
||||
@Deprecated
|
||||
public enum FilterMode{
|
||||
|
||||
/**
|
||||
* Shadows are not filtered. Nearest sample is used, causing in blocky
|
||||
@ -89,30 +94,32 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
*/
|
||||
Bilinear,
|
||||
/**
|
||||
* Dither-based sampling is used, very cheap but can look bad
|
||||
* at low resolutions.
|
||||
* Dither-based sampling is used, very cheap but can look bad at low
|
||||
* resolutions.
|
||||
*/
|
||||
Dither,
|
||||
/**
|
||||
* 4x4 percentage-closer filtering is used. Shadows will be smoother
|
||||
* at the cost of performance
|
||||
* 4x4 percentage-closer filtering is used. Shadows will be smoother at
|
||||
* the cost of performance
|
||||
*/
|
||||
PCF4,
|
||||
/**
|
||||
* 8x8 percentage-closer filtering is used. Shadows will be smoother
|
||||
* at the cost of performance
|
||||
* 8x8 percentage-closer filtering is used. Shadows will be smoother at
|
||||
* the cost of performance
|
||||
*/
|
||||
PCFPOISSON,
|
||||
/**
|
||||
* 8x8 percentage-closer filtering is used. Shadows will be smoother
|
||||
* at the cost of performance
|
||||
* 8x8 percentage-closer filtering is used. Shadows will be smoother at
|
||||
* the cost of performance
|
||||
*/
|
||||
PCF8
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the shadow comparison mode
|
||||
* Specifies the shadow comparison mode
|
||||
* @deprecated use {@link CompareMode}
|
||||
*/
|
||||
@Deprecated
|
||||
public enum CompareMode {
|
||||
|
||||
/**
|
||||
@ -169,24 +176,30 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
protected boolean applyFadeInfo = false;
|
||||
|
||||
/**
|
||||
* Create a PSSM Shadow Renderer
|
||||
* 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>
|
||||
* Create a PSSM Shadow Renderer 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).
|
||||
* @param nbSplits the number of shadow maps rendered (the more shadow maps the more quality, the less fps).
|
||||
* @param nbSplits the number of shadow maps rendered (the more shadow maps
|
||||
* the more quality, the less fps).
|
||||
* @param nbSplits the number of shadow maps rendered (the more shadow maps
|
||||
* the more quality, the less fps).
|
||||
*/
|
||||
public PssmShadowRenderer(AssetManager manager, int size, int nbSplits) {
|
||||
this(manager, size, nbSplits, new Material(manager, "Common/MatDefs/Shadow/PostShadowPSSM.j3md"));
|
||||
this(manager, size, nbSplits, new Material(manager, "Common/MatDefs/Shadow/PostShadow.j3md"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a PSSM Shadow Renderer
|
||||
* 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>
|
||||
* Create a PSSM Shadow Renderer 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).
|
||||
* @param postShadowMat the material used for post shadows if you need to override it
|
||||
* @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
|
||||
*/
|
||||
protected PssmShadowRenderer(AssetManager manager, int size, int nbSplits, Material postShadowMat) {
|
||||
|
||||
@ -219,7 +232,7 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
//DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
|
||||
shadowFB[i].setColorTexture(dummyTex);
|
||||
|
||||
postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]);
|
||||
postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]);
|
||||
|
||||
//quads for debuging purpose
|
||||
dispPic[i] = new Picture("Picture" + i);
|
||||
@ -240,8 +253,10 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filtering mode for shadow edges see {@link FilterMode} for more info
|
||||
* @param filterMode
|
||||
* Sets the filtering mode for shadow edges see {@link FilterMode} for more
|
||||
* info
|
||||
*
|
||||
* @param filterMode
|
||||
*/
|
||||
final public void setFilterMode(FilterMode filterMode) {
|
||||
if (filterMode == null) {
|
||||
@ -271,7 +286,8 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
|
||||
/**
|
||||
* sets the shadow compare mode see {@link CompareMode} for more info
|
||||
* @param compareMode
|
||||
*
|
||||
* @param compareMode
|
||||
*/
|
||||
final public void setCompareMode(CompareMode compareMode) {
|
||||
if (compareMode == null) {
|
||||
@ -351,7 +367,8 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
|
||||
/**
|
||||
* returns the light direction used by the processor
|
||||
* @return
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Vector3f getDirection() {
|
||||
return direction;
|
||||
@ -359,7 +376,8 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
|
||||
/**
|
||||
* Sets the light direction to use to compute shadows
|
||||
* @param direction
|
||||
*
|
||||
* @param direction
|
||||
*/
|
||||
public void setDirection(Vector3f direction) {
|
||||
this.direction.set(direction).normalizeLocal();
|
||||
@ -425,12 +443,23 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
lightViewProjectionsMatrices[i].set(shadowCam.getViewProjectionMatrix());
|
||||
renderManager.setCamera(shadowCam, false);
|
||||
|
||||
if (debugfrustums) {
|
||||
// frustrumFromBound(b.casterBB,ColorRGBA.Blue );
|
||||
// frustrumFromBound(b.receiverBB,ColorRGBA.Green );
|
||||
// frustrumFromBound(b.splitBB,ColorRGBA.Yellow );
|
||||
((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, i));
|
||||
ShadowUtil.updateFrustumPoints2(shadowCam, points);
|
||||
((Node) viewPort.getScenes().get(0)).attachChild(createFrustum(points, i));
|
||||
|
||||
}
|
||||
|
||||
r.setFrameBuffer(shadowFB[i]);
|
||||
r.clearBuffers(false, true, false);
|
||||
|
||||
// render shadow casters to shadow map
|
||||
viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCam, true);
|
||||
}
|
||||
debugfrustums = false;
|
||||
if (flushQueues) {
|
||||
occluders.clear();
|
||||
}
|
||||
@ -441,6 +470,11 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
renderManager.setCamera(viewCam, false);
|
||||
|
||||
}
|
||||
boolean debugfrustums = false;
|
||||
|
||||
public void displayFrustum() {
|
||||
debugfrustums = true;
|
||||
}
|
||||
|
||||
//debug only : displays depth shadow maps
|
||||
protected void displayShadowMap(Renderer r) {
|
||||
@ -457,8 +491,9 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
renderManager.setCamera(cam, false);
|
||||
}
|
||||
|
||||
/**For dubuging purpose
|
||||
* Allow to "snapshot" the current frustrum to the scene
|
||||
/**
|
||||
* For dubuging purpose Allow to "snapshot" the current frustrum to the
|
||||
* scene
|
||||
*/
|
||||
public void displayDebug() {
|
||||
debug = true;
|
||||
@ -515,36 +550,22 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
|
||||
//iterating through the mat cache and setting the parameters
|
||||
for (Material mat : matCache) {
|
||||
if (mat.getParam("Splits") == null) {
|
||||
mat.setColor("Splits", splits);
|
||||
}
|
||||
if (mat.getParam("ShadowMapSize") == null) {
|
||||
mat.setFloat("ShadowMapSize", shadowMapSize);
|
||||
}
|
||||
mat.setColor("Splits", splits);
|
||||
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.setTexture("ShadowMap" + j, shadowMaps[j]);
|
||||
}
|
||||
}
|
||||
if (applyHWShadows || mat.getParam("HardwareShadows") == null) {
|
||||
mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
|
||||
}
|
||||
if (applyFilterMode || mat.getParam("FilterMode") == null) {
|
||||
mat.setInt("FilterMode", filterMode.ordinal());
|
||||
}
|
||||
if (mat.getParam("PCFEdge") == null || applyPCFEdge) {
|
||||
mat.setFloat("PCFEdge", edgesThickness);
|
||||
for (int j = 0; j < nbSplits; j++) {
|
||||
mat.setTexture("ShadowMap" + j, shadowMaps[j]);
|
||||
}
|
||||
mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
|
||||
mat.setInt("FilterMode", filterMode.ordinal());
|
||||
mat.setFloat("PCFEdge", edgesThickness);
|
||||
mat.setFloat("ShadowIntensity", shadowIntensity);
|
||||
|
||||
if (mat.getParam("ShadowIntensity") == null || applyShadowIntensity) {
|
||||
mat.setFloat("ShadowIntensity", shadowIntensity);
|
||||
}
|
||||
|
||||
if (fadeInfo != null && mat.getParam("FadeInfo") == null || applyFadeInfo) {
|
||||
mat.setVector2("FadeInfo", fadeInfo);
|
||||
if (fadeInfo != null) {
|
||||
mat.setVector2("FadeInfo", fadeInfo);
|
||||
}
|
||||
|
||||
}
|
||||
@ -567,7 +588,8 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
postshadowMat.setColor("Splits", splits);
|
||||
for (int j = 0; j < nbSplits; j++) {
|
||||
postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
|
||||
}
|
||||
postshadowMat.setTexture("ShadowMap" + j, shadowMaps[j]);
|
||||
}
|
||||
}
|
||||
|
||||
public void preFrame(float tpf) {
|
||||
@ -580,8 +602,8 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the labda parameter
|
||||
* see #setLambda(float lambda)
|
||||
* returns the labda parameter see #setLambda(float lambda)
|
||||
*
|
||||
* @return lambda
|
||||
*/
|
||||
public float getLambda() {
|
||||
@ -602,6 +624,7 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
|
||||
/**
|
||||
* How far the shadows are rendered in the view
|
||||
*
|
||||
* @see #setShadowZExtend(float zFar)
|
||||
* @return shadowZExtend
|
||||
*/
|
||||
@ -610,20 +633,23 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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){
|
||||
if (fadeInfo != null) {
|
||||
fadeInfo.set(zFar - fadeLength, 1f / fadeLength);
|
||||
}
|
||||
this.zFarOverride = zFar;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the shdaow intensity
|
||||
*
|
||||
* @see #setShadowIntensity(float shadowIntensity)
|
||||
* @return shadowIntensity
|
||||
*/
|
||||
@ -632,10 +658,10 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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) {
|
||||
@ -646,6 +672,7 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
|
||||
/**
|
||||
* returns the edges thickness
|
||||
*
|
||||
* @see #setEdgesThickness(int edgesThickness)
|
||||
* @return edgesThickness
|
||||
*/
|
||||
@ -654,8 +681,10 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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));
|
||||
@ -666,6 +695,7 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
|
||||
/**
|
||||
* returns true if the PssmRenderer flushed the shadow queues
|
||||
*
|
||||
* @return flushQueues
|
||||
*/
|
||||
public boolean isFlushQueues() {
|
||||
@ -673,18 +703,21 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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) {
|
||||
@ -702,15 +735,17 @@ public class PssmShadowRenderer implements SceneProcessor {
|
||||
postshadowMat.setVector2("FadeInfo", fadeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the length over which the shadow will fade out when using a shadowZextend
|
||||
|
||||
/**
|
||||
* 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){
|
||||
public float getShadowZFadeLength() {
|
||||
if (fadeInfo != null) {
|
||||
return zFarOverride - fadeInfo.x;
|
||||
}
|
||||
return 0f;
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
@ -361,9 +361,7 @@ public class ShadowUtil {
|
||||
|
||||
if (ortho) {
|
||||
shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
|
||||
} else {
|
||||
shadowCam.setFrustumPerspective(45, 1, 1, 150);
|
||||
}
|
||||
}
|
||||
|
||||
// create transform to rotate points to viewspace
|
||||
Matrix4f viewProjMatrix = shadowCam.getViewProjectionMatrix();
|
||||
@ -442,9 +440,9 @@ public class ShadowUtil {
|
||||
|
||||
splitMin.z = 0;
|
||||
|
||||
if (!ortho) {
|
||||
shadowCam.setFrustumPerspective(45, 1, 1, splitMax.z);
|
||||
}
|
||||
// if (!ortho) {
|
||||
// shadowCam.setFrustumPerspective(45, 1, 1, splitMax.z);
|
||||
// }
|
||||
|
||||
Matrix4f projMatrix = shadowCam.getProjectionMatrix();
|
||||
|
||||
|
143
engine/src/core/com/jme3/shadow/SpotLightShadowFilter.java
Normal file
143
engine/src/core/com/jme3/shadow/SpotLightShadowFilter.java
Normal file
@ -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);
|
||||
|
||||
}
|
||||
}
|
211
engine/src/core/com/jme3/shadow/SpotLightShadowRenderer.java
Normal file
211
engine/src/core/com/jme3/shadow/SpotLightShadowRenderer.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
154
engine/src/test/jme3test/light/ShadowTestUIManager.java
Normal file
154
engine/src/test/jme3test/light/ShadowTestUIManager.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
311
engine/src/test/jme3test/light/TestDirectionalLightShadow.java
Normal file
311
engine/src/test/jme3test/light/TestDirectionalLightShadow.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -32,12 +32,7 @@
|
||||
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.PointLight;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Quaternion;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.post.FilterPostProcessor;
|
||||
@ -46,18 +41,18 @@ import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.shape.Box;
|
||||
import com.jme3.scene.shape.Sphere;
|
||||
import com.jme3.shadow.EdgeFilteringMode;
|
||||
import com.jme3.shadow.PointLightShadowFilter;
|
||||
import com.jme3.shadow.PointLightShadowRenderer;
|
||||
|
||||
public class TestPointLightShadows extends SimpleApplication implements ActionListener {
|
||||
public class TestPointLightShadows extends SimpleApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
TestPointLightShadows app = new TestPointLightShadows();
|
||||
app.start();
|
||||
}
|
||||
Node lightNode;
|
||||
private boolean hardwareShadows = true;
|
||||
PointLightShadowRenderer plsr ;
|
||||
PointLightShadowRenderer plsr;
|
||||
PointLightShadowFilter plsf;
|
||||
|
||||
@Override
|
||||
@ -91,147 +86,44 @@ public class TestPointLightShadows extends SimpleApplication implements ActionLi
|
||||
|
||||
plsr = new PointLightShadowRenderer(assetManager, 512);
|
||||
plsr.setLight((PointLight) scene.getLocalLightList().get(0));
|
||||
plsr.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest);
|
||||
// plsr.displayDebug();
|
||||
plsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
|
||||
// plsr.setFlushQueues(false);
|
||||
// plsr.displayDebug();
|
||||
viewPort.addProcessor(plsr);
|
||||
|
||||
|
||||
// PointLight pl = new PointLight();
|
||||
// pl.setPosition(new Vector3f(0, 0.5f, 0));
|
||||
// pl.setRadius(5);
|
||||
// rootNode.addLight(pl);
|
||||
|
||||
// Geometry lightMdl2 = new Geometry("Light2", new Sphere(10, 10, 0.1f));
|
||||
// //Geometry lightMdl = new Geometry("Light", new Box(.1f,.1f,.1f));
|
||||
// lightMdl2.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
|
||||
// lightMdl2.setShadowMode(RenderQueue.ShadowMode.Off);
|
||||
// rootNode.attachChild(lightMdl2);
|
||||
// lightMdl2.setLocalTranslation(pl.getPosition());
|
||||
// PointLightShadowRenderer plsr2 = new PointLightShadowRenderer(assetManager, 512);
|
||||
// plsr2.setLight(pl);
|
||||
// plsr2.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
|
||||
// // plsr.displayDebug();
|
||||
// viewPort.addProcessor(plsr2);
|
||||
|
||||
|
||||
plsf = new PointLightShadowFilter(assetManager, 512);
|
||||
plsf.setLight((PointLight) scene.getLocalLightList().get(0));
|
||||
plsf.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest);
|
||||
plsf.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
|
||||
plsf.setEnabled(false);
|
||||
|
||||
|
||||
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
|
||||
fpp.addFilter(plsf);
|
||||
fpp.addFilter(plsf);
|
||||
viewPort.addProcessor(fpp);
|
||||
|
||||
initUIAndInputs();
|
||||
ShadowTestUIManager uiMan = new ShadowTestUIManager(assetManager, plsr, plsf, guiNode, inputManager, viewPort);
|
||||
}
|
||||
|
||||
BitmapText shadowTypeText;
|
||||
BitmapText shadowCompareText;
|
||||
BitmapText shadowFilterText;
|
||||
BitmapText shadowIntensityText;
|
||||
final static String TYPE_TEXT = "(Space) Shadow type : ";
|
||||
final static String COMPARE_TEXT = "(enter) Shadow compare ";
|
||||
final static String FILTERING_TEXT = "(f) Edge filtering : ";
|
||||
final static String INTENSITY_TEXT = "(t:up, g:down) Shadow intensity : ";
|
||||
|
||||
private void initUIAndInputs() {
|
||||
guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
|
||||
shadowTypeText = createText();
|
||||
shadowCompareText = createText();
|
||||
shadowFilterText = createText();
|
||||
shadowIntensityText = createText();
|
||||
|
||||
shadowTypeText.setText(TYPE_TEXT+"Processor");
|
||||
shadowCompareText.setText(COMPARE_TEXT+(hardwareShadows?"Hardware":"Software"));
|
||||
shadowFilterText.setText(FILTERING_TEXT+plsr.getFilterMode().toString());
|
||||
shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity());
|
||||
|
||||
shadowTypeText.setLocalTranslation(10, cam.getHeight()-20, 0);
|
||||
shadowCompareText.setLocalTranslation(10, cam.getHeight()-40, 0);
|
||||
shadowFilterText.setLocalTranslation(10, cam.getHeight()-60, 0);
|
||||
shadowIntensityText.setLocalTranslation(10, cam.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");
|
||||
|
||||
}
|
||||
|
||||
int filteringIndex = 0;
|
||||
int renderModeIndex = 0;
|
||||
|
||||
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.setCompareMode(hardwareShadows ? PointLightShadowRenderer.CompareMode.Hardware : PointLightShadowRenderer.CompareMode.Software);
|
||||
plsf.setCompareMode(hardwareShadows ? PointLightShadowRenderer.CompareMode.Hardware : PointLightShadowRenderer.CompareMode.Software);
|
||||
|
||||
shadowCompareText.setText(COMPARE_TEXT+(hardwareShadows?"Hardware":"Software"));
|
||||
}
|
||||
|
||||
|
||||
if (name.equals("changeFiltering") && keyPressed) {
|
||||
filteringIndex = plsr.getFilterMode().ordinal();
|
||||
filteringIndex = (filteringIndex + 1) % PointLightShadowRenderer.FilterMode.values().length;
|
||||
PointLightShadowRenderer.FilterMode m = PointLightShadowRenderer.FilterMode.values()[filteringIndex];
|
||||
plsr.setFilterMode(m);
|
||||
plsf.setFilterMode(m);
|
||||
shadowFilterText.setText(FILTERING_TEXT+m.toString());
|
||||
}
|
||||
|
||||
|
||||
if (name.equals("ShadowUp") && keyPressed) {
|
||||
plsr.setShadowIntensity(plsr.getShadowIntensity() + 0.1f);
|
||||
plsf.setShadowIntensity(plsr.getShadowIntensity() + 0.1f);
|
||||
|
||||
shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity());
|
||||
}
|
||||
if (name.equals("ShadowDown") && keyPressed) {
|
||||
plsr.setShadowIntensity(plsr.getShadowIntensity() - 0.1f);
|
||||
plsf.setShadowIntensity(plsr.getShadowIntensity() - 0.1f);
|
||||
shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity());
|
||||
}
|
||||
if (name.equals("ThicknessUp") && keyPressed) {
|
||||
plsr.setEdgesThickness(plsr.getEdgesThickness() + 1);
|
||||
plsf.setEdgesThickness(plsr.getEdgesThickness() + 1);
|
||||
System.out.println("Shadow thickness : " + plsr.getEdgesThickness());
|
||||
}
|
||||
if (name.equals("ThicknessDown") && keyPressed) {
|
||||
plsr.setEdgesThickness(plsr.getEdgesThickness() - 1);
|
||||
plsf.setEdgesThickness(plsr.getEdgesThickness() - 1);
|
||||
System.out.println("Shadow thickness : " + plsr.getEdgesThickness());
|
||||
}
|
||||
|
||||
}
|
||||
float time;
|
||||
|
||||
@Override
|
||||
public void simpleUpdate(float tpf) {
|
||||
time += tpf;
|
||||
lightNode.setLocalTranslation(FastMath.cos(time)*0.4f,lightNode.getLocalTranslation().y , FastMath.sin(time)*0.4f);
|
||||
|
||||
}
|
||||
|
||||
private BitmapText createText() {
|
||||
BitmapText t = new BitmapText(guiFont, false);
|
||||
t.setSize(guiFont.getCharSet().getRenderedSize()*0.75f);
|
||||
return t;
|
||||
// lightNode.move(FastMath.cos(tpf) * 0.4f, 0, FastMath.sin(tpf) * 0.4f);
|
||||
}
|
||||
}
|
@ -32,9 +32,13 @@
|
||||
package jme3test.light;
|
||||
|
||||
import com.jme3.app.SimpleApplication;
|
||||
import com.jme3.export.JmeExporter;
|
||||
import com.jme3.export.JmeImporter;
|
||||
import com.jme3.export.Savable;
|
||||
import com.jme3.font.BitmapText;
|
||||
import com.jme3.input.KeyInput;
|
||||
import com.jme3.input.controls.ActionListener;
|
||||
import com.jme3.input.controls.AnalogListener;
|
||||
import com.jme3.input.controls.KeyTrigger;
|
||||
import com.jme3.light.AmbientLight;
|
||||
import com.jme3.light.DirectionalLight;
|
||||
@ -63,6 +67,7 @@ import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture.WrapMode;
|
||||
import com.jme3.util.SkyFactory;
|
||||
import com.jme3.util.TangentBinormalGenerator;
|
||||
import java.io.IOException;
|
||||
|
||||
public class TestPssmShadow extends SimpleApplication implements ActionListener {
|
||||
|
||||
@ -123,7 +128,7 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
|
||||
ground.setShadowMode(ShadowMode.CastAndReceive);
|
||||
rootNode.attachChild(ground);
|
||||
|
||||
DirectionalLight l = new DirectionalLight();
|
||||
l = new DirectionalLight();
|
||||
l.setDirection(new Vector3f(-1, -1, -1));
|
||||
rootNode.addLight(l);
|
||||
|
||||
@ -136,6 +141,7 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
|
||||
|
||||
rootNode.attachChild(sky);
|
||||
}
|
||||
DirectionalLight l;
|
||||
|
||||
@Override
|
||||
public void simpleInitApp() {
|
||||
@ -148,36 +154,42 @@ 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.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
|
||||
pssmRenderer.setDirection(new Vector3f(-0.5973172f, -0.56583486f, 0.8846725f).normalizeLocal());
|
||||
pssmRenderer.setLambda(0.55f);
|
||||
pssmRenderer.setShadowIntensity(0.6f);
|
||||
pssmRenderer.setCompareMode(CompareMode.Software);
|
||||
pssmRenderer.setFilterMode(FilterMode.Dither);
|
||||
// pssmRenderer.displayDebug();
|
||||
|
||||
pssmRenderer.displayFrustum();
|
||||
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.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
|
||||
pssmRenderer.setDirection(new Vector3f(-0.5973172f, -0.56583486f, 0.8846725f).normalizeLocal());
|
||||
pssmFilter.setLambda(0.55f);
|
||||
pssmFilter.setShadowIntensity(0.6f);
|
||||
pssmFilter.setCompareMode(CompareMode.Software);
|
||||
pssmFilter.setFilterMode(FilterMode.Dither);
|
||||
pssmFilter.setEnabled(false);
|
||||
|
||||
|
||||
// pssmFilter.setShadowZFadeLength(300);
|
||||
// pssmFilter.setShadowZExtend(500);
|
||||
|
||||
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) */
|
||||
guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
|
||||
@ -195,7 +207,20 @@ 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.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", "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown", "changeFiltering",
|
||||
"switchGroundMat", "splits", "up", "down", "right", "left", "fwd", "back");
|
||||
|
||||
}
|
||||
|
||||
private void print(String str) {
|
||||
@ -246,10 +271,10 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
|
||||
break;
|
||||
case 1:
|
||||
viewPort.removeProcessor(pssmRenderer);
|
||||
pssmFilter.setEnabled(true);
|
||||
pssmFilter.setEnabled(true);
|
||||
break;
|
||||
case 2:
|
||||
pssmFilter.setEnabled(false);
|
||||
pssmFilter.setEnabled(false);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -311,5 +336,78 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
|
||||
}
|
||||
}
|
||||
|
||||
// if (name.equals("splits") && keyPressed) {
|
||||
// pssmRenderer.displayFrustum();
|
||||
// }
|
||||
|
||||
|
||||
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);
|
||||
pssmFilter.setDirection(v);
|
||||
pssmRenderer.setDirection(v);
|
||||
}
|
||||
}
|
||||
|
198
engine/src/test/jme3test/light/TestSpotLightShadows.java
Normal file
198
engine/src/test/jme3test/light/TestSpotLightShadows.java
Normal file
@ -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…
x
Reference in New Issue
Block a user