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 ShadowMap2
|
||||
Texture2D ShadowMap3
|
||||
//pointLights
|
||||
Texture2D ShadowMap4
|
||||
Texture2D ShadowMap5
|
||||
|
||||
Float ShadowIntensity
|
||||
Vector4 Splits
|
||||
@ -117,6 +120,10 @@ MaterialDef Phong Lighting {
|
||||
Matrix4 LightViewProjectionMatrix1
|
||||
Matrix4 LightViewProjectionMatrix2
|
||||
Matrix4 LightViewProjectionMatrix3
|
||||
//pointLight
|
||||
Matrix4 LightViewProjectionMatrix4
|
||||
Matrix4 LightViewProjectionMatrix5
|
||||
Vector3 LightPos
|
||||
|
||||
Float PCFEdge
|
||||
Float ShadowMapSize
|
||||
@ -209,6 +216,8 @@ MaterialDef Phong Lighting {
|
||||
COLOR_MAP : ColorMap
|
||||
SHADOWMAP_SIZE : ShadowMapSize
|
||||
FADE : FadeInfo
|
||||
PSSM : Splits
|
||||
POINTLIGHT : LightViewProjectionMatrix5
|
||||
}
|
||||
|
||||
ForcedRenderState {
|
||||
@ -235,6 +244,8 @@ MaterialDef Phong Lighting {
|
||||
COLOR_MAP : ColorMap
|
||||
SHADOWMAP_SIZE : ShadowMapSize
|
||||
FADE : FadeInfo
|
||||
PSSM : Splits
|
||||
POINTLIGHT : LightViewProjectionMatrix5
|
||||
}
|
||||
|
||||
ForcedRenderState {
|
||||
|
@ -18,6 +18,12 @@ uniform mat4 m_LightViewProjectionMatrix1;
|
||||
uniform mat4 m_LightViewProjectionMatrix2;
|
||||
uniform mat4 m_LightViewProjectionMatrix3;
|
||||
|
||||
#ifdef POINTLIGHT
|
||||
uniform vec3 m_LightPos;
|
||||
uniform mat4 m_LightViewProjectionMatrix4;
|
||||
uniform mat4 m_LightViewProjectionMatrix5;
|
||||
#endif
|
||||
|
||||
#ifdef FADE
|
||||
uniform vec2 m_FadeInfo;
|
||||
#endif
|
||||
@ -47,23 +53,53 @@ void main(){
|
||||
vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
|
||||
vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
|
||||
vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
|
||||
#ifdef POINTLIGHT
|
||||
vec4 projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos;
|
||||
vec4 projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos;
|
||||
#endif
|
||||
|
||||
|
||||
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
|
||||
|
||||
float shadow = 1.0;
|
||||
if(shadowPosition < m_Splits.x){
|
||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
||||
}else if( shadowPosition < m_Splits.y){
|
||||
shadowBorderScale = 0.5;
|
||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
||||
}else if( shadowPosition < m_Splits.z){
|
||||
shadowBorderScale = 0.25;
|
||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
||||
}else if( shadowPosition < m_Splits.w){
|
||||
shadowBorderScale = 0.125;
|
||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
|
||||
}
|
||||
#ifdef PSSM
|
||||
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
|
||||
|
||||
if(shadowPosition < m_Splits.x){
|
||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
||||
}else if( shadowPosition < m_Splits.y){
|
||||
shadowBorderScale = 0.5;
|
||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
||||
}else if( shadowPosition < m_Splits.z){
|
||||
shadowBorderScale = 0.25;
|
||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
||||
}else if( shadowPosition < m_Splits.w){
|
||||
shadowBorderScale = 0.125;
|
||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef POINTLIGHT
|
||||
vec3 vect = worldPos.xyz - m_LightPos;
|
||||
vec3 absv= abs(vect);
|
||||
float maxComp = max(absv.x,max(absv.y,absv.z));
|
||||
if(maxComp == absv.y){
|
||||
if(vect.y < 0.0){
|
||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
||||
}else{
|
||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
||||
}
|
||||
}else if(maxComp == absv.z){
|
||||
if(vect.z < 0.0){
|
||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
||||
}else{
|
||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
|
||||
}
|
||||
}else if(maxComp == absv.x){
|
||||
if(vect.x < 0.0){
|
||||
shadow = GETSHADOW(m_ShadowMap4, projCoord4);
|
||||
}else{
|
||||
shadow = GETSHADOW(m_ShadowMap5, projCoord5);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FADE
|
||||
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 ShadowMap2
|
||||
Texture2D ShadowMap3
|
||||
//pointLights
|
||||
Texture2D ShadowMap4
|
||||
Texture2D ShadowMap5
|
||||
Vector3 LightPos
|
||||
|
||||
Float ShadowIntensity
|
||||
Vector4 Splits
|
||||
@ -16,7 +20,10 @@ MaterialDef Post Shadow {
|
||||
Matrix4 LightViewProjectionMatrix0
|
||||
Matrix4 LightViewProjectionMatrix1
|
||||
Matrix4 LightViewProjectionMatrix2
|
||||
Matrix4 LightViewProjectionMatrix3
|
||||
Matrix4 LightViewProjectionMatrix3
|
||||
//pointLight
|
||||
Matrix4 LightViewProjectionMatrix4
|
||||
Matrix4 LightViewProjectionMatrix5
|
||||
|
||||
Float PCFEdge
|
||||
|
||||
@ -48,6 +55,8 @@ MaterialDef Post Shadow {
|
||||
PCFEDGE : PCFEdge
|
||||
SHADOWMAP_SIZE : ShadowMapSize
|
||||
FADE : FadeInfo
|
||||
PSSM : Splits
|
||||
POINTLIGHT : LightViewProjectionMatrix5
|
||||
}
|
||||
|
||||
}
|
||||
@ -66,6 +75,8 @@ MaterialDef Post Shadow {
|
||||
PCFEDGE : PCFEdge
|
||||
SHADOWMAP_SIZE : ShadowMapSize
|
||||
FADE : FadeInfo
|
||||
PSSM : Splits
|
||||
POINTLIGHT : LightViewProjectionMatrix5
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,12 @@ uniform mat4 m_LightViewProjectionMatrix1;
|
||||
uniform mat4 m_LightViewProjectionMatrix2;
|
||||
uniform mat4 m_LightViewProjectionMatrix3;
|
||||
|
||||
#ifdef POINTLIGHT
|
||||
uniform vec3 m_LightPos;
|
||||
uniform mat4 m_LightViewProjectionMatrix4;
|
||||
uniform mat4 m_LightViewProjectionMatrix5;
|
||||
#endif
|
||||
|
||||
#ifdef FADE
|
||||
uniform vec2 m_FadeInfo;
|
||||
#endif
|
||||
@ -47,22 +53,53 @@ vec4 main_multiSample(in int numSample){
|
||||
vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
|
||||
vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
|
||||
vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
|
||||
#ifdef POINTLIGHT
|
||||
vec4 projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos;
|
||||
vec4 projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos;
|
||||
#endif
|
||||
|
||||
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
|
||||
|
||||
float shadow = 1.0;
|
||||
if(shadowPosition < m_Splits.x){
|
||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
||||
}else if( shadowPosition < m_Splits.y){
|
||||
shadowBorderScale = 0.5;
|
||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
||||
}else if( shadowPosition < m_Splits.z){
|
||||
shadowBorderScale = 0.25;
|
||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
||||
}else if( shadowPosition < m_Splits.w){
|
||||
shadowBorderScale = 0.125;
|
||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
|
||||
}
|
||||
#ifdef PSSM
|
||||
float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x + m_ViewProjectionMatrixRow2.y * worldPos.y + m_ViewProjectionMatrixRow2.z * worldPos.z + m_ViewProjectionMatrixRow2.w;
|
||||
|
||||
if(shadowPosition < m_Splits.x){
|
||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
||||
}else if( shadowPosition < m_Splits.y){
|
||||
shadowBorderScale = 0.5;
|
||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
||||
}else if( shadowPosition < m_Splits.z){
|
||||
shadowBorderScale = 0.25;
|
||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
||||
}else if( shadowPosition < m_Splits.w){
|
||||
shadowBorderScale = 0.125;
|
||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef POINTLIGHT
|
||||
vec3 vect = worldPos.xyz - m_LightPos;
|
||||
vec3 absv= abs(vect);
|
||||
float maxComp = max(absv.x,max(absv.y,absv.z));
|
||||
if(maxComp == absv.y){
|
||||
if(vect.y < 0.0){
|
||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
||||
}else{
|
||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
||||
}
|
||||
}else if(maxComp == absv.z){
|
||||
if(vect.z < 0.0){
|
||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
||||
}else{
|
||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
|
||||
}
|
||||
}else if(maxComp == absv.x){
|
||||
if(vect.x < 0.0){
|
||||
shadow = GETSHADOW(m_ShadowMap4, projCoord4);
|
||||
}else{
|
||||
shadow = GETSHADOW(m_ShadowMap5, projCoord5);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FADE
|
||||
shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));
|
||||
|
@ -1,11 +1,21 @@
|
||||
#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;
|
||||
@ -35,19 +45,46 @@ void main(){
|
||||
#endif
|
||||
|
||||
float shadow = 1.0;
|
||||
if(shadowPosition < m_Splits.x){
|
||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
||||
}else if( shadowPosition < m_Splits.y){
|
||||
shadowBorderScale = 0.5;
|
||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
||||
}else if( shadowPosition < m_Splits.z){
|
||||
shadowBorderScale = 0.25;
|
||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
||||
}else if( shadowPosition < m_Splits.w){
|
||||
shadowBorderScale = 0.125;
|
||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
|
||||
}
|
||||
|
||||
#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
|
||||
|
@ -8,6 +8,9 @@ MaterialDef Post Shadow {
|
||||
Texture2D ShadowMap1
|
||||
Texture2D ShadowMap2
|
||||
Texture2D ShadowMap3
|
||||
//pointLights
|
||||
Texture2D ShadowMap4
|
||||
Texture2D ShadowMap5
|
||||
|
||||
Float ShadowIntensity
|
||||
Vector4 Splits
|
||||
@ -17,6 +20,10 @@ MaterialDef Post Shadow {
|
||||
Matrix4 LightViewProjectionMatrix1
|
||||
Matrix4 LightViewProjectionMatrix2
|
||||
Matrix4 LightViewProjectionMatrix3
|
||||
//pointLight
|
||||
Matrix4 LightViewProjectionMatrix4
|
||||
Matrix4 LightViewProjectionMatrix5
|
||||
Vector3 LightPos
|
||||
|
||||
Float PCFEdge
|
||||
|
||||
@ -38,6 +45,8 @@ MaterialDef Post Shadow {
|
||||
PCFEDGE : PCFEdge
|
||||
SHADOWMAP_SIZE : ShadowMapSize
|
||||
FADE : FadeInfo
|
||||
PSSM : Splits
|
||||
POINTLIGHT : LightViewProjectionMatrix5
|
||||
}
|
||||
|
||||
RenderState {
|
||||
@ -62,6 +71,8 @@ MaterialDef Post Shadow {
|
||||
PCFEDGE : PCFEdge
|
||||
SHADOWMAP_SIZE : ShadowMapSize
|
||||
FADE : FadeInfo
|
||||
PSSM : Splits
|
||||
POINTLIGHT : LightViewProjectionMatrix5
|
||||
}
|
||||
|
||||
RenderState {
|
||||
|
@ -11,7 +11,17 @@ 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;
|
||||
|
||||
@ -30,9 +40,12 @@ const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
|
||||
void main(){
|
||||
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
|
||||
vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
|
||||
worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
|
||||
|
||||
#ifdef DISCARD_ALPHA
|
||||
texCoord = inTexCoord;
|
||||
@ -42,4 +55,8 @@ void main(){
|
||||
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,12 +1,23 @@
|
||||
#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;
|
||||
@ -35,20 +46,53 @@ void main(){
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
float shadow = 1.0;
|
||||
if(shadowPosition < m_Splits.x){
|
||||
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
|
||||
}else if( shadowPosition < m_Splits.y){
|
||||
shadowBorderScale = 0.5;
|
||||
shadow = GETSHADOW(m_ShadowMap1, projCoord1);
|
||||
}else if( shadowPosition < m_Splits.z){
|
||||
shadowBorderScale = 0.25;
|
||||
shadow = GETSHADOW(m_ShadowMap2, projCoord2);
|
||||
}else if( shadowPosition < m_Splits.w){
|
||||
shadowBorderScale = 0.125;
|
||||
shadow = GETSHADOW(m_ShadowMap3, projCoord3);
|
||||
}
|
||||
#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));
|
||||
|
@ -3,7 +3,7 @@
|
||||
#define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
|
||||
#else
|
||||
#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
|
||||
|
||||
#if FILTER_MODE == 0
|
||||
@ -35,8 +35,14 @@ uniform SHADOWMAP m_ShadowMap0;
|
||||
uniform SHADOWMAP m_ShadowMap1;
|
||||
uniform SHADOWMAP m_ShadowMap2;
|
||||
uniform SHADOWMAP m_ShadowMap3;
|
||||
#ifdef POINTLIGHT
|
||||
uniform SHADOWMAP m_ShadowMap4;
|
||||
uniform SHADOWMAP m_ShadowMap5;
|
||||
#endif
|
||||
|
||||
#ifdef PSSM
|
||||
uniform vec4 m_Splits;
|
||||
#endif
|
||||
|
||||
uniform float m_ShadowIntensity;
|
||||
|
||||
@ -53,10 +59,16 @@ float Shadow_DoShadowCompare(in SHADOWMAP tex, vec4 projCoord){
|
||||
}
|
||||
|
||||
float Shadow_BorderCheck(in vec2 coord){
|
||||
#ifdef PSSM
|
||||
// Fastest, "hack" method (uses 4-5 instructions)
|
||||
vec4 t = vec4(coord.xy, 0.0, 1.0);
|
||||
t = step(t.wwxy, t.xyzz);
|
||||
return dot(t,t);
|
||||
#else
|
||||
//fix me this is a big fat hack to avoid issues whith point lights,
|
||||
//this function fail to return correct values, but it works with PSSM
|
||||
return 0.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
|
||||
|
@ -9,8 +9,8 @@
|
||||
#define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z)
|
||||
#else
|
||||
#define SHADOWMAP sampler2D
|
||||
#define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z, textureProjOffset(tex, coord, offset).r)
|
||||
#define SHADOWCOMPARE(tex,coord) step(coord.z, textureProj(tex, coord).r)
|
||||
#define SHADOWCOMPAREOFFSET(tex,coord,offset) step(coord.z / coord.w, textureProjOffset(tex, coord, offset).r)
|
||||
#define SHADOWCOMPARE(tex,coord) step(coord.z / coord.w, textureProj(tex, coord).r)
|
||||
#define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy))
|
||||
#endif
|
||||
|
||||
@ -45,18 +45,30 @@ uniform SHADOWMAP m_ShadowMap0;
|
||||
uniform SHADOWMAP m_ShadowMap1;
|
||||
uniform SHADOWMAP m_ShadowMap2;
|
||||
uniform SHADOWMAP m_ShadowMap3;
|
||||
#ifdef POINTLIGHT
|
||||
uniform SHADOWMAP m_ShadowMap4;
|
||||
uniform SHADOWMAP m_ShadowMap5;
|
||||
#endif
|
||||
|
||||
#ifdef PSSM
|
||||
uniform vec4 m_Splits;
|
||||
#endif
|
||||
uniform float m_ShadowIntensity;
|
||||
|
||||
const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
|
||||
float shadowBorderScale = 1.0;
|
||||
|
||||
float Shadow_BorderCheck(in vec2 coord){
|
||||
#ifdef PSSM
|
||||
// Fastest, "hack" method (uses 4-5 instructions)
|
||||
vec4 t = vec4(coord.xy, 0.0, 1.0);
|
||||
t = step(t.wwxy, t.xyzz);
|
||||
return dot(t,t);
|
||||
#else
|
||||
//fix me this is a big fat hack to avoid issues whith point lights,
|
||||
//this function fail to return correct values, but it works with PSSM
|
||||
return 0.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
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.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1));
|
||||
gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
|
||||
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.min;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -65,19 +66,17 @@ public class ShadowUtil {
|
||||
*/
|
||||
public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) {
|
||||
int w = viewCam.getWidth();
|
||||
int h = viewCam.getHeight();
|
||||
float n = viewCam.getFrustumNear();
|
||||
float f = viewCam.getFrustumFar();
|
||||
int h = viewCam.getHeight();
|
||||
|
||||
points[0].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), n));
|
||||
points[1].set(viewCam.getWorldCoordinates(new Vector2f(0, h), n));
|
||||
points[2].set(viewCam.getWorldCoordinates(new Vector2f(w, h), n));
|
||||
points[3].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), n));
|
||||
points[0].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), 0));
|
||||
points[1].set(viewCam.getWorldCoordinates(new Vector2f(0, h), 0));
|
||||
points[2].set(viewCam.getWorldCoordinates(new Vector2f(w, h), 0));
|
||||
points[3].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), 0));
|
||||
|
||||
points[4].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), f));
|
||||
points[5].set(viewCam.getWorldCoordinates(new Vector2f(0, h), f));
|
||||
points[6].set(viewCam.getWorldCoordinates(new Vector2f(w, h), f));
|
||||
points[7].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), f));
|
||||
points[4].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), 1));
|
||||
points[5].set(viewCam.getWorldCoordinates(new Vector2f(0, h), 1));
|
||||
points[6].set(viewCam.getWorldCoordinates(new Vector2f(w, h), 1));
|
||||
points[7].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), 1));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -494,4 +493,28 @@ public class ShadowUtil {
|
||||
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