[WIP] BasicShadow Thingy! Added test app for visually adjusting settings

I have finally pooled together all of my hacks into one shadow renderer! (kidding) This ended up looking worlds better than I thought it would and it is fast, fast, fast! I have it running in my game now and I am THRILLED with the performance and look.



EDIT: I’m going to add a test project which allows you to adjust setting while the app is running. I just need to add an onscreen report of the variable settings and then I’ll post it.



Here are the screens:







Complex model self-shadowing:







Usage:

[java]// heh… BS… assetManager, textureSize, shadowStrength, noiseBias, edgeSize

BasicShadow bs = new BasicShadow(assetManager, 1024, 0.25f, 0.0025f, 7.0f);

bs.setDirection(new Vector3f(-1f, -1f, -1f)); // IMPORTANT! Sorry I forgot to add this :slight_smile:

[/java]



Here’s the code:



BasicShadow.java

[java]package mygame;



import com.jme3.asset.AssetManager;

import com.jme3.material.Material;

import com.jme3.math.Vector3f;

import com.jme3.post.SceneProcessor;

import com.jme3.renderer.Camera;

import com.jme3.renderer.RenderManager;

import com.jme3.renderer.Renderer;

import com.jme3.renderer.ViewPort;

import com.jme3.renderer.queue.GeometryList;

import com.jme3.renderer.queue.RenderQueue;

import com.jme3.renderer.queue.RenderQueue.ShadowMode;

import com.jme3.shadow.ShadowUtil;

import com.jme3.texture.FrameBuffer;

import com.jme3.texture.Image.Format;

import com.jme3.texture.Texture2D;

import com.jme3.ui.Picture;



/**

  • BasicShadow uses sub-standard shadow mapping hacks with one map
  • it’s useful to render shadows in… well… everything.

    *
  • @author whoeverwantscredit

    */

    public class BasicShadow implements SceneProcessor {



    private RenderManager renderManager;

    private ViewPort viewPort;

    private FrameBuffer shadowFB;

    private Texture2D shadowMap;

    private Camera shadowCam;

    private Material preshadowMat;

    private Material postshadowMat;

    private Picture dispPic = new Picture("Picture");

    private boolean noOccluders = false;

    private Vector3f[] points = new Vector3f[8];

    private Vector3f direction = new Vector3f();

    private float texSize;

    private float shadowStrength = 0.25f;

    private float noiseBias = 0.0025f;

    private float edgeSize = 5.0f;



    /**
  • Creates a BasicShadow thingy
  • @param manager the asset manager
  • @param size the size of the shadow map (the map is square)
  • @param shadowStrength the shadow el-darkness (0.0 to 1.0)
  • @param noiseBias for fine tuning noise exclusion (0.005 to 0.00015 seem to work well)
  • @param edgeSize size of the smoothed shadow edge (something between 0.01f and 10f)

    */

    public BasicShadow(AssetManager manager, int texSize, float shadowStrength, float noiseBias, float edgeSize) {

    this.texSize = new Float(texSize);

    this.shadowStrength = shadowStrength;

    this.noiseBias = noiseBias;

    this.edgeSize = edgeSize;



    shadowFB = new FrameBuffer(texSize, texSize, 1);

    shadowMap = new Texture2D(texSize, texSize, Format.Depth);

    shadowFB.setDepthTexture(shadowMap);

    shadowCam = new Camera(texSize, texSize);



    preshadowMat = new Material(manager, "MatDefs/BasicShadowPre.j3md");

    postshadowMat = new Material(manager, "MatDefs/BasicShadowPost.j3md");

    postshadowMat.setTexture("ShadowMap", shadowMap);

    postshadowMat.setFloat("TexSize", this.texSize);

    postshadowMat.setFloat("ShadowStrength", shadowStrength);

    postshadowMat.setFloat("NoiseBias", noiseBias);

    postshadowMat.setFloat("EdgeSize", edgeSize);



    dispPic.setTexture(manager, shadowMap, false);



    for (int i = 0; i < points.length; i++) {

    points = new Vector3f();

    }

    }



    public void initialize(RenderManager rm, ViewPort vp) {

    renderManager = rm;

    viewPort = vp;



    reshape(vp, vp.getCamera().getWidth(), vp.getCamera().getHeight());

    }



    public boolean isInitialized() {

    return viewPort != null;

    }



    /**
  • returns the light direction used for this processor
  • @return

    */

    public Vector3f getDirection() {

    return direction;

    }



    /**
  • sets the light direction to use to computs shadows
  • @param direction

    */

    public void setDirection(Vector3f direction) {

    this.direction.set(direction).normalizeLocal();

    }



    /**
  • @return edgeSize

    */

    public float getEdgeSize() {

    return edgeSize;

    }



    /**
  • sets the shadow blur edge size
  • @param edgeSize

    */

    public void setEdgeSize(float edgeSize) {

    this.edgeSize = edgeSize;

    if (postshadowMat != null) {

    postshadowMat.setFloat("EdgeSize", edgeSize);

    }

    }



    /**
  • @return noiseBias

    */

    public float getNoiseBias() {

    return noiseBias;

    }



    /**
  • sets the depth bias for excluding unwanted noise
  • @param noiseBias

    */

    public void setNoiseBias(float noiseBias) {

    this.noiseBias = noiseBias;

    if (postshadowMat != null) {

    postshadowMat.setFloat("NoiseBias", noiseBias);

    }

    }



    /**
  • @return shadowStrength

    */

    public float getShadowStrength() {

    return shadowStrength;

    }



    /**
  • sets how present the shadow appears
  • @param shadowStrength

    */

    public void setShadowStrength(float shadowStrength) {

    this.shadowStrength = shadowStrength;

    if (postshadowMat != null) {

    postshadowMat.setFloat("ShadowStrength", shadowStrength);

    }

    }



    /**
  • debug only
  • @return

    */

    public Vector3f[] getPoints() {

    return points;

    }



    /**
  • debug only
  • returns the shadow camera
  • @return

    /

    public Camera getShadowCamera() {

    return shadowCam;

    }



    /

  • debug functions

    */

    public void incShadowEdge() {

    if (getEdgeSize() < 9f)

    setEdgeSize(getEdgeSize()+1f);

    }

    public void decShadowEdge() {

    if (getEdgeSize() > 1f)

    setEdgeSize(getEdgeSize()-1f);

    }

    public void incShadowStrength() {

    if (getShadowStrength() < 10f)

    setShadowStrength(getShadowStrength()+0.05f);

    }

    public void decShadowStrength() {

    if (getShadowStrength() > 0.05f)

    setShadowStrength(getShadowStrength()-0.05f);

    }

    public void incNoiseBias() {

    if (getNoiseBias() < 0.25f)

    setNoiseBias(getNoiseBias()+0.001f);

    }

    public void decNoiseBias() {

    if (getNoiseBias() > 0.0000005f)

    setNoiseBias(getNoiseBias()-0.001f);

    }

    public void postQueue(RenderQueue rq) {

    GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast);

    if (occluders.size() == 0) {

    noOccluders = true;

    return;

    } else {

    noOccluders = false;

    }



    GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive);



    // update frustum points based on current camera

    Camera viewCam = viewPort.getCamera();

    ShadowUtil.updateFrustumPoints(viewCam,

    viewCam.getFrustumNear(),

    viewCam.getFrustumFar(),

    1.0f,

    points);



    Vector3f frustaCenter = new Vector3f();

    for (Vector3f point : points) {

    frustaCenter.addLocal(point);

    }

    frustaCenter.multLocal(1f / texSize);



    // update light direction

    shadowCam.setProjectionMatrix(null);

    shadowCam.setParallelProjection(true);

    // shadowCam.setFrustumPerspective(45, 1, 1, 20);



    shadowCam.lookAtDirection(direction, Vector3f.UNIT_Y);

    shadowCam.update();

    shadowCam.setLocation(frustaCenter);

    shadowCam.update();

    shadowCam.updateViewProjection();



    // render shadow casters to shadow map

    ShadowUtil.updateShadowCamera(occluders, receivers, shadowCam, points);



    Renderer r = renderManager.getRenderer();

    renderManager.setCamera(shadowCam, false);

    renderManager.setForcedMaterial(preshadowMat);



    r.setFrameBuffer(shadowFB);

    r.clearBuffers(false, true, false);

    viewPort.getQueue().renderShadowQueue(ShadowMode.Cast, renderManager, shadowCam, true);

    r.setFrameBuffer(viewPort.getOutputFrameBuffer());



    renderManager.setForcedMaterial(null);

    renderManager.setCamera(viewCam, false);

    }



    /**
  • debug only
  • @return

    /

    public Picture getDisplayPicture() {

    return dispPic;

    }



    public void postFrame(FrameBuffer out) {

    if (!noOccluders) {

    postshadowMat.setMatrix4(“LightViewProjectionMatrix”, shadowCam.getViewProjectionMatrix());

    renderManager.setForcedMaterial(postshadowMat);

    viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, viewPort.getCamera(), true);

    renderManager.setForcedMaterial(null);

    }

    }



    public void preFrame(float tpf) {

    }



    public void cleanup() {

    }



    public void reshape(ViewPort vp, int w, int h) {

    dispPic.setPosition(w / 20f, h / 20f);

    dispPic.setWidth(w / 5f);

    dispPic.setHeight(h / 5f);

    }

    }

    [/java]



    MatDefs x2…

    BasicShadowPre.j3md

    [java]MaterialDef Pre Shadow {

    Technique {

    VertexShader GLSL100 : Shaders/BasicShadowPre.vert

    FragmentShader GLSL100 : Shaders/BasicShadowPre.frag



    WorldParameters {

    WorldViewProjectionMatrix

    WorldViewMatrix

    }



    RenderState {

    FaceCull Front

    DepthTest On

    DepthWrite On

    PolyOffset 0 0

    ColorWrite Off

    }

    }

    }[/java]



    BasicShadowPost.j3md

    [java]MaterialDef Post Shadow {



    MaterialParameters {

    Texture2D ShadowMap

    Float EdgeSize

    Float TexSize

    Float ShadowStrength : 0.2

    Float NoiseBias : 2.0

    Matrix4 LightViewProjectionMatrix

    }



    Technique {

    VertexShader GLSL100: Shaders/BasicShadowPost.vert

    FragmentShader GLSL100: Shaders/BasicShadowPost.frag



    WorldParameters {

    WorldViewProjectionMatrix

    WorldMatrix

    }



    Defines {

    NO_SHADOW2DPROJ

    }



    RenderState {

    Blend Modulate

    }

    }



    }[/java]



    Shaders x4

    BasicShadowPre.vert

    [java]attribute vec4 inPosition;

    attribute vec2 inTexCoord;



    uniform mat4 g_WorldViewProjectionMatrix;

    uniform mat4 g_WorldViewMatrix;



    varying vec2 texCoord;

    varying vec4 v_position;



    void main(){

    gl_Position = g_WorldViewProjectionMatrix * inPosition;

    texCoord = inTexCoord;

    v_position = gl_Position;

    }[/java]



    BasicShadowPre.frag

    [java]varying vec4 v_position;



    void main() {

    float depth = v_position.z / v_position.w ;

    depth = depth * 0.5 + 0.5;

    gl_FragColor = vec4( depth );

    }[/java]



    BasicShadowPost.vert

    [java]uniform mat4 m_LightViewProjectionMatrix;

    uniform mat4 g_WorldViewProjectionMatrix;

    uniform mat4 g_WorldMatrix;



    varying vec4 projCoord;



    attribute vec3 inPosition;



    const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,

    0.0, 0.5, 0.0, 0.0,

    0.0, 0.0, 0.5, 0.0,

    0.5, 0.5, 0.5, 1.0);



    void main(){

    gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);

    // get the vertex in world space

    vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0);



    vec4 coord = m_LightViewProjectionMatrix * worldPos;

    projCoord = biasMat * coord;

    }[/java]



    BasicShadowPost.frag

    [java]#ifdef NO_SHADOW2DPROJ

    #define SHADOWMAP sampler2D

    #define SHADOWTEX texture2D

    #define SHADCOORD(coord) coord.xy

    #else

    #define SHADOWMAP sampler2DShadow

    #define SHADOWTEX shadow2D

    #define SHADCOORD(coord) vec3(coord.xy,0.0)

    #endif



    uniform float m_EdgeSize;

    uniform float m_TexSize;

    uniform float m_ShadowStrength;

    uniform float m_NoiseBias;

    float pixSize = 1.0 / m_TexSize;

    vec2 pixSize2 = vec2(pixSize);

    uniform SHADOWMAP m_ShadowMap;

    varying vec4 projCoord;



    float random (vec4 seed4) {

    float dot_product = dot(seed4, vec4(12.9898,78.233,45.164,94.673));

    return fract(sin(dot_product) * 43758.5453);

    }



    float Shadow_DoShadowCompareOffset(in SHADOWMAP tex, vec4 projCoord){

    vec4 samp = SHADOWTEX(tex, SHADCOORD(projCoord.xy));

    if (projCoord.z - m_NoiseBias <= samp.r && projCoord.z + m_NoiseBias >= samp.r)

    return 1.0-m_ShadowStrength;

    else if (projCoord.z - m_NoiseBias > samp.r)

    return 1.0-m_ShadowStrength;

    else return 1.0;

    }



    float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){

    float shadow = 0.0;

    float iter = 4.0;

    for (float i = 0.0; i < iter; i += 1.0)

    shadow += Shadow_DoShadowCompareOffset(tex,projCoord + (random(vec4(projCoord
    i))/(1000.0-(m_EdgeSize*100.0))));

    shadow /= iter;

    return shadow;

    }



    float Shadow_GetShadow(in SHADOWMAP tex, in vec4 projCoord){

    return clamp(Shadow_DoDither_2x2(tex, projCoord), 0.0, 1.0);

    }



    void main() {

    vec4 coord = projCoord;

    coord.xyz /= coord.w;

    float shad = Shadow_GetShadow(m_ShadowMap, coord);

    gl_FragColor = vec4(shad,shad,shad,1.0);

    }

    [/java]



    //** Test app **//

    Legend for test app:

    U = inc edge size / J = dec edge size

    I = inc shadow strength / K = dec shadow strength

    O = inc noise bias / L = dec noise bias

    P = toggle material white / textured



    NOTE: You will need to copy the teapot.obj from jme test library to the Models directory under assets. Also, either change the image the texture is using to something you already have in Textures, or grab the one below this and add it to you Textures dir under assets.



    One last note: You may want adjust the inc/dec methods in the BasicShadow.java to increment/decrement by other values then what I set up. They’re easy to find and adjust.



    Main.java (this allows you to adjust the setting visually and prints the variables to the screen)

    [java]package mygame;



    import com.jme3.app.SimpleApplication;

    import com.jme3.font.BitmapFont;

    import com.jme3.font.BitmapFont.Align;

    import com.jme3.font.BitmapText;

    import com.jme3.font.Rectangle;

    import com.jme3.input.KeyInput;

    import com.jme3.input.controls.ActionListener;

    import com.jme3.input.controls.KeyTrigger;

    import com.jme3.light.AmbientLight;

    import com.jme3.material.Material;

    import com.jme3.math.ColorRGBA;

    import com.jme3.math.Quaternion;

    import com.jme3.math.Vector3f;

    import com.jme3.renderer.Camera;

    import com.jme3.renderer.queue.RenderQueue.Bucket;

    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.Spatial.CullHint;

    import com.jme3.scene.debug.WireFrustum;

    import com.jme3.scene.shape.Box;

    import com.jme3.shadow.ShadowUtil;

    import com.jme3.texture.Texture;



    public class Main extends SimpleApplication {



    float angle;

    Spatial lightMdl;

    Spatial teapot;

    Geometry frustumMdl;

    WireFrustum frustum;



    private BasicShadow bsr;

    private Vector3f[] points;



    private boolean toggleMat = false;



    Geometry floorGeom;

    Material mat1, mat2;

    String currentMat = “mat1”;



    String statsStr = “Test”;

    BitmapText statsText;



    {

    points = new Vector3f[8];

    for (int i = 0; i < points.length; i++) points = new Vector3f();

    }



    public static void main(String[] args){

    Main app = new Main();

    app.start();

    }



    @Override

    public void simpleInitApp() {

    // put the camera in a bad position

    cam.setLocation(new Vector3f(0.7804813f, 1.7502685f, -2.1556435f));

    cam.setRotation(new Quaternion(0.1961598f, -0.7213164f, 0.2266092f, 0.6243975f));

    cam.setFrustumFar(10);



    mat1 = assetManager.loadMaterial(“Common/Materials/WhiteColor.j3m”);



    mat2 = new Material(assetManager, “Common/MatDefs/Light/Lighting.j3md”);

    Texture diff = assetManager.loadTexture(“Textures/gatehouse_castle.png”);

    diff.setWrap(Texture.WrapMode.Repeat);

    mat2.setTexture(“DiffuseMap”, diff);



    AmbientLight al = new AmbientLight();

    al.setColor(new ColorRGBA(1.8f, 1.8f, 1.8f, 1.0f));



    rootNode.addLight(al);



    rootNode.setShadowMode(ShadowMode.Off);

    Box floor = new Box(Vector3f.ZERO, 3, 0.1f, 3);

    floorGeom = new Geometry(“Floor”, floor);

    floorGeom.setMaterial(mat1);

    floorGeom.setLocalTranslation(0,-0.2f,0);

    floorGeom.setShadowMode(ShadowMode.Receive);

    rootNode.attachChild(floorGeom);



    teapot = assetManager.loadModel(“Models/Teapot.obj”);

    teapot.setLocalScale(2f);

    teapot.setMaterial(mat1);

    teapot.setShadowMode(ShadowMode.CastAndReceive);

    teapot.setCullHint(Spatial.CullHint.Never);

    rootNode.attachChild(teapot);

    // lightMdl = new Geometry(“Light”, new Sphere(10, 10, 0.1f));

    // lightMdl.setMaterial(mat);

    // // disable shadowing for light representation

    // lightMdl.setShadowMode(ShadowMode.Off);

    // rootNode.attachChild(lightMdl);



    bsr = new BasicShadow(assetManager, 1240, 0.25f, 0.0025f, 7.0f);

    bsr.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());



    viewPort.addProcessor(bsr);



    frustum = new WireFrustum(bsr.getPoints());

    frustumMdl = new Geometry(“f”, frustum);

    frustumMdl.setCullHint(Spatial.CullHint.Never);

    frustumMdl.setShadowMode(ShadowMode.Off);

    frustumMdl.setMaterial(new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”));

    frustumMdl.getMaterial().getAdditionalRenderState().setWireframe(true);

    frustumMdl.getMaterial().setColor(“Color”, ColorRGBA.Red);

    rootNode.attachChild(frustumMdl);



    BitmapFont statsFont = assetManager.loadFont(“Interface/Fonts/Console.fnt”);

    Node stats = new Node();

    stats.setQueueBucket(Bucket.Gui);

    stats.setCullHint(CullHint.Never);



    statsText = new BitmapText(statsFont);

    statsText.setText(statsStr);

    stats.setLocalTranslation(0, this.getGuiViewPort().getCamera().getHeight()-statsText.getLineHeight(), 0);



    stats.attachChild(statsText);

    guiNode.attachChild(stats);





    inputManager.addMapping(“KEY_U”,new KeyTrigger(KeyInput.KEY_U)); // Inc edge

    inputManager.addListener(eventListener, “KEY_U”);

    inputManager.addMapping(“KEY_J”,new KeyTrigger(KeyInput.KEY_J)); // Dec edge

    inputManager.addListener(eventListener, “KEY_J”);

    inputManager.addMapping(“KEY_I”,new KeyTrigger(KeyInput.KEY_I)); // Inc strength

    inputManager.addListener(eventListener, “KEY_I”);

    inputManager.addMapping(“KEY_K”,new KeyTrigger(KeyInput.KEY_K)); // Dec strength

    inputManager.addListener(eventListener, “KEY_K”);

    inputManager.addMapping(“KEY_O”,new KeyTrigger(KeyInput.KEY_O)); // Inc bias

    inputManager.addListener(eventListener, “KEY_O”);

    inputManager.addMapping(“KEY_L”,new KeyTrigger(KeyInput.KEY_L)); // Dec bias

    inputManager.addListener(eventListener, “KEY_L”);

    inputManager.addMapping(“KEY_P”,new KeyTrigger(KeyInput.KEY_P)); // Toggle materials

    inputManager.addListener(eventListener, “KEY_P”);

    }



    private ActionListener eventListener = new ActionListener() {

    public void onAction(String binding, boolean value, float tpf) {

    if (binding.equals(“KEY_U”) && value) {

    bsr.incShadowEdge();

    }

    if (binding.equals(“KEY_J”) && value) {

    bsr.decShadowEdge();

    }

    if (binding.equals(“KEY_I”) && value) {

    bsr.incShadowStrength();

    }

    if (binding.equals(“KEY_K”) && value) {

    bsr.decShadowStrength();

    }

    if (binding.equals(“KEY_O”) && value) {

    bsr.incNoiseBias();

    }

    if (binding.equals(“KEY_L”) && value) {

    bsr.decNoiseBias();

    }

    if (binding.equals(“KEY_P”) && value) {

    toggleMat = true;

    }

    }

    };



    @Override

    public void simpleUpdate(float tpf){

    Camera shadowCam = bsr.getShadowCamera();

    ShadowUtil.updateFrustumPoints2(shadowCam, points);



    frustum.update(points);

    // rotate teapot around Y axis

    teapot.rotate(0, tpf * 0.25f, 0);



    if (toggleMat) {

    if (currentMat.equals(“mat1”)) {

    teapot.setMaterial(mat2);

    floorGeom.setMaterial(mat2);

    currentMat = “mat2”;

    } else {

    teapot.setMaterial(mat1);

    floorGeom.setMaterial(mat1);

    currentMat = “mat1”;

    }

    toggleMat = false;

    }



    // Update stats

    statsStr = "Shadow Edge Size: " + String.valueOf(bsr.getEdgeSize()) + “n”;

    statsStr += "Shadow Strength: " + String.valueOf(bsr.getShadowStrength()) + “n”;

    statsStr += "Noise Bias: " + String.valueOf(bsr.getNoiseBias());

    statsText.setText(statsStr);

    }

    }[/java]



    Test texture:

4 Likes

awesome, looks gr8 tho is it only directional?

+1 XD

@Setekh said:
awesome, looks gr8 tho is it only directional?
+1 XD


Yeah... I'm still trying to finish the OMNI shadow thingy. I'm stuck at the point where I need to project the cube map from the lights position. /cry
1 Like

Do you really have a 5120x5120 shadow map? or that number is for something else?

2 Likes
@nehon said:
Do you really have a 5120x5120 shadow map? or that number is for something else?


Not on the teapot... I just selected random crap for the usage. the teapot is 512x512 (no zero). Oh.... ooops... thanks for the catch... I thought it said 512... it says 5120... I'll change that in the post. I was truly confused why you picked that number.

Tough crowd around here >.< I thought you might actually say something nice about it :)
1 Like

Eh… I lied. The teapot was at 1024. My bad.

The screens are really good.

But don’t you have aliasing issues in you game with only one shadow map?

@nehon said:
The screens are really good.
But don't you have aliasing issues in you game with only one shadow map?


I'll grab some screens... If there is, it is minimal at best.

Actually... by aliasing... do you mean flickering? noise? or?

Omg… where is my brain at… yeah… there is some. I haven’t spent any time playing with the bias and edge size though… which helps hide most of what you might see otherwise.

@nehon Here are a few screen caps in game. I still need to adjust the setting for the model size, etc… but the aliasing isn’t easy to spot.







@t0neg0d said:
Actually... by aliasing... do you mean flickering? noise? or?

I mean that you can see the shadow map pixels on the border of the projected shadow despite of the dithering
http://www.gemaga.com/wp-content/uploads/2007/12/anti-aliasing.thumbnail.png

That looks good. The shadows are really bright but it gives a nice effect.

What size is you shadow map on theses screens?

Those are 1024… lemme grab the settings from the code…

[java]BasicShadow bsr = new BasicShadow(assetManager, 1024, 0.15f, 0.0025f, 6.0f);

bsr.setDirection(new Vector3f(.6f, -1f, .6f));

if (FILTER_SHADOW)

viewPort.addProcessor(bsr);[/java]

t0neg0d:



as usually i tested your great work(only tested), but i got a problem. it seem to be bug. or i just forgot about something…



could you tell me why this platform box is not receiving shadow? i have ShadowMode.Receive. it is copy from jme tests.



http://i.imgur.com/Hldd0.png



[java]package oxplay.test;



import com.jme3.app.SimpleApplication;

import com.jme3.material.Material;

import com.jme3.math.Quaternion;

import com.jme3.math.Vector3f;

import com.jme3.renderer.queue.RenderQueue.ShadowMode;

import com.jme3.scene.Geometry;

import com.jme3.scene.Spatial;

import com.jme3.scene.shape.Box;

import oxplay.app.world.filters.BasicShadow;



public class TestShadow extends SimpleApplication {



float angle;

Spatial lightMdl;

Spatial teapot;

Geometry frustumMdl;



public static void main(String[] args) {

TestShadow app = new TestShadow();

app.start();

}



@Override

public void simpleInitApp() {

cam.setLocation(new Vector3f(0.7804813f, 1.7502685f, -2.1556435f));

cam.setRotation(new Quaternion(0.1961598f, -0.7213164f, 0.2266092f, 0.6243975f));

cam.setFrustumFar(10);

Material mat = assetManager.loadMaterial(“Common/Materials/WhiteColor.j3m”);

rootNode.setShadowMode(ShadowMode.Off);

Box floor = new Box(Vector3f.ZERO, 3, 0.1f, 3);

Geometry floorGeom = new Geometry(“Floor”, floor);

floorGeom.setMaterial(mat);

floorGeom.setLocalTranslation(0, -0.2f, 0);

floorGeom.setShadowMode(ShadowMode.Receive);

rootNode.attachChild(floorGeom);

teapot = assetManager.loadModel(“Models/Teapot.obj”);

teapot.setLocalScale(2f);

teapot.setMaterial(mat);

teapot.setShadowMode(ShadowMode.CastAndReceive);

rootNode.attachChild(teapot);

BasicShadow bs = new BasicShadow(assetManager, 1024, 0.25f, 0.0025f, 7.0f);

viewPort.addProcessor(bs);

bs.setShadowStrength(0.3f);

}



@Override

public void simpleUpdate(float tpf) {

teapot.rotate(0, tpf * 0.25f, 0);

}

}

[/java]

The light direction seems to be coming from downward.

You probably have to set a correct direction.

oh my god… im blind, thanks.

@oxplay2

Actually… I noticed a problem before I went to bed. It’s in the BasicShadowPost.frag. I’ll post an update once I fix it.



Guess I should say what it is… it’s a problem in the eval of the self-shadow vs cast shadow (I pieced together multiple takes on the BSR and added a new dithering technique… I should have used cut/paste lol) and it is causing flickering in certain circumstances that shouldn’t have ever been there.

@nehon said:
The light direction seems to be coming from downward.
You probably have to set a correct direction.


Lol... thank you again! I forgot to add setting the light direction to the usage.

Added test app for visually adjusting the shadow settings. Legend and Main.java posted to OP

I gave it a spin but on my Linux + GeForce GT520 I get


com.jme3.renderer.RendererException: Shader link failure, shader:Shader[numSources=2, numUniforms=2, shaderSources=[ShaderSource[name=Shaders/BasicShadowPre.vert, defines, type=Vertex, language=GLSL100], ShaderSource[name=Shaders/BasicShadowPre.frag, defines, type=Fragment, language=GLSL100]]] info:Vertex info
0(6) : error C5052: gl_FragColor is not accessible in this profile


I'm totally lost when it comes to shaders so I don't even know how to start fixing this :)