Changed the way post shadow pass is done. It's now a technique of the lighting material definition.

This allow to have shadows that fully works with partially transparent objects (like trees).
If a material does not have the postShadow technique, the renderer uses the fallback material (like before).

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9279 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
rem..om 13 years ago
parent 7445565df3
commit bf55974bf4
  1. 69
      engine/src/core-data/Common/MatDefs/Light/Lighting.j3md
  2. 50
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag
  3. 9
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert
  4. 24
      engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag
  5. 22
      engine/src/core-data/Common/MatDefs/Shadow/PreShadow.frag
  6. 79
      engine/src/core/com/jme3/shadow/PssmShadowRenderer.java
  7. 57
      engine/src/test/jme3test/light/TestTransparentShadow.java

@ -99,6 +99,25 @@ MaterialDef Phong Lighting {
// the env map is a spheremap and not a cube map
Boolean EnvMapAsSphereMap
//shadows
Int FilterMode
Boolean HardwareShadows
Texture2D ShadowMap0
Texture2D ShadowMap1
Texture2D ShadowMap2
Texture2D ShadowMap3
Float ShadowIntensity
Vector4 Splits
Matrix4 LightViewProjectionMatrix0
Matrix4 LightViewProjectionMatrix1
Matrix4 LightViewProjectionMatrix2
Matrix4 LightViewProjectionMatrix3
Float PCFEdge
}
Technique {
@ -141,7 +160,7 @@ MaterialDef Phong Lighting {
SEPARATE_TEXCOORD : SeparateTexCoord
USE_REFLECTION : EnvMap
SPHERE_MAP : SphereMap
SPHERE_MAP : SphereMap
}
}
@ -156,7 +175,8 @@ MaterialDef Phong Lighting {
}
Defines {
DIFFUSEMAP_ALPHA : DiffuseMap
COLOR_MAP : ColorMap
DISCARD_ALPHA : AlphaDiscardThreshold
}
RenderState {
@ -169,6 +189,51 @@ MaterialDef Phong Lighting {
}
Technique PostShadow15{
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM.vert
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadowPSSM15.frag
WorldParameters {
WorldViewProjectionMatrix
WorldMatrix
}
Defines {
HARDWARE_SHADOWS : HardwareShadows
FILTER_MODE : FilterMode
PCFEDGE : PCFEdge
DISCARD_ALPHA : AlphaDiscardThreshold
COLOR_MAP : ColorMap
}
RenderState {
Blend Modulate
}
}
Technique PostShadow{
VertexShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.vert
FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadowPSSM.frag
WorldParameters {
WorldViewProjectionMatrix
WorldMatrix
}
Defines {
HARDWARE_SHADOWS : HardwareShadows
FILTER_MODE : FilterMode
PCFEDGE : PCFEdge
DISCARD_ALPHA : AlphaDiscardThreshold
COLOR_MAP : ColorMap
}
RenderState {
Blend Modulate
}
}
Technique PreNormalPass {
VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert

@ -1,6 +1,6 @@
#ifdef HARDWARE_SHADOWS
#define SHADOWMAP sampler2DShadow
#define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
#define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r
#else
#define SHADOWMAP sampler2D
#define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
@ -27,6 +27,7 @@
#define KERNEL 8.0
#endif
uniform SHADOWMAP m_ShadowMap0;
uniform SHADOWMAP m_ShadowMap1;
uniform SHADOWMAP m_ShadowMap2;
@ -43,7 +44,7 @@ varying vec4 projCoord3;
varying float shadowPosition;
const float texSize = 1024.0;
float texSize = 1024.0;
const float pixSize = 1.0 / texSize;
const vec2 pixSize2 = vec2(pixSize);
@ -64,6 +65,11 @@ float Shadow_BorderCheck(in vec2 coord){
}
float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
float shadow = 0.0;
vec2 o = mod(floor(gl_FragCoord.xy), 2.0);
shadow += Shadow_DoShadowCompareOffset(tex,projCoord,vec2(-1.5, 1.5) + o);
@ -75,6 +81,9 @@ float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
}
float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
vec4 gather = vec4(0.0);
gather.x = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(0.0, 0.0));
gather.y = Shadow_DoShadowCompareOffset(tex, projCoord, vec2(1.0, 0.0));
@ -88,12 +97,15 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
float shadow = 0.0;
float border = Shadow_BorderCheck(projCoord.xy);
if (border > 0.0)
return 1.0;
float bound = KERNEL * 0.5 - 0.5;
bound *= PCFEDGE;
for (float y = -bound; y <= bound; y += PCFEDGE){
for (float x = -bound; x <= bound; x += PCFEDGE){
shadow += clamp(Shadow_DoShadowCompareOffset(tex,projCoord,vec2(x,y)) +
Shadow_BorderCheck(projCoord.xy),
border,
0.0, 1.0);
}
}
@ -102,18 +114,46 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
return shadow;
}
void main(){
#ifdef DISCARD_ALPHA
#ifdef COLOR_MAP
uniform sampler2D m_ColorMap;
#else
uniform sampler2D m_DiffuseMap;
#endif
uniform float m_AlphaDiscardThreshold;
varying vec2 texCoord;
#endif
void main(){
#ifdef DISCARD_ALPHA
#ifdef COLOR_MAP
float alpha = texture2D(m_ColorMap,texCoord).a;
#else
float alpha = texture2D(m_DiffuseMap,texCoord).a;
#endif
if(alpha<=m_AlphaDiscardThreshold){
discard;
}
#endif
vec4 shadowPerSplit = vec4(0.0);
shadowPerSplit.x = GETSHADOW(m_ShadowMap0, projCoord0);
shadowPerSplit.y = GETSHADOW(m_ShadowMap1, projCoord1);
shadowPerSplit.z = GETSHADOW(m_ShadowMap2, projCoord2);
shadowPerSplit.w = GETSHADOW(m_ShadowMap3, projCoord3);
vec4 less = step( shadowPosition, m_Splits );
vec4 more = vec4(1.0) - step( shadowPosition, vec4(0.0, m_Splits.xyz) );
float shadow = dot(shadowPerSplit, less * more );
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
gl_FragColor = vec4(shadow, shadow, shadow, 1.0);
gl_FragColor = vec4(shadow, shadow, shadow, 1.0);
//gl_FragColor = vec4(alpha, alpha, alpha, 1.0);
}

@ -13,7 +13,12 @@ varying vec4 projCoord3;
varying float shadowPosition;
varying vec2 texCoord;
attribute vec3 inPosition;
#ifdef DISCARD_ALPHA
attribute vec2 inTexCoord;
#endif
const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0,
@ -28,7 +33,9 @@ void main(){
// get the vertex in world space
vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
#ifdef DISCARD_ALPHA
texCoord = inTexCoord;
#endif
// populate the light view matrices array and convert vertex to light viewProj space
projCoord0 = biasMat * m_LightViewProjectionMatrix0 * worldPos;
projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;

@ -120,9 +120,31 @@ float Shadow_DoPCF(in SHADOWMAP tex, in vec4 projCoord){
return shadow;
}
#ifdef DISCARD_ALPHA
#ifdef COLOR_MAP
uniform sampler2D m_ColorMap;
#else
uniform sampler2D m_DiffuseMap;
#endif
uniform float m_AlphaDiscardThreshold;
varying vec2 texCoord;
#endif
void main(){
float shadow = 0.0;
#ifdef DISCARD_ALPHA
#ifdef COLOR_MAP
float alpha = texture2D(m_ColorMap,texCoord).a;
#else
float alpha = texture2D(m_DiffuseMap,texCoord).a;
#endif
if(alpha < m_AlphaDiscardThreshold){
discard;
}
#endif
if(shadowPosition < m_Splits.x){
shadow = GETSHADOW(m_ShadowMap0, projCoord0);
}else if( shadowPosition < m_Splits.y){
@ -134,6 +156,6 @@ void main(){
}
shadow = shadow * m_ShadowIntensity + (1.0 - m_ShadowIntensity);
outFragColor = vec4(shadow, shadow, shadow, 1.0);
outFragColor = vec4(shadow, shadow, shadow, 1.0);
}

@ -1,14 +1,26 @@
varying vec2 texCoord;
#ifdef DIFFUSEMAP_ALPHA
uniform sampler2D m_DiffuseMap;
#ifdef DISCARD_ALPHA
#ifdef COLOR_MAP
uniform sampler2D m_ColorMap;
#else
uniform sampler2D m_DiffuseMap;
#endif
uniform float m_AlphaDiscardThreshold;
#endif
void main(){
#ifdef DIFFUSEMAP_ALPHA
if (texture2D(m_DiffuseMap, texCoord).a <= 0.50)
discard;
#ifdef DISCARD_ALPHA
#ifdef COLOR_MAP
if (texture2D(m_ColorMap, texCoord).a <= m_AlphaDiscardThreshold){
discard;
}
#else
if (texture2D(m_DiffuseMap, texCoord).a <= m_AlphaDiscardThreshold){
discard;
}
#endif
#endif
gl_FragColor = vec4(1.0);

@ -31,11 +31,13 @@ package com.jme3.shadow;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Matrix4f;
import com.jme3.math.Vector3f;
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;
@ -140,6 +142,12 @@ public class PssmShadowRenderer implements SceneProcessor {
private Picture[] dispPic;
private Vector3f[] points = new Vector3f[8];
private boolean flushQueues = true;
//render state for post shadow pass
private RenderState state = new RenderState();
// define if the fallback material should be used.
private boolean needsfallBackMaterial = false;
//Name of the post material technique
private String postTechniqueName = "PostShadow";
/**
* Create a PSSM Shadow Renderer
@ -208,13 +216,17 @@ public class PssmShadowRenderer implements SceneProcessor {
for (int i = 0; i < points.length; i++) {
points[i] = new Vector3f();
}
//initializing render state for post shadow pass (modulade blending and cullmode of for back faces )
state.setBlendMode(RenderState.BlendMode.Modulate);
state.setFaceCullMode(RenderState.FaceCullMode.Off);
}
/**
* Sets the filtering mode for shadow edges see {@link FilterMode} for more info
* @param filterMode
*/
public void setFilterMode(FilterMode filterMode) {
final public void setFilterMode(FilterMode filterMode) {
if (filterMode == null) {
throw new NullPointerException();
}
@ -243,7 +255,7 @@ public class PssmShadowRenderer implements SceneProcessor {
* sets the shadow compare mode see {@link CompareMode} for more info
* @param compareMode
*/
public void setCompareMode(CompareMode compareMode) {
final public void setCompareMode(CompareMode compareMode) {
if (compareMode == null) {
throw new NullPointerException();
}
@ -306,6 +318,12 @@ public class PssmShadowRenderer implements SceneProcessor {
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() {
@ -430,15 +448,24 @@ public class PssmShadowRenderer implements SceneProcessor {
public void postFrame(FrameBuffer out) {
Camera cam = viewPort.getCamera();
if (!noOccluders) {
postshadowMat.setColor("Splits", splits);
for (int i = 0; i < nbSplits; i++) {
postshadowMat.setMatrix4("LightViewProjectionMatrix" + i, lightViewProjectionsMatrices[i]);
//setting params to recieving geometry list
setMatParams();
//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);
}
renderManager.setForcedMaterial(postshadowMat);
//forcing the post shadow technique and render state
renderManager.setForcedTechnique(postTechniqueName);
renderManager.setForcedRenderState(state);
//rendering the post shadow pass
viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, cam, flushQueues);
//resetting renderManager settings
renderManager.setForcedTechnique(null);
renderManager.setForcedMaterial(null);
renderManager.setForcedRenderState(null);
renderManager.setCamera(cam, false);
}
@ -447,6 +474,42 @@ public class PssmShadowRenderer implements SceneProcessor {
}
}
private void setMatParams() {
GeometryList l = viewPort.getQueue().getShadowQueueContent(ShadowMode.Receive);
//iteratin throught all the geometries of the list to set the material params
for (int i = 0; i < l.size(); i++) {
Material mat = l.get(i).getMaterial();
//checking if the material has the post technique and setting the params.
if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) {
mat.setColor("Splits", splits);
postshadowMat.setColor("Splits", splits);
for (int j = 0; j < nbSplits; j++) {
mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
mat.setTexture("ShadowMap" + j, shadowMaps[j]);
}
mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
mat.setInt("FilterMode", filterMode.ordinal());
mat.setFloat("PCFEdge", edgesThickness);
mat.setFloat("ShadowIntensity", shadowIntensity);
} else {
needsfallBackMaterial = true;
}
}
//At least one material of the receiving geoms does not support the post shadow techniques
//so we fall back to the forced material solution (transparent shadows won't be supported for these objects)
if (needsfallBackMaterial) {
postshadowMat.setColor("Splits", splits);
for (int j = 0; j < nbSplits; j++) {
postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
}
}
}
public void preFrame(float tpf) {
}
@ -511,7 +574,7 @@ public class PssmShadowRenderer implements SceneProcessor {
* default is 0.7
* @param shadowIntensity the darkness of the shadow
*/
public void setShadowIntensity(float shadowIntensity) {
final public void setShadowIntensity(float shadowIntensity) {
this.shadowIntensity = shadowIntensity;
postshadowMat.setFloat("ShadowIntensity", shadowIntensity);
}

@ -42,10 +42,9 @@ import com.jme3.math.*;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.SceneGraphVisitorAdapter;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.LodControl;
import com.jme3.scene.shape.Quad;
import com.jme3.scene.shape.Sphere;
import com.jme3.shadow.PssmShadowRenderer;
import com.jme3.shadow.PssmShadowRenderer.CompareMode;
import com.jme3.shadow.PssmShadowRenderer.FilterMode;
@ -59,15 +58,16 @@ public class TestTransparentShadow extends SimpleApplication {
public void simpleInitApp() {
cam.setLocation(new Vector3f(2.0606942f, 3.20342f, 6.7860126f));
cam.setRotation(new Quaternion(-0.017481906f, 0.98241085f, -0.12393151f, -0.13857932f));
cam.setLocation(new Vector3f(5.700248f, 6.161693f, 5.1404157f));
cam.setRotation(new Quaternion(-0.09441641f, 0.8993388f, -0.24089815f, -0.35248178f));
viewPort.setBackgroundColor(ColorRGBA.DarkGray);
Quad q = new Quad(20, 20);
q.scaleTextureCoordinates(Vector2f.UNIT_XY.mult(5));
q.scaleTextureCoordinates(Vector2f.UNIT_XY.mult(10));
Geometry geom = new Geometry("floor", q);
Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
mat.setFloat("Shininess", 0);
geom.setMaterial(mat);
geom.rotate(-FastMath.HALF_PI, 0, 0);
@ -76,34 +76,21 @@ public class TestTransparentShadow extends SimpleApplication {
rootNode.attachChild(geom);
// create the geometry and attach it
Spatial teaGeom = assetManager.loadModel("Models/Tree/Tree.mesh.j3o");
teaGeom.setQueueBucket(Bucket.Transparent);
teaGeom.setShadowMode(ShadowMode.Cast);
Spatial tree = assetManager.loadModel("Models/Tree/Tree.mesh.j3o");
tree.setQueueBucket(Bucket.Transparent);
tree.setShadowMode(ShadowMode.CastAndReceive);
teaGeom.depthFirstTraversal(new SceneGraphVisitorAdapter(){
@Override
public void visit(Geometry geom) {
LodControl lodCtrl = new LodControl();
lodCtrl.setTrisPerPixel(0.25f);
geom.addControl(lodCtrl);
}
});
AmbientLight al = new AmbientLight();
al.setColor(ColorRGBA.White.mult(2));
al.setColor(ColorRGBA.White.mult(0.7f));
rootNode.addLight(al);
DirectionalLight dl1 = new DirectionalLight();
dl1.setDirection(new Vector3f(1, -1, 1).normalizeLocal());
dl1.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f));
dl1.setDirection(new Vector3f(0, -1, 0.5f).normalizeLocal());
dl1.setColor(ColorRGBA.White.mult(1.5f));
rootNode.addLight(dl1);
DirectionalLight dl = new DirectionalLight();
dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
dl.setColor(new ColorRGBA(0.965f, 0.949f, 0.772f, 1f).mult(0.7f));
rootNode.addLight(dl);
rootNode.attachChild(teaGeom);
rootNode.attachChild(tree);
/** Uses Texture from jme3-test-data library! */
ParticleEmitter fire = new ParticleEmitter("Emitter", ParticleMesh.Type.Triangle, 30);
@ -125,16 +112,26 @@ public class TestTransparentShadow extends SimpleApplication {
fire.setLocalTranslation(1.0f, 0, 1.0f);
fire.setLocalScale(0.3f);
fire.setQueueBucket(Bucket.Translucent);
rootNode.attachChild(fire);
// rootNode.attachChild(fire);
Material mat2 = assetManager.loadMaterial("Common/Materials/RedColor.j3m");
Geometry ball = new Geometry("sphere", new Sphere(16, 16, 0.5f));
ball.setMaterial(mat2);
ball.setShadowMode(ShadowMode.CastAndReceive);
rootNode.attachChild(ball);
ball.setLocalTranslation(-1.0f, 1.5f, 1.0f);
PssmShadowRenderer pssmRenderer = new PssmShadowRenderer(assetManager, 1024, 1);
pssmRenderer.setDirection(new Vector3f(0.01f, -1f, 0.01f).normalizeLocal());
pssmRenderer.setDirection(dl1.getDirection());
pssmRenderer.setLambda(0.55f);
pssmRenderer.setShadowIntensity(0.6f);
pssmRenderer.setShadowIntensity(0.8f);
pssmRenderer.setCompareMode(CompareMode.Software);
pssmRenderer.setFilterMode(FilterMode.PCF4);
pssmRenderer.displayDebug();
//pssmRenderer.displayDebug();
viewPort.addProcessor(pssmRenderer);
}
}

Loading…
Cancel
Save