Shadow Mapping

http://wiki.vrmedia.it/index.php?title=Shadow_Mapping



anyone want to implement that into a RenderPass ? doesn't look too hard. I tried my heart out, but my knowledge of opengl just is great enough.



heres what i coded up that doesn't work at all:


import com.jme.image.Texture;
import com.jme.light.PointLight;
import com.jme.math.Matrix4f;
import com.jme.math.Vector3f;
import com.jme.renderer.AbstractCamera;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.TextureRenderer;
import com.jme.renderer.pass.Pass;
import com.jme.scene.Node;
import com.jme.scene.SceneElement;
import com.jme.scene.Spatial;
import com.jme.scene.batch.TriangleBatch;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.GLSLShaderObjectsState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.RenderState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;

public class ShadowMapPass extends Pass {
    private static final long serialVersionUID = 1L;

   
    private TextureRenderer tRenderer;
   private Texture mainTexture;

    private Quad fullScreenQuad;
   private TriangleBatch fullScreenQuadBatch;
   Camera cam;
   PointLight light;
   Vector3f lightDirection;

   private GLSLShaderObjectsState shadowShader;

   private boolean supported = true;
    private boolean useCurrentScene = false;
    Matrix4f lightProjection = new Matrix4f();
    Matrix4f lightModelView = new Matrix4f();

   public void cleanup() {
        super.cleanUp();
        if (tRenderer != null)
            tRenderer.cleanup();
   }

   public boolean isSupported() {
      return supported;
   }
   public ShadowMapPass(Camera cam, PointLight light, Vector3f direction) {
      DisplaySystem display = DisplaySystem.getDisplaySystem();

      //Create texture renderers and rendertextures(alternating between two not to overwrite pbuffers)
        tRenderer = display.createTextureRenderer(
                display.getWidth(),
                display.getHeight(),
                TextureRenderer.RENDER_TEXTURE_2D);
        tRenderer.setBackgroundColor(new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f));
        tRenderer.setCamera(cam);
        this.cam=cam;
        this.light=light;
        lightDirection = direction;
      mainTexture = new Texture();
      mainTexture.setWrap(Texture.WM_CLAMP_S_CLAMP_T);
      mainTexture.setFilter(Texture.FM_LINEAR);
        tRenderer.setupTexture(mainTexture);

      shadowShader = display.getRenderer().createGLSLShaderObjectsState();
      if(!shadowShader.isSupported()) {
         supported = false;
      } else {
         shadowShader.load(ShadowMapPass.class.getClassLoader().getResource("shadow.vert"),
               ShadowMapPass.class.getClassLoader().getResource("shadow.frag"));
         shadowShader.setEnabled(true);
      }

      //Create fullscreen quad
      fullScreenQuad = new Quad("FullScreenQuad", display.getWidth()/4, display.getHeight()/4);
        fullScreenQuadBatch = fullScreenQuad.getBatch(0);
      fullScreenQuad.getLocalRotation().set(0, 0, 0, 1);
      fullScreenQuad.getLocalTranslation().set(display.getWidth() / 2, display.getHeight() / 2, 0);
      fullScreenQuad.getLocalScale().set(1, 1, 1);
      fullScreenQuad.setRenderQueueMode(Renderer.QUEUE_ORTHO);

      fullScreenQuad.setCullMode(SceneElement.CULL_NEVER);
      fullScreenQuad.setTextureCombineMode(TextureState.REPLACE);
      fullScreenQuad.setLightCombineMode(LightState.OFF);
       
      TextureState ts = display.getRenderer().createTextureState();
      ts.setEnabled(true);
        fullScreenQuadBatch.setRenderState(ts);

      AlphaState as = display.getRenderer().createAlphaState();
      as.setBlendEnabled(true);
      as.setSrcFunction(AlphaState.SB_ONE);
      as.setDstFunction(AlphaState.DB_ONE);
      as.setEnabled(true);
        fullScreenQuadBatch.setRenderState(as);

        fullScreenQuad.updateRenderState();
        fullScreenQuad.updateGeometricState(0.0f, true);
   }

    /**
     * Helper class to get all spatials rendered in one TextureRenderer.render() call.
     */
   public void doRender(Renderer r) {
       
        Vector3f camOrigPos =cam.getLocation();
        Vector3f camOrigDir =cam.getDirection();
        cam.setLocation(light.getLocation());
        cam.setDirection(lightDirection);
        lightProjection.set( ((AbstractCamera) cam).getProjectionMatrix()  );
        lightModelView.set( ((AbstractCamera) cam).getModelViewMatrix()  );
        cam.setLocation(camOrigPos);
        cam.setDirection(camOrigDir);
       
            TextureState ts = (TextureState) fullScreenQuadBatch.states[RenderState.RS_TEXTURE];
           
          //Extract intensity
          shadowShader.clearUniforms();
          shadowShader.setUniform("shadowMap", 0);
   
          ts.setTexture(mainTexture, 0);
            fullScreenQuadBatch.states[RenderState.RS_GLSL_SHADER_OBJECTS] = shadowShader;
            tRenderer.render(fullScreenQuad,mainTexture);
   
   }

}

Doesn't jME already have Shadow's implemented?



com.jme.renderer.pass.ShadowedRenderPass



or are you trying to do it your own way?


the shadowrenderpass implements stencil shadows. shadowmapping is a different technique which uses depth textures and opengl shadow tests to produce the shadows…actually the projected texture stuff does things in the same direction(TestProjectedTexture etc)

This be more performant wouldn't it? Well, what little knowledge I have in this area leads me to beleive it would be (very little :|).

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;

import com.jme.image.Texture;
import com.jme.light.PointLight;
import com.jme.math.Matrix4f;
import com.jme.math.Vector3f;
import com.jme.renderer.AbstractCamera;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.TextureRenderer;
import com.jme.renderer.pass.Pass;
import com.jme.scene.SceneElement;
import com.jme.scene.batch.TriangleBatch;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.GLSLShaderObjectsState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jmex.effects.ProjectedTextureUtil;

public class ShadowMapPass extends Pass {
    private static final long serialVersionUID = 1L;

   
    private TextureRenderer tRenderer;
   private Texture mainTexture;

    private Quad fullScreenQuad;
   private TriangleBatch fullScreenQuadBatch;
   Camera cam;
   PointLight light;
   Vector3f lightDirection;
   DisplaySystem display;
   private GLSLShaderObjectsState shadowShader;

   private boolean supported = true;
    private boolean useCurrentScene = false;
    Matrix4f lightProjection = new Matrix4f();
    Matrix4f lightModelView = new Matrix4f();

   public void cleanup() {
        super.cleanUp();
        if (tRenderer != null)
            tRenderer.cleanup();
   }

   public boolean isSupported() {
      return supported;
   }
   public ShadowMapPass(Camera cam, PointLight light, Vector3f direction) {
      display = DisplaySystem.getDisplaySystem();
      
      //Create texture renderers and rendertextures(alternating between two not to overwrite pbuffers)
        tRenderer = display.createTextureRenderer(
                display.getWidth(),
                display.getHeight(),
                TextureRenderer.RENDER_TEXTURE_2D);
        tRenderer.setBackgroundColor(new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f));
        tRenderer.setCamera(cam);
        this.cam=cam;
        this.light=light;
        lightDirection = direction;
      mainTexture = new Texture();
      mainTexture.setWrap(Texture.WM_CLAMP_S_CLAMP_T);
      mainTexture.setFilter(Texture.FM_LINEAR);
        tRenderer.setupTexture(mainTexture);

      shadowShader = display.getRenderer().createGLSLShaderObjectsState();
      if(!shadowShader.isSupported()) {
         
         supported = false;
      } else {
         shadowShader.load(ShadowMapPass.class.getClassLoader().getResource("shadowmap.vert"),
               ShadowMapPass.class.getClassLoader().getResource("shadowmap.frag"));
         shadowShader.setEnabled(true);
      }

      //Create fullscreen quad
      fullScreenQuad = new Quad("FullScreenQuad", display.getWidth(), display.getHeight());
      fullScreenQuad.getLocalRotation().set(0, 0, 0, 1);
      fullScreenQuad.getLocalTranslation().set(display.getWidth() / 2, display.getHeight() / 2, 0);
      fullScreenQuad.getLocalScale().set(0.1f, 0.1f, 0.1f);
      fullScreenQuad.setRenderQueueMode(Renderer.QUEUE_ORTHO);

      fullScreenQuad.setCullMode(SceneElement.CULL_NEVER);
      fullScreenQuad.setTextureCombineMode(TextureState.REPLACE);
      fullScreenQuad.setLightCombineMode(LightState.OFF);

      TextureState ts = display.getRenderer().createTextureState();
      ts.setEnabled(true);
      fullScreenQuad.setRenderState(ts);

      fullScreenQuad.updateRenderState();
      fullScreenQuad.updateGeometricState(0.0f, true);

        fullScreenQuad.updateRenderState();
        fullScreenQuad.updateGeometricState(0.0f, true);
        ProjectedTextureUtil.setupProjectedTexture( mainTexture, Texture.WM_BCLAMP_S_BCLAMP_T, Texture.ACF_ADD );
   }

    /**
     * Helper class to get all spatials rendered in one TextureRenderer.render() call.
     */
   public void doRender(Renderer r) {
        /*Vector3f camOrigPos =cam.getLocation();
        Vector3f camOrigDir =cam.getDirection();
        cam.setLocation(light.getLocation());
        cam.setDirection(lightDirection);
        lightProjection.set( ((AbstractCamera) cam).getProjectionMatrix()  );
        lightModelView.set( ((AbstractCamera) cam).getModelViewMatrix()  );

      GL11.glMatrixMode(GL11.GL_TEXTURE);
      GL11.glLoadIdentity();  // clear things up
      GL11.glTranslatef(0.5f, 0.5f, 0.5f); // we have to clamp values in the [0.0, 1.0] range, not [-1.0, 1.0]
      GL11.glScalef(0.5f, 0.5f, 0.5f);
      GL11.glMultMatrix(lightProjection.toFloatBuffer()); // now multiply by the matrices we have retrieved before
      GL11.glMultMatrix(lightProjection.toFloatBuffer());*/
      
//       finally, multiply by the *inverse* of the *current* modelview matrix
      //Matrix4f modelView = ( ((AbstractCamera) cam).getModelViewMatrix() );
      //modelView.invert();
      //if (! InvertMatrix(&s_mat_m))
         //Quit("Singular view matrix!"); // this should not happen
      
      //GL11.glMultMatrix(modelView.toFloatBuffer());
      //GL11.glMatrixMode(GL11.GL_MODELVIEW);
        Vector3f camOrigPos =cam.getLocation();
        Vector3f camOrigDir =cam.getDirection();
        cam.setLocation(light.getLocation());
        cam.setDirection(lightDirection);
        lightProjection.set( ((AbstractCamera) cam).getProjectionMatrix()  );
        lightModelView.set( ((AbstractCamera) cam).getModelViewMatrix()  );
        cam.setLocation(camOrigPos);
        cam.setDirection(camOrigDir);
        Matrix4f texMatrix = new Matrix4f();
        texMatrix.loadIdentity();
        texMatrix.mult(lightProjection);
        texMatrix.mult(lightModelView);
       
        Matrix4f currentModelView = new Matrix4f();
        currentModelView.set( ((AbstractCamera) cam).getModelViewMatrix()  );
        currentModelView.invertLocal();
        texMatrix.mult(currentModelView);
            TextureState ts = display.getRenderer().createTextureState();
           
          //Extract intensity
          shadowShader.clearUniforms();
          shadowShader.setUniform("shadowMap", mainTexture.getTextureId());
          shadowShader.apply();
          ts.setTexture(mainTexture, mainTexture.getTextureId());
          mainTexture.setMatrix(texMatrix);
          fullScreenQuad.setRenderState(shadowShader);
          fullScreenQuad.setRenderState(ts);
          r.draw(fullScreenQuad);
          ProjectedTextureUtil.updateProjectedTexture( mainTexture, 180.0f, 1.0f, 1.0f, 1500.0f, new Vector3f(0,0,0), new Vector3f(0,-1,0), Vector3f.UNIT_Y );

          
   }

}



Heres my current code. Still doesn't work. the texture outputted is just completely white :(

shadowmapping can be done without shaders, which i think is a better alternative

i don't know how besides this way. :confused:

Alright. i stopped trying to do it with shaders. Anyway, im trying to get the depth buffer into a texture… I use this code:

      Texture mainTexture = new Texture();
            
       GL11.glBindTexture (GL11.GL_TEXTURE_2D, 0);
       GL11.glTexImage2D (GL11.GL_TEXTURE_2D, 0, GL11.GL_DEPTH_COMPONENT, 512, 512, 0,GL11.GL_DEPTH_COMPONENT, GL11.GL_UNSIGNED_BYTE,(IntBuffer)null);
       GL11.glTexParameteri (GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
       TextureState ts = r.createTextureState();
       ts.setEnabled(true);
       ts.setTexture(mainTexture, 0);
       fullScreenQuad.setRenderState(ts);
       fullScreenQuad.updateRenderState();
       r.draw(fullScreenQuad);



but everything comes up white. Do i need to call something before grabbing the depth buffer?

I was doing somereading about shadow mapping and came across this:

Rendering Fake Soft Shadows with Smoothies



The video’s look great and the paper and source code are all provided. If someone is wanting to go through the trouble of implementing shadow mapping, I think this would be nice one to do.

well i made progress, managed to get the depth buffer :stuck_out_tongue: