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{
|
Technique PostShadow15{
|
||||||
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM.vert
|
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow.vert
|
||||||
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM15.frag
|
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag
|
||||||
|
|
||||||
WorldParameters {
|
WorldParameters {
|
||||||
WorldViewProjectionMatrix
|
WorldViewProjectionMatrix
|
||||||
@ -228,8 +228,8 @@ MaterialDef Phong Lighting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Technique PostShadow{
|
Technique PostShadow{
|
||||||
VertexShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.vert
|
VertexShader GLSL100: Common/MatDefs/Shadow/PostShadow.vert
|
||||||
FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.frag
|
FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag
|
||||||
|
|
||||||
WorldParameters {
|
WorldParameters {
|
||||||
WorldViewProjectionMatrix
|
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"
|
#import "Common/ShaderLib/Shadows.glsllib"
|
||||||
|
|
||||||
uniform SHADOWMAP m_ShadowMap;
|
#ifdef PSSM
|
||||||
varying vec4 projCoord;
|
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(){
|
void main(){
|
||||||
vec4 coord = projCoord;
|
|
||||||
coord.xyz /= coord.w;
|
#ifdef DISCARD_ALPHA
|
||||||
float shad = Shadow_GetShadow(m_ShadowMap, coord) * 0.7 + 0.3;
|
#ifdef COLOR_MAP
|
||||||
gl_FragColor = vec4(shad,shad,shad,1.0);
|
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 {
|
MaterialDef Post Shadow {
|
||||||
|
|
||||||
MaterialParameters {
|
MaterialParameters {
|
||||||
Texture2D ShadowMap
|
Int FilterMode
|
||||||
Matrix4 LightViewProjectionMatrix
|
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 {
|
Technique {
|
||||||
@ -15,7 +66,13 @@ MaterialDef Post Shadow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Defines {
|
Defines {
|
||||||
NO_SHADOW2DPROJ
|
HARDWARE_SHADOWS : HardwareShadows
|
||||||
|
FILTER_MODE : FilterMode
|
||||||
|
PCFEDGE : PCFEdge
|
||||||
|
SHADOWMAP_SIZE : ShadowMapSize
|
||||||
|
FADE : FadeInfo
|
||||||
|
PSSM : Splits
|
||||||
|
POINTLIGHT : LightViewProjectionMatrix5
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderState {
|
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_WorldViewProjectionMatrix;
|
||||||
uniform mat4 g_WorldMatrix;
|
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;
|
attribute vec3 inPosition;
|
||||||
|
|
||||||
|
#ifdef DISCARD_ALPHA
|
||||||
|
attribute vec2 inTexCoord;
|
||||||
|
#endif
|
||||||
|
|
||||||
const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
|
const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
|
||||||
0.0, 0.5, 0.0, 0.0,
|
0.0, 0.5, 0.0, 0.0,
|
||||||
0.0, 0.0, 0.5, 0.0,
|
0.0, 0.0, 0.5, 0.0,
|
||||||
0.5, 0.5, 0.5, 1.0);
|
0.5, 0.5, 0.5, 1.0);
|
||||||
|
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
|
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
|
// 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
|
#ifdef DISCARD_ALPHA
|
||||||
//projCoord = biasMat * (m_LightViewProjectionMatrix * worldPos);
|
texCoord = inTexCoord;
|
||||||
vec4 coord = m_LightViewProjectionMatrix * worldPos;
|
#endif
|
||||||
projCoord = biasMat * coord;
|
// populate the light view matrices array and convert vertex to light viewProj space
|
||||||
//projCoord.z /= gl_DepthRange.far;
|
projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos;
|
||||||
//projCoord = (m_LightViewProjectionMatrix * worldPos);
|
projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
|
||||||
//projCoord /= projCoord.w;
|
projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
|
||||||
//projCoord.xy = projCoord.xy * vec2(0.5, -0.5) + vec2(0.5);
|
projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
|
||||||
|
#ifdef POINTLIGHT
|
||||||
|
projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos;
|
||||||
|
projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos;
|
||||||
|
#else
|
||||||
|
|
||||||
// bias from [-1, 1] to [0, 1] for sampling shadow map
|
vec4 vLightPos = g_ViewMatrix * vec4(m_LightPos,1.0);
|
||||||
//projCoord = (projCoord.xyzw * vec4(0.5)) + vec4(0.5);
|
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_Texture;
|
||||||
uniform sampler2D m_DepthTexture;
|
uniform sampler2D m_DepthTexture;
|
||||||
@ -59,46 +59,21 @@ void main(){
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
float shadow = 1.0;
|
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
|
#ifdef POINTLIGHT
|
||||||
vec3 vect = worldPos.xyz - m_LightPos;
|
shadow = getPointLightShadows(worldPos, m_LightPos,
|
||||||
vec3 absv= abs(vect);
|
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5,
|
||||||
float maxComp = max(absv.x,max(absv.y,absv.z));
|
projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5);
|
||||||
if(maxComp == absv.y){
|
#else
|
||||||
if(vect.y < 0.0){
|
#ifdef PSSM
|
||||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
|
||||||
}else{
|
shadow = getDirectionalLightShadows(m_Splits, shadowPosition,
|
||||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,
|
||||||
}
|
projCoord0, projCoord1, projCoord2, projCoord3);
|
||||||
}else if(maxComp == absv.z){
|
#else
|
||||||
if(vect.z < 0.0){
|
//spotlight
|
||||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
shadow = getSpotLightShadows(m_ShadowMap0,projCoord0);
|
||||||
}else{
|
#endif
|
||||||
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
|
#endif
|
||||||
|
|
||||||
#ifdef FADE
|
#ifdef FADE
|
||||||
|
@ -11,7 +11,6 @@ MaterialDef Post Shadow {
|
|||||||
//pointLights
|
//pointLights
|
||||||
Texture2D ShadowMap4
|
Texture2D ShadowMap4
|
||||||
Texture2D ShadowMap5
|
Texture2D ShadowMap5
|
||||||
Vector3 LightPos
|
|
||||||
|
|
||||||
Float ShadowIntensity
|
Float ShadowIntensity
|
||||||
Vector4 Splits
|
Vector4 Splits
|
||||||
@ -24,6 +23,7 @@ MaterialDef Post Shadow {
|
|||||||
//pointLight
|
//pointLight
|
||||||
Matrix4 LightViewProjectionMatrix4
|
Matrix4 LightViewProjectionMatrix4
|
||||||
Matrix4 LightViewProjectionMatrix5
|
Matrix4 LightViewProjectionMatrix5
|
||||||
|
Vector3 LightPos
|
||||||
|
|
||||||
Float PCFEdge
|
Float PCFEdge
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#import "Common/ShaderLib/MultiSample.glsllib"
|
#import "Common/ShaderLib/MultiSample.glsllib"
|
||||||
#import "Common/ShaderLib/PssmShadows15.glsllib"
|
#import "Common/ShaderLib/Shadows15.glsllib"
|
||||||
|
|
||||||
|
|
||||||
uniform COLORTEXTURE m_Texture;
|
uniform COLORTEXTURE m_Texture;
|
||||||
@ -59,53 +59,29 @@ vec4 main_multiSample(in int numSample){
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
float shadow = 1.0;
|
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
|
#ifdef POINTLIGHT
|
||||||
vec3 vect = worldPos.xyz - m_LightPos;
|
shadow = getPointLightShadows(worldPos, m_LightPos,
|
||||||
vec3 absv= abs(vect);
|
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,m_ShadowMap4,m_ShadowMap5,
|
||||||
float maxComp = max(absv.x,max(absv.y,absv.z));
|
projCoord0, projCoord1, projCoord2, projCoord3, projCoord4, projCoord5);
|
||||||
if(maxComp == absv.y){
|
#else
|
||||||
if(vect.y < 0.0){
|
#ifdef PSSM
|
||||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
|
||||||
}else{
|
shadow = getDirectionalLightShadows(m_Splits, shadowPosition,
|
||||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
m_ShadowMap0,m_ShadowMap1,m_ShadowMap2,m_ShadowMap3,
|
||||||
}
|
projCoord0, projCoord1, projCoord2, projCoord3);
|
||||||
}else if(maxComp == absv.z){
|
#else
|
||||||
if(vect.z < 0.0){
|
//spotlight
|
||||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
shadow = getSpotLightShadows(m_ShadowMap0,projCoord0);
|
||||||
}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
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef FADE
|
#ifdef FADE
|
||||||
shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));
|
shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));
|
||||||
#endif
|
#endif
|
||||||
shadow= shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
|
|
||||||
|
|
||||||
|
shadow= shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
|
||||||
return color * vec4(shadow, shadow, shadow, 1.0);
|
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
|
#define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
|
||||||
#else
|
#else
|
||||||
#define SHADOWMAP sampler2D
|
#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
|
#endif
|
||||||
|
|
||||||
#if FILTER_MODE == 0
|
#if FILTER_MODE == 0
|
||||||
@ -49,29 +49,31 @@ uniform float m_ShadowIntensity;
|
|||||||
const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
|
const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
|
||||||
float shadowBorderScale = 1.0;
|
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);
|
vec4 coord = vec4(projCoord.xy + offset.xy * pixSize2 * shadowBorderScale, projCoord.zw);
|
||||||
return SHADOWCOMPARE(tex, coord);
|
return SHADOWCOMPARE(tex, coord);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Shadow_DoShadowCompare(in SHADOWMAP tex, vec4 projCoord){
|
float Shadow_DoShadowCompare(SHADOWMAP tex, vec4 projCoord){
|
||||||
return SHADOWCOMPARE(tex, projCoord);
|
return SHADOWCOMPARE(tex, projCoord);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Shadow_BorderCheck(in vec2 coord){
|
float Shadow_BorderCheck(vec2 coord){
|
||||||
#ifdef PSSM
|
|
||||||
// Fastest, "hack" method (uses 4-5 instructions)
|
// Fastest, "hack" method (uses 4-5 instructions)
|
||||||
vec4 t = vec4(coord.xy, 0.0, 1.0);
|
vec4 t = vec4(coord.xy, 0.0, 1.0);
|
||||||
t = step(t.wwxy, t.xyzz);
|
t = step(t.wwxy, t.xyzz);
|
||||||
return dot(t,t);
|
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);
|
float border = Shadow_BorderCheck(projCoord.xy);
|
||||||
if (border > 0.0)
|
if (border > 0.0)
|
||||||
return 1.0;
|
return 1.0;
|
||||||
@ -87,7 +89,7 @@ float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
|||||||
return shadow;
|
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);
|
float border = Shadow_BorderCheck(projCoord.xy);
|
||||||
if (border > 0.0)
|
if (border > 0.0)
|
||||||
return 1.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 );
|
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 shadow = 0.0;
|
||||||
float border = Shadow_BorderCheck(projCoord.xy);
|
float border = Shadow_BorderCheck(projCoord.xy);
|
||||||
if (border > 0.0)
|
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 poissonDisk10 = vec2(0.5653144, 0.60262);
|
||||||
const vec2 poissonDisk11 = vec2(0.0123658, 0.8627419);
|
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 shadow = 0.0;
|
||||||
float border = Shadow_BorderCheck(projCoord.xy);
|
float border = Shadow_BorderCheck(projCoord.xy);
|
||||||
if (border > 0.0)
|
if (border > 0.0)
|
||||||
@ -161,3 +163,69 @@ float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
|
|||||||
return shadow;
|
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)
|
#define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z)
|
||||||
#else
|
#else
|
||||||
#define SHADOWMAP sampler2D
|
#define SHADOWMAP sampler2D
|
||||||
#define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z / coord.w, textureProjOffset(tex, coord, offset).r)
|
#define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z, textureProjOffset(tex, coord, offset).r)
|
||||||
#define SHADOWCOMPARE(tex,coord) step(coord.z / coord.w, textureProj(tex, coord).r)
|
#define SHADOWCOMPARE(tex,coord) step(coord.z, textureProj(tex, coord).r)
|
||||||
#define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy))
|
#define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if FILTER_MODE == 0
|
#if FILTER_MODE == 0
|
||||||
#define GETSHADOW SHADOWCOMPARE
|
#define GETSHADOW Shadow_Nearest
|
||||||
#define KERNEL 1
|
#define KERNEL 1
|
||||||
#elif FILTER_MODE == 1
|
#elif FILTER_MODE == 1
|
||||||
#ifdef HARDWARE_SHADOWS
|
#ifdef HARDWARE_SHADOWS
|
||||||
#define GETSHADOW SHADOWCOMPARE
|
#define GETSHADOW Shadow_Nearest
|
||||||
#else
|
#else
|
||||||
#define GETSHADOW Shadow_DoBilinear_2x2
|
#define GETSHADOW Shadow_DoBilinear_2x2
|
||||||
#endif
|
#endif
|
||||||
@ -59,16 +59,18 @@ const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
|
|||||||
float shadowBorderScale = 1.0;
|
float shadowBorderScale = 1.0;
|
||||||
|
|
||||||
float Shadow_BorderCheck(in vec2 coord){
|
float Shadow_BorderCheck(in vec2 coord){
|
||||||
#ifdef PSSM
|
|
||||||
// Fastest, "hack" method (uses 4-5 instructions)
|
// Fastest, "hack" method (uses 4-5 instructions)
|
||||||
vec4 t = vec4(coord.xy, 0.0, 1.0);
|
vec4 t = vec4(coord.xy, 0.0, 1.0);
|
||||||
t = step(t.wwxy, t.xyzz);
|
t = step(t.wwxy, t.xyzz);
|
||||||
return dot(t,t);
|
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
|
float Shadow_Nearest(in SHADOWMAP tex, in vec4 projCoord){
|
||||||
return 0.0;
|
float border = Shadow_BorderCheck(projCoord.xy);
|
||||||
#endif
|
if (border > 0.0){
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
return SHADOWCOMPARE(tex,projCoord);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
||||||
@ -173,4 +175,68 @@ float Shadow_DoPCFPoisson(in SHADOWMAP tex, in vec4 projCoord){
|
|||||||
return shadow * 0.08333333333;
|
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.
|
* it's useful to render shadows in a small scene, but edges might look a bit jagged.
|
||||||
*
|
*
|
||||||
* @author Kirill Vainer
|
* @author Kirill Vainer
|
||||||
|
* @deprecated use {@link DirectionalLightShadowRenderer} with one split.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class BasicShadowRenderer implements SceneProcessor {
|
public class BasicShadowRenderer implements SceneProcessor {
|
||||||
|
|
||||||
private RenderManager renderManager;
|
private RenderManager renderManager;
|
||||||
@ -79,7 +81,7 @@ public class BasicShadowRenderer implements SceneProcessor {
|
|||||||
shadowCam = new Camera(size, size);
|
shadowCam = new Camera(size, size);
|
||||||
|
|
||||||
preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");
|
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);
|
postshadowMat.setTexture("ShadowMap", shadowMap);
|
||||||
|
|
||||||
dispPic.setTexture(manager, shadowMap, false);
|
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;
|
package com.jme3.shadow;
|
||||||
|
|
||||||
import com.jme3.light.DirectionalLight;
|
/**
|
||||||
import com.jme3.light.Light;
|
* <code>ShadowEdgeFiltering</code> specifies how shadows are filtered
|
||||||
import com.jme3.light.PointLight;
|
*/
|
||||||
import com.jme3.math.Vector3f;
|
public enum EdgeFilteringMode {
|
||||||
import com.jme3.renderer.Camera;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a camera according to a light
|
* Shadows are not filtered. Nearest sample is used, causing in blocky
|
||||||
* Handy to compute projection matrix of a light
|
* shadows.
|
||||||
* @author Kirill Vainer
|
|
||||||
*/
|
*/
|
||||||
public class ShadowCamera {
|
Nearest(0),
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the camera view direction and position based on the light
|
* Bilinear filtering is used. Has the potential of being hardware
|
||||||
|
* accelerated on some GPUs
|
||||||
*/
|
*/
|
||||||
public void updateLightCamera(Camera lightCam) {
|
Bilinear(1),
|
||||||
if (target.getType() == Light.Type.Directional) {
|
/**
|
||||||
DirectionalLight dl = (DirectionalLight) target;
|
* Dither-based sampling is used, very cheap but can look bad at low
|
||||||
lightCam.setParallelProjection(true);
|
* resolutions.
|
||||||
lightCam.setLocation(Vector3f.ZERO);
|
*/
|
||||||
lightCam.lookAtDirection(dl.getDirection(), Vector3f.UNIT_Y);
|
Dither(2),
|
||||||
lightCam.setFrustum(-1, 1, -1, 1, 1, -1);
|
/**
|
||||||
} else {
|
* 4x4 percentage-closer filtering is used. Shadows will be smoother at the
|
||||||
PointLight pl = (PointLight) target;
|
* cost of performance
|
||||||
lightCam.setParallelProjection(false);
|
*/
|
||||||
lightCam.setLocation(pl.getPosition());
|
PCF4(3),
|
||||||
// direction will have to be calculated automatically
|
/**
|
||||||
lightCam.setFrustumPerspective(45, 1, 1, 300);
|
* 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,81 +44,35 @@ import com.jme3.post.Filter;
|
|||||||
import com.jme3.renderer.RenderManager;
|
import com.jme3.renderer.RenderManager;
|
||||||
import com.jme3.renderer.ViewPort;
|
import com.jme3.renderer.ViewPort;
|
||||||
import com.jme3.renderer.queue.RenderQueue;
|
import com.jme3.renderer.queue.RenderQueue;
|
||||||
import com.jme3.shadow.PointLightShadowRenderer.CompareMode;
|
|
||||||
import com.jme3.shadow.PointLightShadowRenderer.FilterMode;
|
|
||||||
import com.jme3.texture.FrameBuffer;
|
import com.jme3.texture.FrameBuffer;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* This Filter does basically the same as a PointLightShadowRenderer except it renders
|
* This Filter does basically the same as a PointLightShadowRenderer except it
|
||||||
* the post shadow pass as a fulscreen quad pass instead of a geometry pass.
|
* renders the post shadow pass as a fulscreen quad pass instead of a geometry
|
||||||
* It's mostly faster than PointLightShadowRenderer as long as you have more than a about ten shadow recieving objects.
|
* pass. It's mostly faster than PointLightShadowRenderer as long as you have
|
||||||
* The expense is the draw back that the shadow Recieve mode set on spatial is ignored.
|
* more than a about ten shadow recieving objects. The expense is the draw back
|
||||||
* So basically all and only objects that render depth in the scene receive shadows.
|
* that the shadow Recieve mode set on spatial is ignored. So basically all and
|
||||||
* See this post for more details http://jmonkeyengine.org/groups/general-2/forum/topic/silly-question-about-shadow-rendering/#post-191599
|
* 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;
|
* API is basically the same as the PssmShadowRenderer;
|
||||||
*
|
*
|
||||||
* @author Rémy Bouquet aka Nehon
|
* @author Rémy Bouquet aka Nehon
|
||||||
*/
|
*/
|
||||||
public class PointLightShadowFilter extends Filter {
|
public class PointLightShadowFilter extends AbstractShadowFilter<PointLightShadowRenderer> {
|
||||||
|
|
||||||
private PointLightShadowRenderer plRenderer;
|
|
||||||
private ViewPort viewPort;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a PSSM Shadow Filter
|
* Creates a PointLightShadowFilter
|
||||||
* 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 assetManager the application asset manager
|
||||||
* @param size the size of the rendered shadowmaps (512,1024,2048, etc...)
|
* @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048,
|
||||||
* @param nbSplits the number of shadow maps rendered (the more shadow maps the more quality, the less fps).
|
* etc...)
|
||||||
*/
|
*/
|
||||||
public PointLightShadowFilter(AssetManager manager, int size) {
|
public PointLightShadowFilter(AssetManager assetManager, int shadowMapSize) {
|
||||||
super("Post Shadow");
|
super(assetManager, shadowMapSize,new PointLightShadowRenderer(assetManager, shadowMapSize));
|
||||||
material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md");
|
|
||||||
plRenderer = new PointLightShadowRenderer(manager, size, material);
|
|
||||||
plRenderer.needsfallBackMaterial = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Material getMaterial() {
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isRequiresDepthTexture() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Material getShadowMaterial() {
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
Vector4f tmpv = new Vector4f();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void preFrame(float tpf) {
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,7 +81,7 @@ public class PointLightShadowFilter extends Filter {
|
|||||||
* @return the point light
|
* @return the point light
|
||||||
*/
|
*/
|
||||||
public PointLight getLight() {
|
public PointLight getLight() {
|
||||||
return plRenderer.getLight();
|
return shadowRenderer.getLight();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,93 +90,7 @@ public class PointLightShadowFilter extends Filter {
|
|||||||
* @param light the point light
|
* @param light the point light
|
||||||
*/
|
*/
|
||||||
public void setLight(PointLight light) {
|
public void setLight(PointLight light) {
|
||||||
plRenderer.setLight(light);
|
shadowRenderer.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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -34,451 +34,47 @@ package com.jme3.shadow;
|
|||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.asset.AssetManager;
|
||||||
import com.jme3.light.PointLight;
|
import com.jme3.light.PointLight;
|
||||||
import com.jme3.material.Material;
|
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.math.Vector3f;
|
||||||
import com.jme3.post.SceneProcessor;
|
|
||||||
import com.jme3.renderer.Camera;
|
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.GeometryList;
|
||||||
import com.jme3.renderer.queue.OpaqueComparator;
|
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.Geometry;
|
||||||
import com.jme3.scene.Node;
|
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>
|
* PointLightShadowRenderer renders shadows for a point light
|
||||||
* 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
|
* @author Rémy Bouquet aka Nehon
|
||||||
*/
|
*/
|
||||||
public class PointLightShadowRenderer implements SceneProcessor {
|
public class PointLightShadowRenderer extends AbstractShadowRenderer {
|
||||||
|
|
||||||
/**
|
public static final int CAM_NUMBER = 6;
|
||||||
* <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;
|
|
||||||
protected PointLight light;
|
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 Camera[] shadowCams;
|
||||||
protected Material preshadowMat;
|
protected GeometryList shadowMapOccluders = new GeometryList(new OpaqueComparator());
|
||||||
protected Material postshadowMat;
|
private Geometry[] frustums = null;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a PSSM Shadow Renderer More info on the technique at <a
|
* Creates a PointLightShadowRenderer
|
||||||
* 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 assetManager the application asset manager
|
||||||
* @param size the size of the rendered shadowmaps (512,1024,2048, etc...)
|
* @param shadowMapSize the size of the rendered shadowmaps (512,1024,2048,
|
||||||
* @param nbSplits the number of shadow maps rendered (the more shadow maps
|
* etc...)
|
||||||
* 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 PointLightShadowRenderer(AssetManager manager, int size) {
|
public PointLightShadowRenderer(AssetManager assetManager, int shadowMapSize) {
|
||||||
this(manager, size, new Material(manager, "Common/MatDefs/Shadow/PostShadowPSSM.j3md"));
|
super(assetManager, shadowMapSize, CAM_NUMBER);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
shadowCams = new Camera[CAM_NUMBER];
|
shadowCams = new Camera[CAM_NUMBER];
|
||||||
|
|
||||||
for (int i = 0; i < CAM_NUMBER; i++) {
|
for (int i = 0; i < CAM_NUMBER; i++) {
|
||||||
lightViewProjectionsMatrices[i] = new Matrix4f();
|
shadowCams[i] = new Camera(shadowMapSize, shadowMapSize);
|
||||||
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";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInitialized() {
|
@Override
|
||||||
return viewPort != null;
|
protected void updateShadowCams(Camera viewCam) {
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("fallthrough")
|
|
||||||
public void postQueue(RenderQueue rq) {
|
|
||||||
if (light == null) {
|
if (light == null) {
|
||||||
throw new IllegalStateException("The light can't be null for a " + this.getClass().getName());
|
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
|
//bottom
|
||||||
shadowCams[0].setAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Z.mult(-1f), Vector3f.UNIT_Y.mult(-1f));
|
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();
|
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];
|
frustums = new Geometry[CAM_NUMBER];
|
||||||
Vector3f[] points = new Vector3f[8];
|
Vector3f[] points = new Vector3f[8];
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
@ -516,220 +127,14 @@ public class PointLightShadowRenderer implements SceneProcessor {
|
|||||||
frustums[i] = createFrustum(points, i);
|
frustums[i] = createFrustum(points, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (frustums[shadowMapIndex].getParent() == null) {
|
||||||
|
((Node) viewPort.getScenes().get(0)).attachChild(frustums[shadowMapIndex]);
|
||||||
}
|
|
||||||
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
|
@Override
|
||||||
for (Material mat : matCache) {
|
protected void setMaterialParameters(Material material) {
|
||||||
|
material.setVector3("LightPos", light.getPosition());
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -748,6 +153,5 @@ public class PointLightShadowRenderer implements SceneProcessor {
|
|||||||
*/
|
*/
|
||||||
public void setLight(PointLight light) {
|
public void setLight(PointLight light) {
|
||||||
this.light = light;
|
this.light = light;
|
||||||
updateShadowCams();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,9 @@ import java.io.IOException;
|
|||||||
* API is basically the same as the PssmShadowRenderer;
|
* API is basically the same as the PssmShadowRenderer;
|
||||||
*
|
*
|
||||||
* @author Rémy Bouquet aka Nehon
|
* @author Rémy Bouquet aka Nehon
|
||||||
|
* @deprecated use {@link DirectionalLightShadowFilter}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class PssmShadowFilter extends Filter {
|
public class PssmShadowFilter extends Filter {
|
||||||
|
|
||||||
private PssmShadowRenderer pssmRenderer;
|
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;
|
||||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
||||||
import com.jme3.scene.Geometry;
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.scene.debug.WireFrustum;
|
import com.jme3.scene.debug.WireFrustum;
|
||||||
import com.jme3.texture.FrameBuffer;
|
import com.jme3.texture.FrameBuffer;
|
||||||
@ -66,16 +67,20 @@ import java.util.List;
|
|||||||
* one.<br> splits are distributed so that the closer they are from the camera,
|
* 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>
|
* 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
|
* This result in a better quality shadow than standard shadow mapping.<br> for
|
||||||
* more informations on this read this
|
* more informations on this read this <a
|
||||||
* <a href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a><br>
|
* href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a><br>
|
||||||
* <p/>
|
* <p/>
|
||||||
* @author Rémy Bouquet aka Nehon
|
* @author Rémy Bouquet aka Nehon
|
||||||
|
* @deprecated use {@link DirectionalLightShadowRenderer}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class PssmShadowRenderer implements SceneProcessor {
|
public class PssmShadowRenderer implements SceneProcessor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>FilterMode</code> specifies how shadows are filtered
|
* <code>FilterMode</code> specifies how shadows are filtered
|
||||||
|
* @deprecated use {@link EdgeFilteringMode}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public enum FilterMode{
|
public enum FilterMode{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,30 +94,32 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
*/
|
*/
|
||||||
Bilinear,
|
Bilinear,
|
||||||
/**
|
/**
|
||||||
* Dither-based sampling is used, very cheap but can look bad
|
* Dither-based sampling is used, very cheap but can look bad at low
|
||||||
* at low resolutions.
|
* resolutions.
|
||||||
*/
|
*/
|
||||||
Dither,
|
Dither,
|
||||||
/**
|
/**
|
||||||
* 4x4 percentage-closer filtering is used. Shadows will be smoother
|
* 4x4 percentage-closer filtering is used. Shadows will be smoother at
|
||||||
* at the cost of performance
|
* the cost of performance
|
||||||
*/
|
*/
|
||||||
PCF4,
|
PCF4,
|
||||||
/**
|
/**
|
||||||
* 8x8 percentage-closer filtering is used. Shadows will be smoother
|
* 8x8 percentage-closer filtering is used. Shadows will be smoother at
|
||||||
* at the cost of performance
|
* the cost of performance
|
||||||
*/
|
*/
|
||||||
PCFPOISSON,
|
PCFPOISSON,
|
||||||
/**
|
/**
|
||||||
* 8x8 percentage-closer filtering is used. Shadows will be smoother
|
* 8x8 percentage-closer filtering is used. Shadows will be smoother at
|
||||||
* at the cost of performance
|
* the cost of performance
|
||||||
*/
|
*/
|
||||||
PCF8
|
PCF8
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the shadow comparison mode
|
* Specifies the shadow comparison mode
|
||||||
|
* @deprecated use {@link CompareMode}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public enum CompareMode {
|
public enum CompareMode {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,24 +176,30 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
protected boolean applyFadeInfo = false;
|
protected boolean applyFadeInfo = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a PSSM Shadow Renderer
|
* Create a PSSM Shadow Renderer More info on the technique at <a
|
||||||
* 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>
|
* 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 manager the application asset manager
|
||||||
* @param size the size of the rendered shadowmaps (512,1024,2048, etc...)
|
* @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
|
||||||
* @param nbSplits the number of shadow maps rendered (the more shadow maps the more quality, the less fps).
|
* 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) {
|
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
|
* Create a PSSM Shadow Renderer More info on the technique at <a
|
||||||
* 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>
|
* 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 manager the application asset manager
|
||||||
* @param size the size of the rendered shadowmaps (512,1024,2048, etc...)
|
* @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
|
||||||
* @param postShadowMat the material used for post shadows if you need to override it
|
* 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) {
|
protected PssmShadowRenderer(AssetManager manager, int size, int nbSplits, Material postShadowMat) {
|
||||||
|
|
||||||
@ -240,7 +253,9 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the filtering mode for shadow edges see {@link FilterMode} for more info
|
* Sets the filtering mode for shadow edges see {@link FilterMode} for more
|
||||||
|
* info
|
||||||
|
*
|
||||||
* @param filterMode
|
* @param filterMode
|
||||||
*/
|
*/
|
||||||
final public void setFilterMode(FilterMode filterMode) {
|
final public void setFilterMode(FilterMode filterMode) {
|
||||||
@ -271,6 +286,7 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* sets the shadow compare mode see {@link CompareMode} for more info
|
* sets the shadow compare mode see {@link CompareMode} for more info
|
||||||
|
*
|
||||||
* @param compareMode
|
* @param compareMode
|
||||||
*/
|
*/
|
||||||
final public void setCompareMode(CompareMode compareMode) {
|
final public void setCompareMode(CompareMode compareMode) {
|
||||||
@ -351,6 +367,7 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the light direction used by the processor
|
* returns the light direction used by the processor
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Vector3f getDirection() {
|
public Vector3f getDirection() {
|
||||||
@ -359,6 +376,7 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the light direction to use to compute shadows
|
* Sets the light direction to use to compute shadows
|
||||||
|
*
|
||||||
* @param direction
|
* @param direction
|
||||||
*/
|
*/
|
||||||
public void setDirection(Vector3f direction) {
|
public void setDirection(Vector3f direction) {
|
||||||
@ -425,12 +443,23 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
lightViewProjectionsMatrices[i].set(shadowCam.getViewProjectionMatrix());
|
lightViewProjectionsMatrices[i].set(shadowCam.getViewProjectionMatrix());
|
||||||
renderManager.setCamera(shadowCam, false);
|
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.setFrameBuffer(shadowFB[i]);
|
||||||
r.clearBuffers(false, true, false);
|
r.clearBuffers(false, true, false);
|
||||||
|
|
||||||
// render shadow casters to shadow map
|
// render shadow casters to shadow map
|
||||||
viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCam, true);
|
viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCam, true);
|
||||||
}
|
}
|
||||||
|
debugfrustums = false;
|
||||||
if (flushQueues) {
|
if (flushQueues) {
|
||||||
occluders.clear();
|
occluders.clear();
|
||||||
}
|
}
|
||||||
@ -441,6 +470,11 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
renderManager.setCamera(viewCam, false);
|
renderManager.setCamera(viewCam, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
boolean debugfrustums = false;
|
||||||
|
|
||||||
|
public void displayFrustum() {
|
||||||
|
debugfrustums = true;
|
||||||
|
}
|
||||||
|
|
||||||
//debug only : displays depth shadow maps
|
//debug only : displays depth shadow maps
|
||||||
protected void displayShadowMap(Renderer r) {
|
protected void displayShadowMap(Renderer r) {
|
||||||
@ -457,8 +491,9 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
renderManager.setCamera(cam, false);
|
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() {
|
public void displayDebug() {
|
||||||
debug = true;
|
debug = true;
|
||||||
@ -515,35 +550,21 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
|
|
||||||
//iterating through the mat cache and setting the parameters
|
//iterating through the mat cache and setting the parameters
|
||||||
for (Material mat : matCache) {
|
for (Material mat : matCache) {
|
||||||
if (mat.getParam("Splits") == null) {
|
|
||||||
mat.setColor("Splits", splits);
|
mat.setColor("Splits", splits);
|
||||||
}
|
|
||||||
if (mat.getParam("ShadowMapSize") == null) {
|
|
||||||
mat.setFloat("ShadowMapSize", shadowMapSize);
|
mat.setFloat("ShadowMapSize", shadowMapSize);
|
||||||
}
|
|
||||||
for (int j = 0; j < nbSplits; j++) {
|
for (int j = 0; j < nbSplits; j++) {
|
||||||
mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
|
mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
|
||||||
}
|
}
|
||||||
if (mat.getParam("ShadowMap0") == null) {
|
|
||||||
for (int j = 0; j < nbSplits; j++) {
|
for (int j = 0; j < nbSplits; j++) {
|
||||||
mat.setTexture("ShadowMap" + j, shadowMaps[j]);
|
mat.setTexture("ShadowMap" + j, shadowMaps[j]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (applyHWShadows || mat.getParam("HardwareShadows") == null) {
|
|
||||||
mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
|
mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
|
||||||
}
|
|
||||||
if (applyFilterMode || mat.getParam("FilterMode") == null) {
|
|
||||||
mat.setInt("FilterMode", filterMode.ordinal());
|
mat.setInt("FilterMode", filterMode.ordinal());
|
||||||
}
|
|
||||||
if (mat.getParam("PCFEdge") == null || applyPCFEdge) {
|
|
||||||
mat.setFloat("PCFEdge", edgesThickness);
|
mat.setFloat("PCFEdge", edgesThickness);
|
||||||
}
|
|
||||||
|
|
||||||
if (mat.getParam("ShadowIntensity") == null || applyShadowIntensity) {
|
|
||||||
mat.setFloat("ShadowIntensity", shadowIntensity);
|
mat.setFloat("ShadowIntensity", shadowIntensity);
|
||||||
}
|
|
||||||
|
|
||||||
if (fadeInfo != null && mat.getParam("FadeInfo") == null || applyFadeInfo) {
|
if (fadeInfo != null) {
|
||||||
mat.setVector2("FadeInfo", fadeInfo);
|
mat.setVector2("FadeInfo", fadeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -567,6 +588,7 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
postshadowMat.setColor("Splits", splits);
|
postshadowMat.setColor("Splits", splits);
|
||||||
for (int j = 0; j < nbSplits; j++) {
|
for (int j = 0; j < nbSplits; j++) {
|
||||||
postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
|
postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
|
||||||
|
postshadowMat.setTexture("ShadowMap" + j, shadowMaps[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -580,8 +602,8 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the labda parameter
|
* returns the labda parameter see #setLambda(float lambda)
|
||||||
* see #setLambda(float lambda)
|
*
|
||||||
* @return lambda
|
* @return lambda
|
||||||
*/
|
*/
|
||||||
public float getLambda() {
|
public float getLambda() {
|
||||||
@ -602,6 +624,7 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* How far the shadows are rendered in the view
|
* How far the shadows are rendered in the view
|
||||||
|
*
|
||||||
* @see #setShadowZExtend(float zFar)
|
* @see #setShadowZExtend(float zFar)
|
||||||
* @return shadowZExtend
|
* @return shadowZExtend
|
||||||
*/
|
*/
|
||||||
@ -610,8 +633,10 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the distance from the eye where the shadows will be rendered
|
* Set the distance from the eye where the shadows will be rendered default
|
||||||
* default value is dynamicaly computed to the shadow casters/receivers union bound zFar, capped to view frustum far value.
|
* 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
|
* @param zFar the zFar values that override the computed one
|
||||||
*/
|
*/
|
||||||
public void setShadowZExtend(float zFar) {
|
public void setShadowZExtend(float zFar) {
|
||||||
@ -624,6 +649,7 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the shdaow intensity
|
* returns the shdaow intensity
|
||||||
|
*
|
||||||
* @see #setShadowIntensity(float shadowIntensity)
|
* @see #setShadowIntensity(float shadowIntensity)
|
||||||
* @return shadowIntensity
|
* @return shadowIntensity
|
||||||
*/
|
*/
|
||||||
@ -632,10 +658,10 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the shadowIntensity, the value should be between 0 and 1,
|
* Set the shadowIntensity, the value should be between 0 and 1, a 0 value
|
||||||
* a 0 value gives a bright and invisilble shadow,
|
* gives a bright and invisilble shadow, a 1 value gives a pitch black
|
||||||
* a 1 value gives a pitch black shadow,
|
* shadow, default is 0.7
|
||||||
* default is 0.7
|
*
|
||||||
* @param shadowIntensity the darkness of the shadow
|
* @param shadowIntensity the darkness of the shadow
|
||||||
*/
|
*/
|
||||||
final public void setShadowIntensity(float shadowIntensity) {
|
final public void setShadowIntensity(float shadowIntensity) {
|
||||||
@ -646,6 +672,7 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the edges thickness
|
* returns the edges thickness
|
||||||
|
*
|
||||||
* @see #setEdgesThickness(int edgesThickness)
|
* @see #setEdgesThickness(int edgesThickness)
|
||||||
* @return edgesThickness
|
* @return edgesThickness
|
||||||
*/
|
*/
|
||||||
@ -654,7 +681,9 @@ 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
|
* 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
|
* @param edgesThickness
|
||||||
*/
|
*/
|
||||||
public void setEdgesThickness(int edgesThickness) {
|
public void setEdgesThickness(int edgesThickness) {
|
||||||
@ -666,6 +695,7 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* returns true if the PssmRenderer flushed the shadow queues
|
* returns true if the PssmRenderer flushed the shadow queues
|
||||||
|
*
|
||||||
* @return flushQueues
|
* @return flushQueues
|
||||||
*/
|
*/
|
||||||
public boolean isFlushQueues() {
|
public boolean isFlushQueues() {
|
||||||
@ -673,8 +703,10 @@ 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.
|
* Set this to false if you want to use several PssmRederers to have
|
||||||
* Make sure the last PssmRenderer in the stack DO flush the queues, but not the others
|
* 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
|
* @param flushQueues
|
||||||
*/
|
*/
|
||||||
public void setFlushQueues(boolean flushQueues) {
|
public void setFlushQueues(boolean flushQueues) {
|
||||||
@ -683,8 +715,9 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the length over which the shadow will fade out when using a
|
* Define the length over which the shadow will fade out when using a
|
||||||
* shadowZextend
|
* shadowZextend This is useful to make dynamic shadows fade into baked
|
||||||
* This is useful to make dynamic shadows fade into baked shadows in the distance.
|
* shadows in the distance.
|
||||||
|
*
|
||||||
* @param length the fade length in world units
|
* @param length the fade length in world units
|
||||||
*/
|
*/
|
||||||
public void setShadowZFadeLength(float length) {
|
public void setShadowZFadeLength(float length) {
|
||||||
@ -704,7 +737,9 @@ public class PssmShadowRenderer implements SceneProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* @return the fade length in world units
|
||||||
*/
|
*/
|
||||||
public float getShadowZFadeLength() {
|
public float getShadowZFadeLength() {
|
||||||
|
@ -361,8 +361,6 @@ public class ShadowUtil {
|
|||||||
|
|
||||||
if (ortho) {
|
if (ortho) {
|
||||||
shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
|
shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
|
||||||
} else {
|
|
||||||
shadowCam.setFrustumPerspective(45, 1, 1, 150);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create transform to rotate points to viewspace
|
// create transform to rotate points to viewspace
|
||||||
@ -442,9 +440,9 @@ public class ShadowUtil {
|
|||||||
|
|
||||||
splitMin.z = 0;
|
splitMin.z = 0;
|
||||||
|
|
||||||
if (!ortho) {
|
// if (!ortho) {
|
||||||
shadowCam.setFrustumPerspective(45, 1, 1, splitMax.z);
|
// shadowCam.setFrustumPerspective(45, 1, 1, splitMax.z);
|
||||||
}
|
// }
|
||||||
|
|
||||||
Matrix4f projMatrix = shadowCam.getProjectionMatrix();
|
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;
|
package jme3test.light;
|
||||||
|
|
||||||
import com.jme3.app.SimpleApplication;
|
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.light.PointLight;
|
||||||
import com.jme3.math.FastMath;
|
|
||||||
import com.jme3.math.Quaternion;
|
import com.jme3.math.Quaternion;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.post.FilterPostProcessor;
|
import com.jme3.post.FilterPostProcessor;
|
||||||
@ -46,17 +41,17 @@ import com.jme3.scene.Geometry;
|
|||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.shape.Box;
|
import com.jme3.scene.shape.Box;
|
||||||
import com.jme3.scene.shape.Sphere;
|
import com.jme3.scene.shape.Sphere;
|
||||||
|
import com.jme3.shadow.EdgeFilteringMode;
|
||||||
import com.jme3.shadow.PointLightShadowFilter;
|
import com.jme3.shadow.PointLightShadowFilter;
|
||||||
import com.jme3.shadow.PointLightShadowRenderer;
|
import com.jme3.shadow.PointLightShadowRenderer;
|
||||||
|
|
||||||
public class TestPointLightShadows extends SimpleApplication implements ActionListener {
|
public class TestPointLightShadows extends SimpleApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
TestPointLightShadows app = new TestPointLightShadows();
|
TestPointLightShadows app = new TestPointLightShadows();
|
||||||
app.start();
|
app.start();
|
||||||
}
|
}
|
||||||
Node lightNode;
|
Node lightNode;
|
||||||
private boolean hardwareShadows = true;
|
|
||||||
PointLightShadowRenderer plsr;
|
PointLightShadowRenderer plsr;
|
||||||
PointLightShadowFilter plsf;
|
PointLightShadowFilter plsf;
|
||||||
|
|
||||||
@ -91,147 +86,44 @@ public class TestPointLightShadows extends SimpleApplication implements ActionLi
|
|||||||
|
|
||||||
plsr = new PointLightShadowRenderer(assetManager, 512);
|
plsr = new PointLightShadowRenderer(assetManager, 512);
|
||||||
plsr.setLight((PointLight) scene.getLocalLightList().get(0));
|
plsr.setLight((PointLight) scene.getLocalLightList().get(0));
|
||||||
plsr.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest);
|
plsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
|
||||||
|
// plsr.setFlushQueues(false);
|
||||||
// plsr.displayDebug();
|
// plsr.displayDebug();
|
||||||
viewPort.addProcessor(plsr);
|
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 = new PointLightShadowFilter(assetManager, 512);
|
||||||
plsf.setLight((PointLight) scene.getLocalLightList().get(0));
|
plsf.setLight((PointLight) scene.getLocalLightList().get(0));
|
||||||
plsf.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest);
|
plsf.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
|
||||||
plsf.setEnabled(false);
|
plsf.setEnabled(false);
|
||||||
|
|
||||||
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
|
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
|
||||||
fpp.addFilter(plsf);
|
fpp.addFilter(plsf);
|
||||||
viewPort.addProcessor(fpp);
|
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
|
@Override
|
||||||
public void simpleUpdate(float tpf) {
|
public void simpleUpdate(float tpf) {
|
||||||
time += tpf;
|
// lightNode.move(FastMath.cos(tpf) * 0.4f, 0, FastMath.sin(tpf) * 0.4f);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -32,9 +32,13 @@
|
|||||||
package jme3test.light;
|
package jme3test.light;
|
||||||
|
|
||||||
import com.jme3.app.SimpleApplication;
|
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.font.BitmapText;
|
||||||
import com.jme3.input.KeyInput;
|
import com.jme3.input.KeyInput;
|
||||||
import com.jme3.input.controls.ActionListener;
|
import com.jme3.input.controls.ActionListener;
|
||||||
|
import com.jme3.input.controls.AnalogListener;
|
||||||
import com.jme3.input.controls.KeyTrigger;
|
import com.jme3.input.controls.KeyTrigger;
|
||||||
import com.jme3.light.AmbientLight;
|
import com.jme3.light.AmbientLight;
|
||||||
import com.jme3.light.DirectionalLight;
|
import com.jme3.light.DirectionalLight;
|
||||||
@ -63,6 +67,7 @@ import com.jme3.texture.Texture;
|
|||||||
import com.jme3.texture.Texture.WrapMode;
|
import com.jme3.texture.Texture.WrapMode;
|
||||||
import com.jme3.util.SkyFactory;
|
import com.jme3.util.SkyFactory;
|
||||||
import com.jme3.util.TangentBinormalGenerator;
|
import com.jme3.util.TangentBinormalGenerator;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public class TestPssmShadow extends SimpleApplication implements ActionListener {
|
public class TestPssmShadow extends SimpleApplication implements ActionListener {
|
||||||
|
|
||||||
@ -123,7 +128,7 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
|
|||||||
ground.setShadowMode(ShadowMode.CastAndReceive);
|
ground.setShadowMode(ShadowMode.CastAndReceive);
|
||||||
rootNode.attachChild(ground);
|
rootNode.attachChild(ground);
|
||||||
|
|
||||||
DirectionalLight l = new DirectionalLight();
|
l = new DirectionalLight();
|
||||||
l.setDirection(new Vector3f(-1, -1, -1));
|
l.setDirection(new Vector3f(-1, -1, -1));
|
||||||
rootNode.addLight(l);
|
rootNode.addLight(l);
|
||||||
|
|
||||||
@ -136,6 +141,7 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
|
|||||||
|
|
||||||
rootNode.attachChild(sky);
|
rootNode.attachChild(sky);
|
||||||
}
|
}
|
||||||
|
DirectionalLight l;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
@ -148,25 +154,31 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
|
|||||||
loadScene();
|
loadScene();
|
||||||
|
|
||||||
pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 3);
|
pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 3);
|
||||||
pssmRenderer.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
|
//pssmRenderer.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
|
||||||
// pssmRenderer.setDirection(new Vector3f(0.5973172f, -0.16583486f, 0.7846725f).normalizeLocal());
|
pssmRenderer.setDirection(new Vector3f(-0.5973172f, -0.56583486f, 0.8846725f).normalizeLocal());
|
||||||
pssmRenderer.setLambda(0.55f);
|
pssmRenderer.setLambda(0.55f);
|
||||||
pssmRenderer.setShadowIntensity(0.6f);
|
pssmRenderer.setShadowIntensity(0.6f);
|
||||||
pssmRenderer.setCompareMode(CompareMode.Software);
|
pssmRenderer.setCompareMode(CompareMode.Software);
|
||||||
pssmRenderer.setFilterMode(FilterMode.Dither);
|
pssmRenderer.setFilterMode(FilterMode.Dither);
|
||||||
// pssmRenderer.displayDebug();
|
|
||||||
|
pssmRenderer.displayFrustum();
|
||||||
viewPort.addProcessor(pssmRenderer);
|
viewPort.addProcessor(pssmRenderer);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pssmFilter = new PssmShadowFilter(assetManager, 1024, 3);
|
pssmFilter = new PssmShadowFilter(assetManager, 1024, 3);
|
||||||
pssmFilter.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
|
//pssmFilter.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
|
||||||
//pssmRenderer.setDirection(new Vector3f(0.5973172f, -0.16583486f, 0.7846725f).normalizeLocal());
|
pssmRenderer.setDirection(new Vector3f(-0.5973172f, -0.56583486f, 0.8846725f).normalizeLocal());
|
||||||
pssmFilter.setLambda(0.55f);
|
pssmFilter.setLambda(0.55f);
|
||||||
pssmFilter.setShadowIntensity(0.6f);
|
pssmFilter.setShadowIntensity(0.6f);
|
||||||
pssmFilter.setCompareMode(CompareMode.Software);
|
pssmFilter.setCompareMode(CompareMode.Software);
|
||||||
pssmFilter.setFilterMode(FilterMode.Dither);
|
pssmFilter.setFilterMode(FilterMode.Dither);
|
||||||
pssmFilter.setEnabled(false);
|
pssmFilter.setEnabled(false);
|
||||||
|
|
||||||
|
|
||||||
|
// pssmFilter.setShadowZFadeLength(300);
|
||||||
|
// pssmFilter.setShadowZExtend(500);
|
||||||
|
|
||||||
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
|
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
|
||||||
// fpp.setNumSamples(4);
|
// fpp.setNumSamples(4);
|
||||||
fpp.addFilter(pssmFilter);
|
fpp.addFilter(pssmFilter);
|
||||||
@ -195,7 +207,20 @@ public class TestPssmShadow extends SimpleApplication implements ActionListener
|
|||||||
inputManager.addMapping("lambdaDown", new KeyTrigger(KeyInput.KEY_J));
|
inputManager.addMapping("lambdaDown", new KeyTrigger(KeyInput.KEY_J));
|
||||||
inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN));
|
inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN));
|
||||||
inputManager.addMapping("switchGroundMat", new KeyTrigger(KeyInput.KEY_M));
|
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) {
|
private void print(String str) {
|
||||||
@ -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