Point light shadows first push. Working solution based on rendering 6 different shadow maps.
Made a PointlLightShadowRenderer and a PointLightShadowFilter. - This will need a lot of refactoring as a lot of code is duplicated with the PSSMShadowRenderer - Also i plan to change the Shadow map rendering to a cubemap instead of 6 separate textures. Added a cornell box model and a test case git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9942 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
8d841b70dd
commit
66ddbb654d
@ -108,6 +108,9 @@ MaterialDef Phong Lighting {
|
|||||||
Texture2D ShadowMap1
|
Texture2D ShadowMap1
|
||||||
Texture2D ShadowMap2
|
Texture2D ShadowMap2
|
||||||
Texture2D ShadowMap3
|
Texture2D ShadowMap3
|
||||||
|
//pointLights
|
||||||
|
Texture2D ShadowMap4
|
||||||
|
Texture2D ShadowMap5
|
||||||
|
|
||||||
Float ShadowIntensity
|
Float ShadowIntensity
|
||||||
Vector4 Splits
|
Vector4 Splits
|
||||||
@ -117,6 +120,10 @@ MaterialDef Phong Lighting {
|
|||||||
Matrix4 LightViewProjectionMatrix1
|
Matrix4 LightViewProjectionMatrix1
|
||||||
Matrix4 LightViewProjectionMatrix2
|
Matrix4 LightViewProjectionMatrix2
|
||||||
Matrix4 LightViewProjectionMatrix3
|
Matrix4 LightViewProjectionMatrix3
|
||||||
|
//pointLight
|
||||||
|
Matrix4 LightViewProjectionMatrix4
|
||||||
|
Matrix4 LightViewProjectionMatrix5
|
||||||
|
Vector3 LightPos
|
||||||
|
|
||||||
Float PCFEdge
|
Float PCFEdge
|
||||||
Float ShadowMapSize
|
Float ShadowMapSize
|
||||||
@ -209,6 +216,8 @@ MaterialDef Phong Lighting {
|
|||||||
COLOR_MAP : ColorMap
|
COLOR_MAP : ColorMap
|
||||||
SHADOWMAP_SIZE : ShadowMapSize
|
SHADOWMAP_SIZE : ShadowMapSize
|
||||||
FADE : FadeInfo
|
FADE : FadeInfo
|
||||||
|
PSSM : Splits
|
||||||
|
POINTLIGHT : LightViewProjectionMatrix5
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
@ -235,6 +244,8 @@ MaterialDef Phong Lighting {
|
|||||||
COLOR_MAP : ColorMap
|
COLOR_MAP : ColorMap
|
||||||
SHADOWMAP_SIZE : ShadowMapSize
|
SHADOWMAP_SIZE : ShadowMapSize
|
||||||
FADE : FadeInfo
|
FADE : FadeInfo
|
||||||
|
PSSM : Splits
|
||||||
|
POINTLIGHT : LightViewProjectionMatrix5
|
||||||
}
|
}
|
||||||
|
|
||||||
ForcedRenderState {
|
ForcedRenderState {
|
||||||
|
@ -18,6 +18,12 @@ uniform mat4 m_LightViewProjectionMatrix1;
|
|||||||
uniform mat4 m_LightViewProjectionMatrix2;
|
uniform mat4 m_LightViewProjectionMatrix2;
|
||||||
uniform mat4 m_LightViewProjectionMatrix3;
|
uniform mat4 m_LightViewProjectionMatrix3;
|
||||||
|
|
||||||
|
#ifdef POINTLIGHT
|
||||||
|
uniform vec3 m_LightPos;
|
||||||
|
uniform mat4 m_LightViewProjectionMatrix4;
|
||||||
|
uniform mat4 m_LightViewProjectionMatrix5;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef FADE
|
#ifdef FADE
|
||||||
uniform vec2 m_FadeInfo;
|
uniform vec2 m_FadeInfo;
|
||||||
#endif
|
#endif
|
||||||
@ -47,23 +53,53 @@ void main(){
|
|||||||
vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
|
vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
|
||||||
vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
|
vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
|
||||||
vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
|
vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
|
||||||
|
#ifdef POINTLIGHT
|
||||||
|
vec4 projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos;
|
||||||
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
|
vec4 projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos;
|
||||||
|
#endif
|
||||||
|
|
||||||
float shadow = 1.0;
|
float shadow = 1.0;
|
||||||
if(shadowPosition < m_Splits.x){
|
#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 if( shadowPosition < m_Splits.y){
|
|
||||||
shadowBorderScale = 0.5;
|
if(shadowPosition < m_Splits.x){
|
||||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
||||||
}else if( shadowPosition < m_Splits.z){
|
}else if( shadowPosition < m_Splits.y){
|
||||||
shadowBorderScale = 0.25;
|
shadowBorderScale = 0.5;
|
||||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
||||||
}else if( shadowPosition < m_Splits.w){
|
}else if( shadowPosition < m_Splits.z){
|
||||||
shadowBorderScale = 0.125;
|
shadowBorderScale = 0.25;
|
||||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
|
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
|
#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));
|
||||||
|
@ -8,6 +8,10 @@ MaterialDef Post Shadow {
|
|||||||
Texture2D ShadowMap1
|
Texture2D ShadowMap1
|
||||||
Texture2D ShadowMap2
|
Texture2D ShadowMap2
|
||||||
Texture2D ShadowMap3
|
Texture2D ShadowMap3
|
||||||
|
//pointLights
|
||||||
|
Texture2D ShadowMap4
|
||||||
|
Texture2D ShadowMap5
|
||||||
|
Vector3 LightPos
|
||||||
|
|
||||||
Float ShadowIntensity
|
Float ShadowIntensity
|
||||||
Vector4 Splits
|
Vector4 Splits
|
||||||
@ -17,6 +21,9 @@ MaterialDef Post Shadow {
|
|||||||
Matrix4 LightViewProjectionMatrix1
|
Matrix4 LightViewProjectionMatrix1
|
||||||
Matrix4 LightViewProjectionMatrix2
|
Matrix4 LightViewProjectionMatrix2
|
||||||
Matrix4 LightViewProjectionMatrix3
|
Matrix4 LightViewProjectionMatrix3
|
||||||
|
//pointLight
|
||||||
|
Matrix4 LightViewProjectionMatrix4
|
||||||
|
Matrix4 LightViewProjectionMatrix5
|
||||||
|
|
||||||
Float PCFEdge
|
Float PCFEdge
|
||||||
|
|
||||||
@ -48,6 +55,8 @@ MaterialDef Post Shadow {
|
|||||||
PCFEDGE : PCFEdge
|
PCFEDGE : PCFEdge
|
||||||
SHADOWMAP_SIZE : ShadowMapSize
|
SHADOWMAP_SIZE : ShadowMapSize
|
||||||
FADE : FadeInfo
|
FADE : FadeInfo
|
||||||
|
PSSM : Splits
|
||||||
|
POINTLIGHT : LightViewProjectionMatrix5
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -66,6 +75,8 @@ MaterialDef Post Shadow {
|
|||||||
PCFEDGE : PCFEdge
|
PCFEDGE : PCFEdge
|
||||||
SHADOWMAP_SIZE : ShadowMapSize
|
SHADOWMAP_SIZE : ShadowMapSize
|
||||||
FADE : FadeInfo
|
FADE : FadeInfo
|
||||||
|
PSSM : Splits
|
||||||
|
POINTLIGHT : LightViewProjectionMatrix5
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,12 @@ uniform mat4 m_LightViewProjectionMatrix1;
|
|||||||
uniform mat4 m_LightViewProjectionMatrix2;
|
uniform mat4 m_LightViewProjectionMatrix2;
|
||||||
uniform mat4 m_LightViewProjectionMatrix3;
|
uniform mat4 m_LightViewProjectionMatrix3;
|
||||||
|
|
||||||
|
#ifdef POINTLIGHT
|
||||||
|
uniform vec3 m_LightPos;
|
||||||
|
uniform mat4 m_LightViewProjectionMatrix4;
|
||||||
|
uniform mat4 m_LightViewProjectionMatrix5;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef FADE
|
#ifdef FADE
|
||||||
uniform vec2 m_FadeInfo;
|
uniform vec2 m_FadeInfo;
|
||||||
#endif
|
#endif
|
||||||
@ -47,22 +53,53 @@ vec4 main_multiSample(in int numSample){
|
|||||||
vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
|
vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
|
||||||
vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
|
vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
|
||||||
vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
|
vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
|
||||||
|
#ifdef POINTLIGHT
|
||||||
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
|
vec4 projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos;
|
||||||
|
vec4 projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos;
|
||||||
|
#endif
|
||||||
|
|
||||||
float shadow = 1.0;
|
float shadow = 1.0;
|
||||||
if(shadowPosition < m_Splits.x){
|
#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 if( shadowPosition < m_Splits.y){
|
|
||||||
shadowBorderScale = 0.5;
|
if(shadowPosition < m_Splits.x){
|
||||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
||||||
}else if( shadowPosition < m_Splits.z){
|
}else if( shadowPosition < m_Splits.y){
|
||||||
shadowBorderScale = 0.25;
|
shadowBorderScale = 0.5;
|
||||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
||||||
}else if( shadowPosition < m_Splits.w){
|
}else if( shadowPosition < m_Splits.z){
|
||||||
shadowBorderScale = 0.125;
|
shadowBorderScale = 0.25;
|
||||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
|
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
|
#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));
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
#import "Common/ShaderLib/PssmShadows.glsllib"
|
#import "Common/ShaderLib/PssmShadows.glsllib"
|
||||||
|
|
||||||
|
#ifdef PSSM
|
||||||
varying float shadowPosition;
|
varying float shadowPosition;
|
||||||
|
#endif
|
||||||
|
|
||||||
varying vec4 projCoord0;
|
varying vec4 projCoord0;
|
||||||
varying vec4 projCoord1;
|
varying vec4 projCoord1;
|
||||||
varying vec4 projCoord2;
|
varying vec4 projCoord2;
|
||||||
varying vec4 projCoord3;
|
varying vec4 projCoord3;
|
||||||
|
|
||||||
|
#ifdef POINTLIGHT
|
||||||
|
varying vec4 projCoord4;
|
||||||
|
varying vec4 projCoord5;
|
||||||
|
uniform vec3 m_LightPos;
|
||||||
|
varying vec4 worldPos;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef DISCARD_ALPHA
|
#ifdef DISCARD_ALPHA
|
||||||
#ifdef COLOR_MAP
|
#ifdef COLOR_MAP
|
||||||
uniform sampler2D m_ColorMap;
|
uniform sampler2D m_ColorMap;
|
||||||
@ -35,18 +45,45 @@ void main(){
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
float shadow = 1.0;
|
float shadow = 1.0;
|
||||||
if(shadowPosition < m_Splits.x){
|
#ifdef PSSM
|
||||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
if(shadowPosition < m_Splits.x){
|
||||||
}else if( shadowPosition < m_Splits.y){
|
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
||||||
shadowBorderScale = 0.5;
|
}else if( shadowPosition < m_Splits.y){
|
||||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
shadowBorderScale = 0.5;
|
||||||
}else if( shadowPosition < m_Splits.z){
|
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
||||||
shadowBorderScale = 0.25;
|
}else if( shadowPosition < m_Splits.z){
|
||||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
shadowBorderScale = 0.25;
|
||||||
}else if( shadowPosition < m_Splits.w){
|
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
||||||
shadowBorderScale = 0.125;
|
}else if( shadowPosition < m_Splits.w){
|
||||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
|
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
|
#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));
|
||||||
|
@ -8,6 +8,9 @@ MaterialDef Post Shadow {
|
|||||||
Texture2D ShadowMap1
|
Texture2D ShadowMap1
|
||||||
Texture2D ShadowMap2
|
Texture2D ShadowMap2
|
||||||
Texture2D ShadowMap3
|
Texture2D ShadowMap3
|
||||||
|
//pointLights
|
||||||
|
Texture2D ShadowMap4
|
||||||
|
Texture2D ShadowMap5
|
||||||
|
|
||||||
Float ShadowIntensity
|
Float ShadowIntensity
|
||||||
Vector4 Splits
|
Vector4 Splits
|
||||||
@ -17,6 +20,10 @@ MaterialDef Post Shadow {
|
|||||||
Matrix4 LightViewProjectionMatrix1
|
Matrix4 LightViewProjectionMatrix1
|
||||||
Matrix4 LightViewProjectionMatrix2
|
Matrix4 LightViewProjectionMatrix2
|
||||||
Matrix4 LightViewProjectionMatrix3
|
Matrix4 LightViewProjectionMatrix3
|
||||||
|
//pointLight
|
||||||
|
Matrix4 LightViewProjectionMatrix4
|
||||||
|
Matrix4 LightViewProjectionMatrix5
|
||||||
|
Vector3 LightPos
|
||||||
|
|
||||||
Float PCFEdge
|
Float PCFEdge
|
||||||
|
|
||||||
@ -38,6 +45,8 @@ MaterialDef Post Shadow {
|
|||||||
PCFEDGE : PCFEdge
|
PCFEDGE : PCFEdge
|
||||||
SHADOWMAP_SIZE : ShadowMapSize
|
SHADOWMAP_SIZE : ShadowMapSize
|
||||||
FADE : FadeInfo
|
FADE : FadeInfo
|
||||||
|
PSSM : Splits
|
||||||
|
POINTLIGHT : LightViewProjectionMatrix5
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderState {
|
RenderState {
|
||||||
@ -62,6 +71,8 @@ MaterialDef Post Shadow {
|
|||||||
PCFEDGE : PCFEdge
|
PCFEDGE : PCFEdge
|
||||||
SHADOWMAP_SIZE : ShadowMapSize
|
SHADOWMAP_SIZE : ShadowMapSize
|
||||||
FADE : FadeInfo
|
FADE : FadeInfo
|
||||||
|
PSSM : Splits
|
||||||
|
POINTLIGHT : LightViewProjectionMatrix5
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderState {
|
RenderState {
|
||||||
|
@ -11,7 +11,17 @@ varying vec4 projCoord1;
|
|||||||
varying vec4 projCoord2;
|
varying vec4 projCoord2;
|
||||||
varying vec4 projCoord3;
|
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;
|
varying float shadowPosition;
|
||||||
|
#endif
|
||||||
|
|
||||||
varying vec2 texCoord;
|
varying vec2 texCoord;
|
||||||
|
|
||||||
@ -30,9 +40,12 @@ const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
|
|||||||
void main(){
|
void main(){
|
||||||
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
|
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
|
||||||
|
|
||||||
shadowPosition = gl_Position.z;
|
#ifdef PSSM
|
||||||
|
shadowPosition = gl_Position.z;
|
||||||
|
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);
|
||||||
|
|
||||||
#ifdef DISCARD_ALPHA
|
#ifdef DISCARD_ALPHA
|
||||||
texCoord = inTexCoord;
|
texCoord = inTexCoord;
|
||||||
@ -42,4 +55,8 @@ void main(){
|
|||||||
projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
|
projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
|
||||||
projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
|
projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
|
||||||
projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
|
projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
|
||||||
|
#ifdef POINTLIGHT
|
||||||
|
projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos;
|
||||||
|
projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos;
|
||||||
|
#endif
|
||||||
}
|
}
|
@ -1,12 +1,23 @@
|
|||||||
#import "Common/ShaderLib/PssmShadows15.glsllib"
|
#import "Common/ShaderLib/PssmShadows15.glsllib"
|
||||||
|
|
||||||
out vec4 outFragColor;
|
out vec4 outFragColor;
|
||||||
|
|
||||||
|
#ifdef PSSM
|
||||||
in float shadowPosition;
|
in float shadowPosition;
|
||||||
|
#endif
|
||||||
|
|
||||||
in vec4 projCoord0;
|
in vec4 projCoord0;
|
||||||
in vec4 projCoord1;
|
in vec4 projCoord1;
|
||||||
in vec4 projCoord2;
|
in vec4 projCoord2;
|
||||||
in vec4 projCoord3;
|
in vec4 projCoord3;
|
||||||
|
|
||||||
|
#ifdef POINTLIGHT
|
||||||
|
in vec4 projCoord4;
|
||||||
|
in vec4 projCoord5;
|
||||||
|
uniform vec3 m_LightPos;
|
||||||
|
in vec4 worldPos;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef DISCARD_ALPHA
|
#ifdef DISCARD_ALPHA
|
||||||
#ifdef COLOR_MAP
|
#ifdef COLOR_MAP
|
||||||
uniform sampler2D m_ColorMap;
|
uniform sampler2D m_ColorMap;
|
||||||
@ -35,20 +46,53 @@ void main(){
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
float shadow = 1.0;
|
float shadow = 1.0;
|
||||||
if(shadowPosition < m_Splits.x){
|
#ifdef PSSM
|
||||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
if(shadowPosition < m_Splits.x){
|
||||||
}else if( shadowPosition < m_Splits.y){
|
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
||||||
shadowBorderScale = 0.5;
|
}else if( shadowPosition < m_Splits.y){
|
||||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
shadowBorderScale = 0.5;
|
||||||
}else if( shadowPosition < m_Splits.z){
|
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
||||||
shadowBorderScale = 0.25;
|
}else if( shadowPosition < m_Splits.z){
|
||||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
shadowBorderScale = 0.25;
|
||||||
}else if( shadowPosition < m_Splits.w){
|
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
||||||
shadowBorderScale = 0.125;
|
}else if( shadowPosition < m_Splits.w){
|
||||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
|
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
|
#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));
|
||||||
|
@ -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, texture2DProj(tex, coord).r)
|
#define SHADOWCOMPARE(tex,coord) step(coord.z / coord.w, texture2DProj(tex, coord).r)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FILTER_MODE == 0
|
#if FILTER_MODE == 0
|
||||||
@ -35,8 +35,14 @@ uniform SHADOWMAP m_ShadowMap0;
|
|||||||
uniform SHADOWMAP m_ShadowMap1;
|
uniform SHADOWMAP m_ShadowMap1;
|
||||||
uniform SHADOWMAP m_ShadowMap2;
|
uniform SHADOWMAP m_ShadowMap2;
|
||||||
uniform SHADOWMAP m_ShadowMap3;
|
uniform SHADOWMAP m_ShadowMap3;
|
||||||
|
#ifdef POINTLIGHT
|
||||||
|
uniform SHADOWMAP m_ShadowMap4;
|
||||||
|
uniform SHADOWMAP m_ShadowMap5;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PSSM
|
||||||
uniform vec4 m_Splits;
|
uniform vec4 m_Splits;
|
||||||
|
#endif
|
||||||
|
|
||||||
uniform float m_ShadowIntensity;
|
uniform float m_ShadowIntensity;
|
||||||
|
|
||||||
@ -53,10 +59,16 @@ float Shadow_DoShadowCompare(in SHADOWMAP tex, vec4 projCoord){
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
return 0.0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
#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, textureProjOffset(tex, coord, offset).r)
|
#define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z / coord.w, textureProjOffset(tex, coord, offset).r)
|
||||||
#define SHADOWCOMPARE(tex,coord) step(coord.z, textureProj(tex, coord).r)
|
#define SHADOWCOMPARE(tex,coord) step(coord.z / coord.w, 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
|
||||||
|
|
||||||
@ -45,18 +45,30 @@ uniform SHADOWMAP m_ShadowMap0;
|
|||||||
uniform SHADOWMAP m_ShadowMap1;
|
uniform SHADOWMAP m_ShadowMap1;
|
||||||
uniform SHADOWMAP m_ShadowMap2;
|
uniform SHADOWMAP m_ShadowMap2;
|
||||||
uniform SHADOWMAP m_ShadowMap3;
|
uniform SHADOWMAP m_ShadowMap3;
|
||||||
|
#ifdef POINTLIGHT
|
||||||
|
uniform SHADOWMAP m_ShadowMap4;
|
||||||
|
uniform SHADOWMAP m_ShadowMap5;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PSSM
|
||||||
uniform vec4 m_Splits;
|
uniform vec4 m_Splits;
|
||||||
|
#endif
|
||||||
uniform float m_ShadowIntensity;
|
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_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
|
||||||
|
return 0.0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
||||||
@ -90,7 +102,7 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
|||||||
gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0));
|
gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0));
|
||||||
gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1));
|
gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1));
|
||||||
gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
|
gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
|
vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
|
||||||
vec2 mx = mix( gather.xz, gather.yw, f.x );
|
vec2 mx = mix( gather.xz, gather.yw, f.x );
|
||||||
|
241
engine/src/core/com/jme3/shadow/PointLightShadowFilter.java
Normal file
241
engine/src/core/com/jme3/shadow/PointLightShadowFilter.java
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
* 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.PointLight;
|
||||||
|
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.shadow.PointLightShadowRenderer.CompareMode;
|
||||||
|
import com.jme3.shadow.PointLightShadowRenderer.FilterMode;
|
||||||
|
import com.jme3.texture.FrameBuffer;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This Filter does basically the same as a PointLightShadowRenderer except it renders
|
||||||
|
* the post shadow pass as a fulscreen quad pass instead of a geometry pass.
|
||||||
|
* It's mostly faster than PointLightShadowRenderer as long as you have more than a about ten shadow recieving objects.
|
||||||
|
* The expense is the draw back that the shadow Recieve mode set on spatial is ignored.
|
||||||
|
* So basically all and only objects that render depth in the scene receive shadows.
|
||||||
|
* See this post for more details http://jmonkeyengine.org/groups/general-2/forum/topic/silly-question-about-shadow-rendering/#post-191599
|
||||||
|
*
|
||||||
|
* API is basically the same as the PssmShadowRenderer;
|
||||||
|
*
|
||||||
|
* @author Rémy Bouquet aka Nehon
|
||||||
|
*/
|
||||||
|
public class PointLightShadowFilter extends Filter {
|
||||||
|
|
||||||
|
private PointLightShadowRenderer plRenderer;
|
||||||
|
private ViewPort viewPort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a PSSM Shadow Filter
|
||||||
|
* More info on the technique at <a href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a>
|
||||||
|
* @param manager the application asset manager
|
||||||
|
* @param size the size of the rendered shadowmaps (512,1024,2048, etc...)
|
||||||
|
* @param nbSplits the number of shadow maps rendered (the more shadow maps the more quality, the less fps).
|
||||||
|
*/
|
||||||
|
public PointLightShadowFilter(AssetManager manager, int size) {
|
||||||
|
super("Post Shadow");
|
||||||
|
material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md");
|
||||||
|
plRenderer = new PointLightShadowRenderer(manager, size, material);
|
||||||
|
plRenderer.needsfallBackMaterial = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Material getMaterial() {
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isRequiresDepthTexture() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Material getShadowMaterial() {
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
Vector4f tmpv = new Vector4f();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void preFrame(float tpf) {
|
||||||
|
plRenderer.preFrame(tpf);
|
||||||
|
material.setMatrix4("ViewProjectionMatrixInverse", viewPort.getCamera().getViewProjectionMatrix().invert());
|
||||||
|
Matrix4f m = viewPort.getCamera().getViewProjectionMatrix();
|
||||||
|
material.setVector4("ViewProjectionMatrixRow2", tmpv.set(m.m20, m.m21, m.m22, m.m23));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postQueue(RenderQueue queue) {
|
||||||
|
plRenderer.postQueue(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
|
||||||
|
plRenderer.setPostShadowParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
|
||||||
|
plRenderer.initialize(renderManager, vp);
|
||||||
|
this.viewPort = vp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets the point light used to cast shadows with this processor
|
||||||
|
*
|
||||||
|
* @return the point light
|
||||||
|
*/
|
||||||
|
public PointLight getLight() {
|
||||||
|
return plRenderer.getLight();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets the light to use for casting shadows with this processor
|
||||||
|
*
|
||||||
|
* @param light the point light
|
||||||
|
*/
|
||||||
|
public void setLight(PointLight light) {
|
||||||
|
plRenderer.setLight(light);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the shdaow intensity
|
||||||
|
* @see #setShadowIntensity(float shadowIntensity)
|
||||||
|
* @return shadowIntensity
|
||||||
|
*/
|
||||||
|
public float getShadowIntensity() {
|
||||||
|
return plRenderer.getShadowIntensity();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the shadowIntensity, the value should be between 0 and 1,
|
||||||
|
* a 0 value gives a bright and invisilble shadow,
|
||||||
|
* a 1 value gives a pitch black shadow,
|
||||||
|
* default is 0.7
|
||||||
|
* @param shadowIntensity the darkness of the shadow
|
||||||
|
*/
|
||||||
|
final public void setShadowIntensity(float shadowIntensity) {
|
||||||
|
plRenderer.setShadowIntensity(shadowIntensity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the edges thickness <br>
|
||||||
|
* @see #setEdgesThickness(int edgesThickness)
|
||||||
|
* @return edgesThickness
|
||||||
|
*/
|
||||||
|
public int getEdgesThickness() {
|
||||||
|
return plRenderer.getEdgesThickness();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the shadow edges thickness. default is 1, setting it to lower values can help to reduce the jagged effect of the shadow edges
|
||||||
|
* @param edgesThickness
|
||||||
|
*/
|
||||||
|
public void setEdgesThickness(int edgesThickness) {
|
||||||
|
plRenderer.setEdgesThickness(edgesThickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns true if the PssmRenderer flushed the shadow queues
|
||||||
|
* @return flushQueues
|
||||||
|
*/
|
||||||
|
public boolean isFlushQueues() {
|
||||||
|
return plRenderer.isFlushQueues();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this to false if you want to use several PssmRederers to have multiple shadows cast by multiple light sources.
|
||||||
|
* Make sure the last PssmRenderer in the stack DO flush the queues, but not the others
|
||||||
|
* @param flushQueues
|
||||||
|
*/
|
||||||
|
public void setFlushQueues(boolean flushQueues) {
|
||||||
|
plRenderer.setFlushQueues(flushQueues);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets the shadow compare mode see {@link CompareMode} for more info
|
||||||
|
* @param compareMode
|
||||||
|
*/
|
||||||
|
final public void setCompareMode(CompareMode compareMode) {
|
||||||
|
plRenderer.setCompareMode(compareMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the filtering mode for shadow edges see {@link FilterMode} for more info
|
||||||
|
* @param filterMode
|
||||||
|
*/
|
||||||
|
final public void setFilterMode(FilterMode filterMode) {
|
||||||
|
plRenderer.setFilterMode(filterMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the length over which the shadow will fade out when using a shadowZextend
|
||||||
|
* @param length the fade length in world units
|
||||||
|
*/
|
||||||
|
public void setShadowZFadeLength(float length){
|
||||||
|
plRenderer.setShadowZFadeLength(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the length over which the shadow will fade out when using a shadowZextend
|
||||||
|
* @return the fade length in world units
|
||||||
|
*/
|
||||||
|
public float getShadowZFadeLength(){
|
||||||
|
return plRenderer.getShadowZFadeLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
753
engine/src/core/com/jme3/shadow/PointLightShadowRenderer.java
Normal file
753
engine/src/core/com/jme3/shadow/PointLightShadowRenderer.java
Normal file
@ -0,0 +1,753 @@
|
|||||||
|
/*
|
||||||
|
* 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.PointLight;
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.math.Matrix4f;
|
||||||
|
import com.jme3.math.Vector2f;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.post.SceneProcessor;
|
||||||
|
import com.jme3.renderer.Camera;
|
||||||
|
import com.jme3.renderer.Caps;
|
||||||
|
import com.jme3.renderer.RenderManager;
|
||||||
|
import com.jme3.renderer.Renderer;
|
||||||
|
import com.jme3.renderer.ViewPort;
|
||||||
|
import com.jme3.renderer.queue.GeometryList;
|
||||||
|
import com.jme3.renderer.queue.OpaqueComparator;
|
||||||
|
import com.jme3.renderer.queue.RenderQueue;
|
||||||
|
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.debug.WireFrustum;
|
||||||
|
import com.jme3.texture.FrameBuffer;
|
||||||
|
import com.jme3.texture.Image.Format;
|
||||||
|
import com.jme3.texture.Texture.MagFilter;
|
||||||
|
import com.jme3.texture.Texture.MinFilter;
|
||||||
|
import com.jme3.texture.Texture.ShadowCompareMode;
|
||||||
|
import com.jme3.texture.Texture2D;
|
||||||
|
import com.jme3.ui.Picture;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PssmShadow renderer use Parrallel Split Shadow Mapping technique (pssm)<br>
|
||||||
|
* It splits the view frustum in several parts and compute a shadow map for each
|
||||||
|
* one.<br> splits are distributed so that the closer they are from the camera,
|
||||||
|
* the smaller they are to maximize the resolution used of the shadow map.<br>
|
||||||
|
* This result in a better quality shadow than standard shadow mapping.<br> for
|
||||||
|
* more informations on this read this <a
|
||||||
|
* href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a><br>
|
||||||
|
* <p/>
|
||||||
|
* @author Rémy Bouquet aka Nehon
|
||||||
|
*/
|
||||||
|
public class PointLightShadowRenderer implements SceneProcessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <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;
|
||||||
|
//common
|
||||||
|
protected float shadowMapSize;
|
||||||
|
protected float shadowIntensity = 0.7f;
|
||||||
|
protected float zFarOverride = 0;
|
||||||
|
protected RenderManager renderManager;
|
||||||
|
protected ViewPort viewPort;
|
||||||
|
protected FrameBuffer[] shadowFB;
|
||||||
|
protected Texture2D[] shadowMaps;
|
||||||
|
protected Texture2D dummyTex;
|
||||||
|
protected Camera[] shadowCams;
|
||||||
|
protected Material preshadowMat;
|
||||||
|
protected Material postshadowMat;
|
||||||
|
protected GeometryList splitOccluders = new GeometryList(new OpaqueComparator());
|
||||||
|
protected Matrix4f[] lightViewProjectionsMatrices;
|
||||||
|
protected boolean noOccluders = false;
|
||||||
|
protected AssetManager assetManager;
|
||||||
|
protected boolean debug = false;
|
||||||
|
protected float edgesThickness = 1.0f;
|
||||||
|
protected FilterMode filterMode;
|
||||||
|
protected CompareMode compareMode;
|
||||||
|
protected Picture[] dispPic;
|
||||||
|
protected boolean flushQueues = true;
|
||||||
|
// define if the fallback material should be used.
|
||||||
|
protected boolean needsfallBackMaterial = false;
|
||||||
|
//Name of the post material technique
|
||||||
|
protected String postTechniqueName = "PostShadow";
|
||||||
|
//flags to know when to change params in the materials
|
||||||
|
protected boolean applyHWShadows = true;
|
||||||
|
protected boolean applyFilterMode = true;
|
||||||
|
protected boolean applyPCFEdge = true;
|
||||||
|
protected boolean applyShadowIntensity = true;
|
||||||
|
//a list of material of the post shadow queue geometries.
|
||||||
|
protected List<Material> matCache = new ArrayList<Material>();
|
||||||
|
//Holding the info for fading shadows in the far distance
|
||||||
|
protected Vector2f fadeInfo;
|
||||||
|
protected float fadeLength;
|
||||||
|
protected boolean applyFadeInfo = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a PSSM Shadow Renderer More info on the technique at <a
|
||||||
|
* href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a>
|
||||||
|
*
|
||||||
|
* @param manager the application asset manager
|
||||||
|
* @param size the size of the rendered shadowmaps (512,1024,2048, etc...)
|
||||||
|
* @param nbSplits the number of shadow maps rendered (the more shadow maps
|
||||||
|
* the more quality, the less fps).
|
||||||
|
* @param nbSplits the number of shadow maps rendered (the more shadow maps
|
||||||
|
* the more quality, the less fps).
|
||||||
|
*/
|
||||||
|
public PointLightShadowRenderer(AssetManager manager, int size) {
|
||||||
|
this(manager, size, new Material(manager, "Common/MatDefs/Shadow/PostShadowPSSM.j3md"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a PSSM Shadow Renderer More info on the technique at <a
|
||||||
|
* href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a>
|
||||||
|
*
|
||||||
|
* @param manager the application asset manager
|
||||||
|
* @param size the size of the rendered shadowmaps (512,1024,2048, etc...)
|
||||||
|
* @param nbSplits the number of shadow maps rendered (the more shadow maps
|
||||||
|
* the more quality, the less fps).
|
||||||
|
* @param postShadowMat the material used for post shadows if you need to
|
||||||
|
* override it
|
||||||
|
*/
|
||||||
|
protected PointLightShadowRenderer(AssetManager manager, int size, Material postShadowMat) {
|
||||||
|
|
||||||
|
this.postshadowMat = postShadowMat;
|
||||||
|
assetManager = manager;
|
||||||
|
shadowMapSize = size;
|
||||||
|
|
||||||
|
shadowFB = new FrameBuffer[CAM_NUMBER];
|
||||||
|
shadowMaps = new Texture2D[CAM_NUMBER];
|
||||||
|
dispPic = new Picture[CAM_NUMBER];
|
||||||
|
lightViewProjectionsMatrices = new Matrix4f[CAM_NUMBER];
|
||||||
|
|
||||||
|
//DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
|
||||||
|
dummyTex = new Texture2D(size, size, Format.RGBA8);
|
||||||
|
|
||||||
|
preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");
|
||||||
|
postshadowMat.setFloat("ShadowMapSize", size);
|
||||||
|
|
||||||
|
shadowCams = new Camera[CAM_NUMBER];
|
||||||
|
|
||||||
|
for (int i = 0; i < CAM_NUMBER; i++) {
|
||||||
|
lightViewProjectionsMatrices[i] = new Matrix4f();
|
||||||
|
shadowFB[i] = new FrameBuffer(size, size, 1);
|
||||||
|
shadowMaps[i] = new Texture2D(size, size, Format.Depth);
|
||||||
|
|
||||||
|
shadowFB[i].setDepthTexture(shadowMaps[i]);
|
||||||
|
|
||||||
|
//DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
|
||||||
|
shadowFB[i].setColorTexture(dummyTex);
|
||||||
|
|
||||||
|
postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]);
|
||||||
|
|
||||||
|
//quads for debuging purpose
|
||||||
|
dispPic[i] = new Picture("Picture" + i);
|
||||||
|
dispPic[i].setTexture(manager, shadowMaps[i], false);
|
||||||
|
|
||||||
|
shadowCams[i] = new Camera(size, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCompareMode(CompareMode.Hardware);
|
||||||
|
setFilterMode(FilterMode.Bilinear);
|
||||||
|
setShadowIntensity(0.7f);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the filtering mode for shadow edges see {@link FilterMode} for more
|
||||||
|
* info
|
||||||
|
*
|
||||||
|
* @param filterMode
|
||||||
|
*/
|
||||||
|
final public void setFilterMode(FilterMode filterMode) {
|
||||||
|
if (filterMode == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.filterMode == filterMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.filterMode = filterMode;
|
||||||
|
postshadowMat.setInt("FilterMode", filterMode.ordinal());
|
||||||
|
postshadowMat.setFloat("PCFEdge", edgesThickness);
|
||||||
|
if (compareMode == CompareMode.Hardware) {
|
||||||
|
for (Texture2D shadowMap : shadowMaps) {
|
||||||
|
if (filterMode == FilterMode.Bilinear) {
|
||||||
|
shadowMap.setMagFilter(MagFilter.Bilinear);
|
||||||
|
shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps);
|
||||||
|
} else {
|
||||||
|
shadowMap.setMagFilter(MagFilter.Nearest);
|
||||||
|
shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
applyFilterMode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FilterMode getFilterMode() {
|
||||||
|
return filterMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets the shadow compare mode see {@link CompareMode} for more info
|
||||||
|
*
|
||||||
|
* @param compareMode
|
||||||
|
*/
|
||||||
|
final public void setCompareMode(CompareMode compareMode) {
|
||||||
|
if (compareMode == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.compareMode == compareMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.compareMode = compareMode;
|
||||||
|
for (Texture2D shadowMap : shadowMaps) {
|
||||||
|
if (compareMode == CompareMode.Hardware) {
|
||||||
|
shadowMap.setShadowCompareMode(ShadowCompareMode.LessOrEqual);
|
||||||
|
if (filterMode == FilterMode.Bilinear) {
|
||||||
|
shadowMap.setMagFilter(MagFilter.Bilinear);
|
||||||
|
shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps);
|
||||||
|
} else {
|
||||||
|
shadowMap.setMagFilter(MagFilter.Nearest);
|
||||||
|
shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
shadowMap.setShadowCompareMode(ShadowCompareMode.Off);
|
||||||
|
shadowMap.setMagFilter(MagFilter.Nearest);
|
||||||
|
shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
postshadowMat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
|
||||||
|
applyHWShadows = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug function that create a displayable frustrum
|
||||||
|
private Geometry createFrustum(Vector3f[] pts, int i) {
|
||||||
|
WireFrustum frustum = new WireFrustum(pts);
|
||||||
|
Geometry frustumMdl = new Geometry("f", frustum);
|
||||||
|
frustumMdl.setCullHint(Spatial.CullHint.Never);
|
||||||
|
frustumMdl.setShadowMode(ShadowMode.Off);
|
||||||
|
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||||
|
mat.getAdditionalRenderState().setWireframe(true);
|
||||||
|
frustumMdl.setMaterial(mat);
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Pink);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Red);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Green);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Blue);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Yellow);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.Gray);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
frustumMdl.getMaterial().setColor("Color", ColorRGBA.White);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
frustumMdl.updateGeometricState();
|
||||||
|
return frustumMdl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(RenderManager rm, ViewPort vp) {
|
||||||
|
renderManager = rm;
|
||||||
|
viewPort = vp;
|
||||||
|
//checking for caps to chosse the appropriate post material technique
|
||||||
|
if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) {
|
||||||
|
postTechniqueName = "PostShadow15";
|
||||||
|
} else {
|
||||||
|
postTechniqueName = "PostShadow";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return viewPort != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("fallthrough")
|
||||||
|
public void postQueue(RenderQueue rq) {
|
||||||
|
if (light == null) {
|
||||||
|
throw new IllegalStateException("The light can't be null for a " + this.getClass().getName());
|
||||||
|
}
|
||||||
|
GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast);
|
||||||
|
if (occluders.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive);
|
||||||
|
if (receivers.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera viewCam = viewPort.getCamera();
|
||||||
|
|
||||||
|
float zFar = zFarOverride;
|
||||||
|
if (zFar == 0) {
|
||||||
|
zFar = viewCam.getFrustumFar();
|
||||||
|
}
|
||||||
|
|
||||||
|
//We prevent computing the frustum points and splits with zeroed or negative near clip value
|
||||||
|
//float frustumNear = Math.max(viewCam.getFrustumNear(), 0.001f);
|
||||||
|
// ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, 1.0f, points);
|
||||||
|
|
||||||
|
updateShadowCams();
|
||||||
|
|
||||||
|
Renderer r = renderManager.getRenderer();
|
||||||
|
renderManager.setForcedMaterial(preshadowMat);
|
||||||
|
renderManager.setForcedTechnique("PreShadow");
|
||||||
|
|
||||||
|
for (int i = 0; i < CAM_NUMBER; i++) {
|
||||||
|
|
||||||
|
//Updating shadow cam with curent split frustra
|
||||||
|
|
||||||
|
ShadowUtil.getOccludersInCamFrustum(occluders, shadowCams[i], splitOccluders);
|
||||||
|
|
||||||
|
//saving light view projection matrix for this split
|
||||||
|
lightViewProjectionsMatrices[i].set(shadowCams[i].getViewProjectionMatrix());
|
||||||
|
renderManager.setCamera(shadowCams[i], false);
|
||||||
|
|
||||||
|
if (debug && frustums[i].getParent() == null) {
|
||||||
|
((Node) viewPort.getScenes().get(0)).attachChild(frustums[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
r.setFrameBuffer(shadowFB[i]);
|
||||||
|
r.clearBuffers(false, true, false);
|
||||||
|
|
||||||
|
// System.out.println("Face " + i + " nb occl " + splitOccluders.size());
|
||||||
|
// render shadow casters to shadow map
|
||||||
|
viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCams[i], true);
|
||||||
|
}
|
||||||
|
if (flushQueues) {
|
||||||
|
occluders.clear();
|
||||||
|
}
|
||||||
|
//restore setting for future rendering
|
||||||
|
r.setFrameBuffer(viewPort.getOutputFrameBuffer());
|
||||||
|
renderManager.setForcedMaterial(null);
|
||||||
|
renderManager.setForcedTechnique(null);
|
||||||
|
renderManager.setCamera(viewCam, false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//debug only : displays depth shadow maps
|
||||||
|
protected void displayShadowMap(Renderer r) {
|
||||||
|
Camera cam = viewPort.getCamera();
|
||||||
|
renderManager.setCamera(cam, true);
|
||||||
|
int h = cam.getHeight();
|
||||||
|
for (int i = 0; i < dispPic.length; i++) {
|
||||||
|
dispPic[i].setPosition((128 * i) + (150 + 64 * (i + 1)), h / 20f);
|
||||||
|
dispPic[i].setWidth(128);
|
||||||
|
dispPic[i].setHeight(128);
|
||||||
|
dispPic[i].updateGeometricState();
|
||||||
|
renderManager.renderGeometry(dispPic[i]);
|
||||||
|
}
|
||||||
|
renderManager.setCamera(cam, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For dubuging purpose Allow to "snapshot" the current frustrum to the
|
||||||
|
* scene
|
||||||
|
*/
|
||||||
|
public void displayDebug() {
|
||||||
|
debug = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postFrame(FrameBuffer out) {
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
displayShadowMap(renderManager.getRenderer());
|
||||||
|
}
|
||||||
|
if (!noOccluders) {
|
||||||
|
//setting params to recieving geometry list
|
||||||
|
setMatParams();
|
||||||
|
|
||||||
|
Camera cam = viewPort.getCamera();
|
||||||
|
//some materials in the scene does not have a post shadow technique so we're using the fall back material
|
||||||
|
if (needsfallBackMaterial) {
|
||||||
|
renderManager.setForcedMaterial(postshadowMat);
|
||||||
|
}
|
||||||
|
|
||||||
|
//forcing the post shadow technique and render state
|
||||||
|
renderManager.setForcedTechnique(postTechniqueName);
|
||||||
|
|
||||||
|
//rendering the post shadow pass
|
||||||
|
viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, cam, flushQueues);
|
||||||
|
|
||||||
|
//resetting renderManager settings
|
||||||
|
renderManager.setForcedTechnique(null);
|
||||||
|
renderManager.setForcedMaterial(null);
|
||||||
|
renderManager.setCamera(cam, false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateShadowCams() {
|
||||||
|
|
||||||
|
|
||||||
|
//bottom
|
||||||
|
shadowCams[0].setAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Z.mult(-1f), Vector3f.UNIT_Y.mult(-1f));
|
||||||
|
|
||||||
|
//top
|
||||||
|
shadowCams[1].setAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Z, Vector3f.UNIT_Y);
|
||||||
|
|
||||||
|
//forward
|
||||||
|
shadowCams[2].setAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Y, Vector3f.UNIT_Z.mult(-1f));
|
||||||
|
|
||||||
|
//backward
|
||||||
|
shadowCams[3].setAxes(Vector3f.UNIT_X, Vector3f.UNIT_Y, Vector3f.UNIT_Z);
|
||||||
|
|
||||||
|
//left
|
||||||
|
shadowCams[4].setAxes(Vector3f.UNIT_Z, Vector3f.UNIT_Y, Vector3f.UNIT_X.mult(-1f));
|
||||||
|
|
||||||
|
//right
|
||||||
|
shadowCams[5].setAxes(Vector3f.UNIT_Z.mult(-1f), Vector3f.UNIT_Y, Vector3f.UNIT_X);
|
||||||
|
|
||||||
|
for (int i = 0; i < CAM_NUMBER; i++) {
|
||||||
|
shadowCams[i].setFrustumPerspective(90f, 1f, 0.1f, light.getRadius());
|
||||||
|
shadowCams[i].setLocation(light.getPosition());
|
||||||
|
shadowCams[i].update();
|
||||||
|
shadowCams[i].updateViewProjection();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug && frustums == null) {
|
||||||
|
frustums = new Geometry[CAM_NUMBER];
|
||||||
|
Vector3f[] points = new Vector3f[8];
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
points[i] = new Vector3f();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < CAM_NUMBER; i++) {
|
||||||
|
ShadowUtil.updateFrustumPoints2(shadowCams[i], points);
|
||||||
|
frustums[i] = createFrustum(points, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
private Geometry[] frustums = null;
|
||||||
|
|
||||||
|
private void setMatParams() {
|
||||||
|
|
||||||
|
GeometryList l = viewPort.getQueue().getShadowQueueContent(ShadowMode.Receive);
|
||||||
|
|
||||||
|
//iteration throught all the geometries of the list to gather the materials
|
||||||
|
|
||||||
|
matCache.clear();
|
||||||
|
for (int i = 0; i < l.size(); i++) {
|
||||||
|
Material mat = l.get(i).getMaterial();
|
||||||
|
//checking if the material has the post technique and adding it to the material cache
|
||||||
|
if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) {
|
||||||
|
if (!matCache.contains(mat)) {
|
||||||
|
matCache.add(mat);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
needsfallBackMaterial = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//iterating through the mat cache and setting the parameters
|
||||||
|
for (Material mat : matCache) {
|
||||||
|
|
||||||
|
if (mat.getParam("ShadowMapSize") == null) {
|
||||||
|
mat.setFloat("ShadowMapSize", shadowMapSize);
|
||||||
|
}
|
||||||
|
for (int j = 0; j < CAM_NUMBER; j++) {
|
||||||
|
mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
|
||||||
|
}
|
||||||
|
mat.setVector3("LightPos", light.getPosition());
|
||||||
|
if (mat.getParam("ShadowMap0") == null) {
|
||||||
|
for (int j = 0; j < CAM_NUMBER; j++) {
|
||||||
|
mat.setTexture("ShadowMap" + j, shadowMaps[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (applyHWShadows || mat.getParam("HardwareShadows") == null) {
|
||||||
|
mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
|
||||||
|
}
|
||||||
|
if (applyFilterMode || mat.getParam("FilterMode") == null) {
|
||||||
|
mat.setInt("FilterMode", filterMode.ordinal());
|
||||||
|
}
|
||||||
|
if (mat.getParam("PCFEdge") == null || applyPCFEdge) {
|
||||||
|
mat.setFloat("PCFEdge", edgesThickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mat.getParam("ShadowIntensity") == null || applyShadowIntensity) {
|
||||||
|
mat.setFloat("ShadowIntensity", shadowIntensity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fadeInfo != null && mat.getParam("FadeInfo") == null || applyFadeInfo) {
|
||||||
|
mat.setVector2("FadeInfo", fadeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
applyHWShadows = false;
|
||||||
|
applyFilterMode = false;
|
||||||
|
applyPCFEdge = false;
|
||||||
|
applyShadowIntensity = false;
|
||||||
|
applyFadeInfo = false;
|
||||||
|
|
||||||
|
//At least one material of the receiving geoms does not support the post shadow techniques
|
||||||
|
//so we fall back to the forced material solution (transparent shadows won't be supported for these objects)
|
||||||
|
if (needsfallBackMaterial) {
|
||||||
|
setPostShadowParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setPostShadowParams() {
|
||||||
|
for (int j = 0; j < CAM_NUMBER; j++) {
|
||||||
|
postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
|
||||||
|
}
|
||||||
|
postshadowMat.setVector3("LightPos", light.getPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void preFrame(float tpf) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanup() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reshape(ViewPort vp, int w, int h) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the shdaow intensity
|
||||||
|
*
|
||||||
|
* @see #setShadowIntensity(float shadowIntensity)
|
||||||
|
* @return shadowIntensity
|
||||||
|
*/
|
||||||
|
public float getShadowIntensity() {
|
||||||
|
return shadowIntensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the shadowIntensity, the value should be between 0 and 1, a 0 value
|
||||||
|
* gives a bright and invisilble shadow, a 1 value gives a pitch black
|
||||||
|
* shadow, default is 0.7
|
||||||
|
*
|
||||||
|
* @param shadowIntensity the darkness of the shadow
|
||||||
|
*/
|
||||||
|
final public void setShadowIntensity(float shadowIntensity) {
|
||||||
|
this.shadowIntensity = shadowIntensity;
|
||||||
|
postshadowMat.setFloat("ShadowIntensity", shadowIntensity);
|
||||||
|
applyShadowIntensity = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How far the shadows are rendered in the view
|
||||||
|
*
|
||||||
|
* @see #setShadowZExtend(float zFar)
|
||||||
|
* @return shadowZExtend
|
||||||
|
*/
|
||||||
|
public float getShadowZExtend() {
|
||||||
|
return zFarOverride;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the distance from the eye where the shadows will be rendered default
|
||||||
|
* value is dynamicaly computed to the shadow casters/receivers union bound
|
||||||
|
* zFar, capped to view frustum far value.
|
||||||
|
*
|
||||||
|
* @param zFar the zFar values that override the computed one
|
||||||
|
*/
|
||||||
|
public void setShadowZExtend(float zFar) {
|
||||||
|
if (fadeInfo != null) {
|
||||||
|
fadeInfo.set(zFar - fadeLength, 1f / fadeLength);
|
||||||
|
}
|
||||||
|
this.zFarOverride = zFar;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the edges thickness
|
||||||
|
*
|
||||||
|
* @see #setEdgesThickness(int edgesThickness)
|
||||||
|
* @return edgesThickness
|
||||||
|
*/
|
||||||
|
public int getEdgesThickness() {
|
||||||
|
return (int) (edgesThickness * 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the shadow edges thickness. default is 1, setting it to lower values
|
||||||
|
* can help to reduce the jagged effect of the shadow edges
|
||||||
|
*
|
||||||
|
* @param edgesThickness
|
||||||
|
*/
|
||||||
|
public void setEdgesThickness(int edgesThickness) {
|
||||||
|
this.edgesThickness = Math.max(1, Math.min(edgesThickness, 10));
|
||||||
|
this.edgesThickness *= 0.1f;
|
||||||
|
postshadowMat.setFloat("PCFEdge", edgesThickness);
|
||||||
|
applyPCFEdge = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns true if the PssmRenderer flushed the shadow queues
|
||||||
|
*
|
||||||
|
* @return flushQueues
|
||||||
|
*/
|
||||||
|
public boolean isFlushQueues() {
|
||||||
|
return flushQueues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this to false if you want to use several PssmRederers to have
|
||||||
|
* multiple shadows cast by multiple light sources. Make sure the last
|
||||||
|
* PssmRenderer in the stack DO flush the queues, but not the others
|
||||||
|
*
|
||||||
|
* @param flushQueues
|
||||||
|
*/
|
||||||
|
public void setFlushQueues(boolean flushQueues) {
|
||||||
|
this.flushQueues = flushQueues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the length over which the shadow will fade out when using a
|
||||||
|
* shadowZextend This is useful to make dynamic shadows fade into baked
|
||||||
|
* shadows in the distance.
|
||||||
|
*
|
||||||
|
* @param length the fade length in world units
|
||||||
|
*/
|
||||||
|
public void setShadowZFadeLength(float length) {
|
||||||
|
if (length == 0) {
|
||||||
|
fadeInfo = null;
|
||||||
|
fadeLength = 0;
|
||||||
|
postshadowMat.clearParam("FadeInfo");
|
||||||
|
} else {
|
||||||
|
if (zFarOverride == 0) {
|
||||||
|
fadeInfo = new Vector2f(0, 0);
|
||||||
|
} else {
|
||||||
|
fadeInfo = new Vector2f(zFarOverride - length, 1.0f / length);
|
||||||
|
}
|
||||||
|
fadeLength = length;
|
||||||
|
postshadowMat.setVector2("FadeInfo", fadeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets the point light used to cast shadows with this processor
|
||||||
|
*
|
||||||
|
* @return the point light
|
||||||
|
*/
|
||||||
|
public PointLight getLight() {
|
||||||
|
return light;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets the light to use for casting shadows with this processor
|
||||||
|
*
|
||||||
|
* @param light the point light
|
||||||
|
*/
|
||||||
|
public void setLight(PointLight light) {
|
||||||
|
this.light = light;
|
||||||
|
updateShadowCams();
|
||||||
|
}
|
||||||
|
}
|
@ -44,6 +44,7 @@ import com.jme3.util.TempVars;
|
|||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,18 +67,16 @@ public class ShadowUtil {
|
|||||||
public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) {
|
public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) {
|
||||||
int w = viewCam.getWidth();
|
int w = viewCam.getWidth();
|
||||||
int h = viewCam.getHeight();
|
int h = viewCam.getHeight();
|
||||||
float n = viewCam.getFrustumNear();
|
|
||||||
float f = viewCam.getFrustumFar();
|
|
||||||
|
|
||||||
points[0].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), n));
|
points[0].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), 0));
|
||||||
points[1].set(viewCam.getWorldCoordinates(new Vector2f(0, h), n));
|
points[1].set(viewCam.getWorldCoordinates(new Vector2f(0, h), 0));
|
||||||
points[2].set(viewCam.getWorldCoordinates(new Vector2f(w, h), n));
|
points[2].set(viewCam.getWorldCoordinates(new Vector2f(w, h), 0));
|
||||||
points[3].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), n));
|
points[3].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), 0));
|
||||||
|
|
||||||
points[4].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), f));
|
points[4].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), 1));
|
||||||
points[5].set(viewCam.getWorldCoordinates(new Vector2f(0, h), f));
|
points[5].set(viewCam.getWorldCoordinates(new Vector2f(0, h), 1));
|
||||||
points[6].set(viewCam.getWorldCoordinates(new Vector2f(w, h), f));
|
points[6].set(viewCam.getWorldCoordinates(new Vector2f(w, h), 1));
|
||||||
points[7].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), f));
|
points[7].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -494,4 +493,28 @@ public class ShadowUtil {
|
|||||||
shadowCam.setProjectionMatrix(result);
|
shadowCam.setProjectionMatrix(result);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the shadow camera to properly contain the given
|
||||||
|
* points (which contain the eye camera frustum corners) and the
|
||||||
|
* shadow occluder objects.
|
||||||
|
*
|
||||||
|
* @param occluders
|
||||||
|
* @param shadowCam
|
||||||
|
* @param points
|
||||||
|
*/
|
||||||
|
public static void getOccludersInCamFrustum(GeometryList occluders,
|
||||||
|
Camera shadowCam,
|
||||||
|
GeometryList splitOccluders) {
|
||||||
|
for (int i = 0; i < occluders.size(); i++) {
|
||||||
|
Geometry g = occluders.get(i);
|
||||||
|
int planeState = shadowCam.getPlaneState();
|
||||||
|
shadowCam.setPlaneState(0);
|
||||||
|
if (shadowCam.contains(g.getWorldBound()) != Camera.FrustumIntersect.Outside) {
|
||||||
|
splitOccluders.add(g);
|
||||||
|
}
|
||||||
|
shadowCam.setPlaneState(planeState);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
237
engine/src/test/jme3test/light/TestPointLightShadows.java
Normal file
237
engine/src/test/jme3test/light/TestPointLightShadows.java
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
/*
|
||||||
|
* 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.PointLight;
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.post.FilterPostProcessor;
|
||||||
|
import com.jme3.renderer.queue.RenderQueue;
|
||||||
|
import com.jme3.scene.Geometry;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.shape.Box;
|
||||||
|
import com.jme3.scene.shape.Sphere;
|
||||||
|
import com.jme3.shadow.PointLightShadowFilter;
|
||||||
|
import com.jme3.shadow.PointLightShadowRenderer;
|
||||||
|
|
||||||
|
public class TestPointLightShadows extends SimpleApplication implements ActionListener {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
TestPointLightShadows app = new TestPointLightShadows();
|
||||||
|
app.start();
|
||||||
|
}
|
||||||
|
Node lightNode;
|
||||||
|
private boolean hardwareShadows = true;
|
||||||
|
PointLightShadowRenderer plsr ;
|
||||||
|
PointLightShadowFilter plsf;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void simpleInitApp() {
|
||||||
|
flyCam.setMoveSpeed(10);
|
||||||
|
cam.setLocation(new Vector3f(0.040581334f, 1.7745866f, 6.155161f));
|
||||||
|
cam.setRotation(new Quaternion(4.3868728E-5f, 0.9999293f, -0.011230096f, 0.0039059948f));
|
||||||
|
|
||||||
|
|
||||||
|
Node scene = (Node) assetManager.loadModel("Models/Test/CornellBox.j3o");
|
||||||
|
scene.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
|
||||||
|
rootNode.attachChild(scene);
|
||||||
|
rootNode.getChild("Cube").setShadowMode(RenderQueue.ShadowMode.Receive);
|
||||||
|
lightNode = (Node) rootNode.getChild("Lamp");
|
||||||
|
Geometry lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
|
||||||
|
//Geometry lightMdl = new Geometry("Light", new Box(.1f,.1f,.1f));
|
||||||
|
lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
|
||||||
|
lightMdl.setShadowMode(RenderQueue.ShadowMode.Off);
|
||||||
|
lightNode.attachChild(lightMdl);
|
||||||
|
//lightMdl.setLocalTranslation(lightNode.getLocalTranslation());
|
||||||
|
|
||||||
|
|
||||||
|
Geometry box = new Geometry("box", new Box(0.2f, 0.2f, 0.2f));
|
||||||
|
//Geometry lightMdl = new Geometry("Light", new Box(.1f,.1f,.1f));
|
||||||
|
box.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
|
||||||
|
box.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
|
||||||
|
rootNode.attachChild(box);
|
||||||
|
box.setLocalTranslation(-1f, 0.5f, -2);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
plsr = new PointLightShadowRenderer(assetManager, 512);
|
||||||
|
plsr.setLight((PointLight) scene.getLocalLightList().get(0));
|
||||||
|
plsr.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest);
|
||||||
|
// plsr.displayDebug();
|
||||||
|
viewPort.addProcessor(plsr);
|
||||||
|
|
||||||
|
|
||||||
|
plsf = new PointLightShadowFilter(assetManager, 512);
|
||||||
|
plsf.setLight((PointLight) scene.getLocalLightList().get(0));
|
||||||
|
plsf.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest);
|
||||||
|
plsf.setEnabled(false);
|
||||||
|
|
||||||
|
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
|
||||||
|
fpp.addFilter(plsf);
|
||||||
|
viewPort.addProcessor(fpp);
|
||||||
|
|
||||||
|
initUIAndInputs();
|
||||||
|
}
|
||||||
|
|
||||||
|
BitmapText shadowTypeText;
|
||||||
|
BitmapText shadowCompareText;
|
||||||
|
BitmapText shadowFilterText;
|
||||||
|
BitmapText shadowIntensityText;
|
||||||
|
final static String TYPE_TEXT = "(Space) Shadow type : ";
|
||||||
|
final static String COMPARE_TEXT = "(enter) Shadow compare ";
|
||||||
|
final static String FILTERING_TEXT = "(f) Edge filtering : ";
|
||||||
|
final static String INTENSITY_TEXT = "(t:up, g:down) Shadow intensity : ";
|
||||||
|
|
||||||
|
private void initUIAndInputs() {
|
||||||
|
guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
|
||||||
|
shadowTypeText = createText();
|
||||||
|
shadowCompareText = createText();
|
||||||
|
shadowFilterText = createText();
|
||||||
|
shadowIntensityText = createText();
|
||||||
|
|
||||||
|
shadowTypeText.setText(TYPE_TEXT+"Processor");
|
||||||
|
shadowCompareText.setText(COMPARE_TEXT+(hardwareShadows?"Hardware":"Software"));
|
||||||
|
shadowFilterText.setText(FILTERING_TEXT+plsr.getFilterMode().toString());
|
||||||
|
shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity());
|
||||||
|
|
||||||
|
shadowTypeText.setLocalTranslation(10, cam.getHeight()-20, 0);
|
||||||
|
shadowCompareText.setLocalTranslation(10, cam.getHeight()-40, 0);
|
||||||
|
shadowFilterText.setLocalTranslation(10, cam.getHeight()-60, 0);
|
||||||
|
shadowIntensityText.setLocalTranslation(10, cam.getHeight()-80, 0);
|
||||||
|
|
||||||
|
guiNode.attachChild(shadowTypeText);
|
||||||
|
guiNode.attachChild(shadowCompareText);
|
||||||
|
guiNode.attachChild(shadowFilterText);
|
||||||
|
guiNode.attachChild(shadowIntensityText);
|
||||||
|
|
||||||
|
inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
|
||||||
|
inputManager.addMapping("changeFiltering", new KeyTrigger(KeyInput.KEY_F));
|
||||||
|
inputManager.addMapping("ShadowUp", new KeyTrigger(KeyInput.KEY_T));
|
||||||
|
inputManager.addMapping("ShadowDown", new KeyTrigger(KeyInput.KEY_G));
|
||||||
|
inputManager.addMapping("ThicknessUp", new KeyTrigger(KeyInput.KEY_Y));
|
||||||
|
inputManager.addMapping("ThicknessDown", new KeyTrigger(KeyInput.KEY_H));
|
||||||
|
inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN));
|
||||||
|
|
||||||
|
|
||||||
|
inputManager.addListener(this, "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown", "changeFiltering");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int filteringIndex = 0;
|
||||||
|
int renderModeIndex = 0;
|
||||||
|
|
||||||
|
public void onAction(String name, boolean keyPressed, float tpf) {
|
||||||
|
if (name.equals("toggle") && keyPressed) {
|
||||||
|
renderModeIndex += 1;
|
||||||
|
renderModeIndex %= 3;
|
||||||
|
|
||||||
|
switch (renderModeIndex) {
|
||||||
|
case 0:
|
||||||
|
viewPort.addProcessor(plsr);
|
||||||
|
shadowTypeText.setText(TYPE_TEXT+"Processor");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
viewPort.removeProcessor(plsr);
|
||||||
|
plsf.setEnabled(true);
|
||||||
|
shadowTypeText.setText(TYPE_TEXT+"Filter");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
plsf.setEnabled(false);
|
||||||
|
shadowTypeText.setText(TYPE_TEXT+"None");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} else if (name.equals("toggleHW") && keyPressed) {
|
||||||
|
hardwareShadows = !hardwareShadows;
|
||||||
|
plsr.setCompareMode(hardwareShadows ? PointLightShadowRenderer.CompareMode.Hardware : PointLightShadowRenderer.CompareMode.Software);
|
||||||
|
plsf.setCompareMode(hardwareShadows ? PointLightShadowRenderer.CompareMode.Hardware : PointLightShadowRenderer.CompareMode.Software);
|
||||||
|
|
||||||
|
shadowCompareText.setText(COMPARE_TEXT+(hardwareShadows?"Hardware":"Software"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (name.equals("changeFiltering") && keyPressed) {
|
||||||
|
filteringIndex = plsr.getFilterMode().ordinal();
|
||||||
|
filteringIndex = (filteringIndex + 1) % PointLightShadowRenderer.FilterMode.values().length;
|
||||||
|
PointLightShadowRenderer.FilterMode m = PointLightShadowRenderer.FilterMode.values()[filteringIndex];
|
||||||
|
plsr.setFilterMode(m);
|
||||||
|
plsf.setFilterMode(m);
|
||||||
|
shadowFilterText.setText(FILTERING_TEXT+m.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (name.equals("ShadowUp") && keyPressed) {
|
||||||
|
plsr.setShadowIntensity(plsr.getShadowIntensity() + 0.1f);
|
||||||
|
plsf.setShadowIntensity(plsr.getShadowIntensity() + 0.1f);
|
||||||
|
|
||||||
|
shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity());
|
||||||
|
}
|
||||||
|
if (name.equals("ShadowDown") && keyPressed) {
|
||||||
|
plsr.setShadowIntensity(plsr.getShadowIntensity() - 0.1f);
|
||||||
|
plsf.setShadowIntensity(plsr.getShadowIntensity() - 0.1f);
|
||||||
|
shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity());
|
||||||
|
}
|
||||||
|
if (name.equals("ThicknessUp") && keyPressed) {
|
||||||
|
plsr.setEdgesThickness(plsr.getEdgesThickness() + 1);
|
||||||
|
plsf.setEdgesThickness(plsr.getEdgesThickness() + 1);
|
||||||
|
System.out.println("Shadow thickness : " + plsr.getEdgesThickness());
|
||||||
|
}
|
||||||
|
if (name.equals("ThicknessDown") && keyPressed) {
|
||||||
|
plsr.setEdgesThickness(plsr.getEdgesThickness() - 1);
|
||||||
|
plsf.setEdgesThickness(plsr.getEdgesThickness() - 1);
|
||||||
|
System.out.println("Shadow thickness : " + plsr.getEdgesThickness());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
float time;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void simpleUpdate(float tpf) {
|
||||||
|
time += tpf;
|
||||||
|
lightNode.setLocalTranslation(FastMath.cos(time)*0.4f,lightNode.getLocalTranslation().y , FastMath.sin(time)*0.4f);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private BitmapText createText() {
|
||||||
|
BitmapText t = new BitmapText(guiFont, false);
|
||||||
|
t.setSize(guiFont.getCharSet().getRenderedSize()*0.75f);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
BIN
engine/test-data/Models/Test/CornellBox.j3o
Normal file
BIN
engine/test-data/Models/Test/CornellBox.j3o
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user