|
kevglass
Guest
|
 |
« on: August 22, 2008, 03:50:56 am » |
|
I've tried to make the work I've done for the sci game a little more generic to build a shadow mapping pass for JME:  It's not perfect by any means yet, a few artifacts hanging around in rendering and some tuning to do make it work in any given scene (you need to tune the shadow map to fit over your viewable area). It only works for directional lights (or rather a direction) and it's a bit of hack how the shadows are applied since I don't seem to be able to understand the last bit of the rendering process. There's also some comments in there where bits of GL functionality need to be moved into JME core (if they're not already there). Anyway, I've borrowed the shadow test for stencil shadows. The shadow mapping pass is essentially a drop in replacement for the old version albeit it only deals with one light source (I guess you could add multiple shadow mapping passes, one for each light, not tried). Here's the pass: package com.jme.renderer.pass;
import java.util.ArrayList;
import org.lwjgl.opengl.ARBDepthTexture; import org.lwjgl.opengl.ARBShadow; import org.lwjgl.opengl.GL11;
import com.jme.image.Texture; import com.jme.image.Texture2D; import com.jme.light.DirectionalLight; import com.jme.math.Matrix4f; import com.jme.math.Vector3f; import com.jme.renderer.AbstractCamera; import com.jme.renderer.ColorRGBA; import com.jme.renderer.Renderer; import com.jme.renderer.TextureRenderer; import com.jme.scene.Spatial; import com.jme.scene.state.BlendState; import com.jme.scene.state.ClipState; import com.jme.scene.state.ColorMaskState; import com.jme.scene.state.CullState; import com.jme.scene.state.LightState; import com.jme.scene.state.MaterialState; import com.jme.scene.state.RenderState; import com.jme.scene.state.TextureState; import com.jme.system.DisplaySystem; import com.jmex.effects.ProjectedTextureUtil;
/** * A pass providing a shadow mapping layer across the top of an existing scene. * * Based on code by Robert Larsson and Joshua Slack * @author kevglass */ public class DirectionalShadowMapPass extends Pass { /** Bias matrix borrowed from the projected texture utility */ private static Matrix4f biasMatrix = new Matrix4f(0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f); // bias from [-1, 1] to [0, 1] /** The renderer used to produce the shadow map */ private TextureRenderer shadowMapRenderer; /** The texture storing the shadow map */ private Texture2D shadowMapTexture; /** The near plane when rendering the shadow map */ private float nearPlane = 1f; /** The far plane when rendering the shadow map - currently tuned for the test*/ private float farPlane = 3000.0f; /** The location the shadow light source is looking at - must point at the focus of the scene */ private Vector3f shadowCameraLookAt; /** The effective location of the light source - derived based on the distance of casting, look at and direction */ private Vector3f shadowCameraLocation; /** The list of occluding nodes */ private ArrayList<Spatial> occluderNodes = new ArrayList<Spatial>(); /** Culling front faces when rendering shadow maps */ private CullState cullFrontFace; /** Turn off textures when rendering shadow maps */ private TextureState noTexture; /** Turn off colours when rendering shadow maps - depth only */ private ColorMaskState colorDisabled; /** Turn off lighting when rendering shadow maps - depth only */ private LightState noLights; /** The blending to both discard the fragements that have been determined to be free of shadows and to blend into the background scene */ private BlendState discardShadowFragments; /** The state applying the shadow map */ private TextureState shadowTextureState; /** The bright light used to blend the shadows version into the scene */ private LightState brightLights; /** The dark material used to blend the shadows into the scene */ private MaterialState darkMaterial; /** Don't perform any plane clipping when rendering the shadowed scene */ private ClipState noClip; /** True once the pass has been initialised */ protected boolean initialised = false; /** The direction shadows are being cast from - directional light? */ protected Vector3f direction; /** The size of the shadow map texture */ private int shadowMapSize; /** * The scaling applied to the shadow map when rendered to - lower number means * higher res but less ara covered by the shadow map */ protected float shadowMapScale = 0.4f; /** * The distance we're modelling the direction light source as being away from the focal point, again * the higher the number the more of the scene is covered but at lower resolution */ protected float dis = 500; /** A place to internally save previous enforced states setup before rendering this pass */ private RenderState[] preStates = new RenderState[RenderState.RS_MAX_STATE]; /** The colour of shadows cast */ private ColorRGBA shadowCol = new ColorRGBA(0,0,0,0.3f); /** * Create a shadow map pass casting shadows from a light with the direction * given. * * @param direction The direction of the light casting the shadows */ public DirectionalShadowMapPass(Vector3f direction) { this(direction, 2048); }
/** * Create a shadow map pass casting shadows from a light with the direction * given. * * @param shadowMapSize The size of the shadow map texture * @param direction The direction of the light casting the shadows */ public DirectionalShadowMapPass(Vector3f direction, int shadowMapSize) { this.shadowMapSize = shadowMapSize; this.direction = direction; setViewTarget(new Vector3f(0,0,0)); } /** * Set the colour of the shadows to be cast * * @param col The colour of the shadows to be cast */ public void setShadowAlpha(float alpha) { shadowCol.a = alpha; if (darkMaterial != null) { darkMaterial.setDiffuse(shadowCol); } } /** * Set the distance of the camera representing the directional light. The further * away the more of the scene will be shadowed but at a lower resolution * * @param dis The distance to be used for the shadow map camera (default = 500) */ public void setViewDistance(float dis) { this.dis = dis; } /** * Set the scale factor thats used to stretch the shadow map * texture across the scene. * * Higher the number the more of the scene will be convered but at * a lower resolution. * * @param scale The scale used to stretch the shadow map across the scene. */ public void setShadowMapScale(float scale) { shadowMapScale = scale; } /** * Set the target of the view. This will be where the camera points * when generating the shadow map and should be the centre of the scene * * @param target The target of the view */ public void setViewTarget(Vector3f target) { if (target.equals(shadowCameraLookAt)) { return; } shadowCameraLookAt = new Vector3f(target); Vector3f temp = new Vector3f(direction); temp.normalizeLocal(); temp.multLocal(-dis); shadowCameraLocation = new Vector3f(target); shadowCameraLocation.addLocal(temp);
if (shadowMapRenderer != null) { updateShadowCamera(); } } /** * saves any states enforced by the user for replacement at the end of the * pass. */ protected void saveEnforcedStates() { for (int x = RenderState.RS_MAX_STATE; --x >= 0;) { preStates[x] = context.enforcedStateList[x]; } }
/** * replaces any states enforced by the user at the end of the pass. */ protected void replaceEnforcedStates() { for (int x = RenderState.RS_MAX_STATE; --x >= 0;) { context.enforcedStateList[x] = preStates[x]; } }
/** * Add a spatial that will occlude light and hence cast a shadow * * @param occluder The spatial to add as an occluder */ public void addOccluder(Spatial occluder) { occluderNodes.add(occluder); } /** * Initialise the pass render states */ public void init(Renderer r) { if (initialised) { return; }
initialised = true; // now it's initialised
// the texture that the shadow map will be rendered into. Modulated so // that it can be blended over the scene. shadowMapTexture = new Texture2D(); shadowMapTexture.setApply(Texture.ApplyMode.Modulate); shadowMapTexture.setMinificationFilter(Texture.MinificationFilter.NearestNeighborNoMipMaps); shadowMapTexture.setWrap(Texture.WrapMode.Clamp); shadowMapTexture.setMagnificationFilter(Texture.MagnificationFilter.Bilinear); shadowMapTexture.setRenderToTextureType(Texture.RenderToTextureType.Depth); shadowMapTexture.setMatrix(new Matrix4f()); shadowMapTexture.setEnvironmentalMapMode(Texture.EnvironmentalMapMode.EyeLinear); // configure the texture renderer to output to the texture shadowMapRenderer = DisplaySystem.getDisplaySystem().createTextureRenderer(shadowMapSize, shadowMapSize,TextureRenderer.Target.Texture2D); shadowMapRenderer.setupTexture(shadowMapTexture);
// render state to apply the shadow map texture shadowTextureState = r.createTextureState(); shadowTextureState.setTexture(shadowMapTexture, 0); noClip = r.createClipState(); noClip.setEnabled(false); // render states to use when rendering into the shadmop, no textures or colours // are required since we're only interested in recording depth // Also only need back faces when rendering the shadow maps noTexture = r.createTextureState(); noTexture.setEnabled(false); colorDisabled = r.createColorMaskState(); colorDisabled.setAll(false); cullFrontFace = r.createCullState(); cullFrontFace.setEnabled(true); cullFrontFace.setCullFace(CullState.Face.Front); noLights = r.createLightState(); noLights.setEnabled(false); // Then rendering and comparing the shadow map with the current // depth the result will be set to alpha 1 if not in shadow and // to 0 if it's is in shadow. However, we're going to blend it into the scene // so the alpha will be zero if there is no shadow at this location but // > 0 on shadows. discardShadowFragments = r.createBlendState(); discardShadowFragments.setEnabled(true); discardShadowFragments.setBlendEnabled(true); discardShadowFragments.setSourceFunction(BlendState.SourceFunction.SourceAlpha); discardShadowFragments.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha); discardShadowFragments.setTestEnabled(true); discardShadowFragments.setReference(0f); discardShadowFragments.setTestFunction(BlendState.TestFunction.GreaterThan); // light used to uniformly light the scene when rendering the shadows themselfs // this is so the geometry colour can be used as the source for blending - i.e. // transparent shadows rather than matte black brightLights = r.createLightState(); brightLights.setEnabled(true); DirectionalLight light = new DirectionalLight(); light.setDiffuse(new ColorRGBA(1, 1, 1, 1f)); light.setEnabled(true); brightLights.attach(light); darkMaterial = r.createMaterialState(); darkMaterial.setEnabled(true); darkMaterial.setDiffuse(shadowCol); darkMaterial.setAmbient(new ColorRGBA(0,0,0,0f)); darkMaterial.setShininess(0); darkMaterial.setSpecular(new ColorRGBA(0,0,0,0)); darkMaterial.setEmissive(new ColorRGBA(0,0,0,0)); darkMaterial.setMaterialFace(MaterialState.MaterialFace.Front); updateShadowCamera(); } /** * @see com.jme.renderer.pass.Pass#doRender(com.jme.renderer.Renderer) */ public void doRender(Renderer r) { if (occluderNodes.size() == 0) { return; } init(r); updateShadowMap(r); renderShadowedScene(r); } /** * Render the scene with shadows * * @param r The renderer to use */ protected void renderShadowedScene(Renderer r) { saveEnforcedStates(); context.enforceState(shadowTextureState); context.enforceState(discardShadowFragments); context.enforceState(brightLights); context.enforceState(darkMaterial); // compare the shadowmap depth wich the current fragment depth // this needs to be moved into JME texture class, not sure where yet? GL11.glTexParameteri(GL11.GL_TEXTURE_2D, ARBShadow.GL_TEXTURE_COMPARE_MODE_ARB, ARBShadow.GL_COMPARE_R_TO_TEXTURE_ARB); GL11.glTexParameteri(GL11.GL_TEXTURE_2D, ARBShadow.GL_TEXTURE_COMPARE_FUNC_ARB, GL11.GL_GEQUAL); GL11.glTexParameteri(GL11.GL_TEXTURE_2D, ARBDepthTexture.GL_DEPTH_TEXTURE_MODE_ARB, GL11.GL_INTENSITY); // draw the scene, only the shadowed bits will be drawn and blended // with the shadow coloured geometry r.setPolygonOffset(0, -5); for (Spatial spat : spatials) { spat.onDraw(r); } r.renderQueue(); r.clearPolygonOffset(); replaceEnforcedStates(); }
/** * Update the shadow map * * @param r The renderer to being use to display this map */ protected void updateShadowMap(Renderer r) { saveEnforcedStates(); context.enforceState(noClip); context.enforceState(noTexture); context.enforceState(colorDisabled); context.enforceState(cullFrontFace); context.enforceState(noLights); r.setPolygonOffset(0, 5); shadowMapRenderer.render(occluderNodes.get(0), shadowMapTexture, true); for (int i=1;i<occluderNodes.size();i++) { shadowMapRenderer.render(occluderNodes.get(i), shadowMapTexture, false); } r.clearPolygonOffset(); replaceEnforcedStates(); } /** * Update the direction from which the shadows are cast */ protected void updateShadowCamera() { // render the shadow map, use the texture renderer to render anything // thats been added as occluder float scale = shadowMapSize * shadowMapScale;
shadowMapRenderer.getCamera().setLocation(shadowCameraLocation); shadowMapRenderer.getCamera().setFrustum(nearPlane, farPlane, -scale, scale, -scale, scale); shadowMapRenderer.getCamera().lookAt(shadowCameraLookAt, Vector3f.UNIT_Y.clone()); shadowMapRenderer.getCamera().setParallelProjection(true); shadowMapRenderer.getCamera().update();
Matrix4f proj = new Matrix4f(); Matrix4f view = new Matrix4f(); proj.set(((AbstractCamera) shadowMapRenderer.getCamera()).getProjectionMatrix()); view.set(((AbstractCamera) shadowMapRenderer.getCamera()).getModelViewMatrix()); shadowMapTexture.getMatrix().set(view.multLocal(proj).multLocal(biasMatrix)).transposeLocal(); } /** * @see com.jme.renderer.pass.Pass#cleanUp() */ public void cleanUp() { super.cleanUp(); if (shadowMapRenderer != null) { shadowMapRenderer.cleanup(); } } /** * Remove the contents of the pass */ public void clear() { occluderNodes.clear(); spatials.clear(); } }
There's no checking to see if you're got the extension for shadow mapping, so if your card hasn't it'll just blow up at the moment. I'm also sure there are ways to implement this with shaders in much smarter ways. Anyone can help me work out the last bits and pieces? Kev PS. Test included on the next post, broke the message size limit.
|
|
|
|
« Last Edit: August 22, 2008, 05:00:21 am by kevglass »
|
Logged
|
|
|
|
|
kevglass
Guest
|
 |
« Reply #1 on: August 22, 2008, 03:51:18 am » |
|
And the test: package jmetest.renderer;
import java.util.HashMap;
import javax.swing.ImageIcon;
import com.jme.app.SimplePassGame; import com.jme.bounding.BoundingBox; import com.jme.image.Texture; import com.jme.input.ChaseCamera; import com.jme.input.ThirdPersonHandler; import com.jme.light.DirectionalLight; import com.jme.light.PointLight; import com.jme.math.FastMath; import com.jme.math.Vector3f; import com.jme.renderer.ColorRGBA; import com.jme.renderer.Renderer; import com.jme.renderer.pass.DirectionalShadowMapPass; import com.jme.renderer.pass.RenderPass; import com.jme.scene.Node; import com.jme.scene.VBOInfo; import com.jme.scene.shape.Box; import com.jme.scene.shape.PQTorus; import com.jme.scene.state.CullState; import com.jme.scene.state.FogState; import com.jme.scene.state.TextureState; import com.jme.util.TextureManager; import com.jmex.terrain.TerrainPage; import com.jmex.terrain.util.FaultFractalHeightMap; import com.jmex.terrain.util.ProceduralTextureGenerator;
/** * <code>TestDirectionShadowMapPass</code> * * @author Joshua Slack * @version $Revision: 1.15 $ * * @author kevglass - updated to test shadow mapping */ public class TestDirectionalShadowMapPass extends SimplePassGame { private Node m_character; private Node occluders; private ChaseCamera chaser; private TerrainPage page; private FogState fs; private Vector3f normal = new Vector3f(); private static DirectionalShadowMapPass sPass;
/** * Entry point for the test, * * @param args */ public static void main(String[] args) { TestDirectionalShadowMapPass app = new TestDirectionalShadowMapPass(); app.setConfigShowMode(ConfigShowMode.AlwaysShow); app.start(); } TestDirectionalShadowMapPass() { }
/** * builds the scene. * * @see com.jme.app.BaseGame#initGame() */ protected void simpleInitGame() { display.setTitle("jME - Shadow Mapping Pass Test"); display.getRenderer().setBackgroundColor(ColorRGBA.gray.clone());
setupCharacter(); setupTerrain(); setupChaseCamera(); setupInput(); setupOccluders(); rootNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE); RenderPass rPass = new RenderPass(); rPass.add(statNode); rPass.add(rootNode); pManager.add(rPass); sPass = new DirectionalShadowMapPass(new Vector3f(-1,-1,-1)); sPass.add(rootNode); sPass.addOccluder(m_character); sPass.addOccluder(occluders); pManager.add(sPass); } protected void simpleUpdate() { chaser.update(tpf); float characterMinHeight = page.getHeight(m_character .getLocalTranslation())+((BoundingBox)m_character.getWorldBound()).yExtent; if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight)) { m_character.getLocalTranslation().y = characterMinHeight; }
float camMinHeight = characterMinHeight + 150f; if (!Float.isInfinite(camMinHeight) && !Float.isNaN(camMinHeight) && cam.getLocation().y <= camMinHeight) { cam.getLocation().y = camMinHeight; cam.update(); } sPass.setViewTarget(cam.getLocation()); }
private void setupCharacter() { PQTorus b = new PQTorus("torus - target", 2, 3, 2.0f, 1.0f, 64, 12); b.setModelBound(new BoundingBox()); b.updateModelBound(); b.setVBOInfo(new VBOInfo(true)); m_character = new Node("char node"); rootNode.attachChild(m_character); m_character.attachChild(b); m_character.updateWorldBound(); // We do this to allow the camera setup access to the world bound in our setup code.
TextureState ts = display.getRenderer().createTextureState(); ts.setEnabled(true); ts.setTexture( TextureManager.loadTexture( TestDirectionalShadowMapPass.class.getClassLoader().getResource( "jmetest/data/images/Monkey.jpg"), Texture.MinificationFilter.BilinearNearestMipMap, Texture.MagnificationFilter.Bilinear)); m_character.setRenderState(ts); } private void setupTerrain() {
DirectionalLight dr = new DirectionalLight(); dr.setEnabled(true); dr.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f)); dr.setAmbient(new ColorRGBA(.2f, .2f, .2f, .3f)); dr.setDirection(new Vector3f(0.5f, -0.4f, 0).normalizeLocal()); dr.setShadowCaster(true);
PointLight pl = new PointLight(); pl.setEnabled(true); pl.setDiffuse(new ColorRGBA(.7f, .7f, .7f, 1.0f)); pl.setAmbient(new ColorRGBA(.25f, .25f, .25f, .25f)); pl.setLocation(new Vector3f(0,500,0));
DirectionalLight dr2 = new DirectionalLight(); dr2.setEnabled(true); dr2.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f)); dr2.setAmbient(new ColorRGBA(.2f, .2f, .2f, .4f)); dr2.setDirection(new Vector3f(-0.2f, -0.3f, .2f).normalizeLocal()); dr2.setShadowCaster(true);
CullState cs = display.getRenderer().createCullState(); cs.setCullFace(CullState.Face.Back); cs.setEnabled(true); rootNode.setRenderState(cs);
lightState.detachAll(); lightState.attach(dr); lightState.attach(dr2); lightState.attach(pl); lightState.setGlobalAmbient(new ColorRGBA(0.6f, 0.6f, 0.6f, 1.0f));
FaultFractalHeightMap heightMap = new FaultFractalHeightMap(257, 32, 0, 255, 0.55f); Vector3f terrainScale = new Vector3f(10, 1, 10); heightMap.setHeightScale(0.001f); page = new TerrainPage("Terrain", 33, heightMap.getSize(), terrainScale, heightMap.getHeightMap());
page.setDetailTexture(1, 16); rootNode.attachChild(page);
ProceduralTextureGenerator pt = new ProceduralTextureGenerator( heightMap); pt.addTexture(new ImageIcon(TestDirectionalShadowMapPass.class.getClassLoader() .getResource("jmetest/data/texture/grassb.png")), -128, 0, 128); pt.addTexture(new ImageIcon(TestDirectionalShadowMapPass.class.getClassLoader() .getResource("jmetest/data/texture/dirt.jpg")), 0, 128, 255); pt.addTexture(new ImageIcon(TestDirectionalShadowMapPass.class.getClassLoader() .getResource("jmetest/data/texture/highest.jpg")), 128, 255, 384);
pt.createTexture(512);
TextureState ts = display.getRenderer().createTextureState(); ts.setEnabled(true); Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(), Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear, true); ts.setTexture(t1, 0);
Texture t2 = TextureManager.loadTexture(TestDirectionalShadowMapPass.class .getClassLoader() .getResource("jmetest/data/texture/Detail.jpg"), Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear); ts.setTexture(t2, 1); t2.setWrap(Texture.WrapMode.Repeat);
t1.setApply(Texture.ApplyMode.Combine); t1.setCombineFuncRGB(Texture.CombinerFunctionRGB.Modulate); t1.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture); t1.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor); t1.setCombineSrc1RGB(Texture.CombinerSource.PrimaryColor); t1.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);
t2.setApply(Texture.ApplyMode.Combine); t2.setCombineFuncRGB(Texture.CombinerFunctionRGB.AddSigned); t2.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture); t2.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor); t2.setCombineSrc1RGB(Texture.CombinerSource.Previous); t2.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor); rootNode.setRenderState(ts);
fs = display.getRenderer().createFogState(); fs.setDensity(0.5f); fs.setEnabled(true); fs.setColor(new ColorRGBA(0.5f, 0.5f, 0.5f, 0.5f)); fs.setEnd(1000); fs.setStart(500); fs.setDensityFunction(FogState.DensityFunction.Linear); fs.setQuality(FogState.Quality.PerVertex); rootNode.setRenderState(fs); }
private void setupOccluders() {
TextureState ts = display.getRenderer().createTextureState(); ts.setEnabled(true); ts.setTexture( TextureManager.loadTexture( TestDirectionalShadowMapPass.class.getClassLoader().getResource( "jmetest/data/texture/rust.jpg"), Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));
occluders = new Node("occs"); occluders.setRenderState(ts); rootNode.attachChild(occluders); for (int i = 0; i < 50; i++) { Box b = new Box("box", new Vector3f(), 8, 50, 8); b.setModelBound(new BoundingBox()); b.updateModelBound(); float x = (float) Math.random() * 2000 - 1000; float z = (float) Math.random() * 2000 - 1000; b.setLocalTranslation(new Vector3f(x, page.getHeight(x, z)+50, z)); page.getSurfaceNormal(b.getLocalTranslation(), normal ); if (normal != null) b.rotateUpTo(normal); occluders.attachChild(b); } occluders.lock(); } private void setupChaseCamera() { Vector3f targetOffset = new Vector3f(); targetOffset.y = ((BoundingBox) m_character.getWorldBound()).yExtent * 1.5f; chaser = new ChaseCamera(cam, m_character); chaser.setTargetOffset(targetOffset); chaser.getMouseLook().setMinRollOut(150); chaser.setMaxDistance(300); }
private void setupInput() { HashMap<String, Object> handlerProps = new HashMap<String, Object>(); handlerProps.put(ThirdPersonHandler.PROP_DOGRADUAL, "true"); handlerProps.put(ThirdPersonHandler.PROP_TURNSPEED, ""+(.5f * FastMath.PI)); handlerProps.put(ThirdPersonHandler.PROP_LOCKBACKWARDS, "true"); handlerProps.put(ThirdPersonHandler.PROP_CAMERAALIGNEDMOVE, "true"); handlerProps.put(ThirdPersonHandler.PROP_ROTATEONLY, "true"); input = new ThirdPersonHandler(m_character, cam, handlerProps); input.setActionSpeed(100f); } }
Kev
|
|
|
|
|
Logged
|
|
|
|
nymon
Project Advocate
Hero Member
Offline
Posts: 1255
|
 |
« Reply #2 on: August 22, 2008, 07:39:20 am » |
|
This is awesome work kevglass, thanks for sharing it!
|
|
|
|
|
Logged
|
|
|
|
hevee
Project Advocate
Sr. Member
Offline
Posts: 945
|
 |
« Reply #3 on: August 22, 2008, 12:50:24 pm » |
|
Thank you very much kevglass!
|
|
|
|
|
Logged
|
|
|
|
Methius
Jr. Member

Offline
Posts: 87
Guaranteed slower than the fastest solution
|
 |
« Reply #4 on: August 22, 2008, 02:31:12 pm » |
|
Thank you so much for this!
|
|
|
|
|
Logged
|
|
|
|
|
faust
|
 |
« Reply #5 on: August 22, 2008, 03:58:34 pm » |
|
Brilliant, thanks for contributing your code. Now it just needs to be added to jME.
|
|
|
|
« Last Edit: August 22, 2008, 04:11:51 pm by faust »
|
Logged
|
|
|
|
|
kevglass
Guest
|
 |
« Reply #6 on: August 22, 2008, 04:32:06 pm » |
|
Now it just needs to be added to jME.
It's a little way off that I'm afraid, needs a fair bit of massaging. Ideally it should find the centre of view itself and should support multiple directional light sources (which I'm starting to think could be implemented with multitexturing if needed - I'll have a go later maybe). Kev
|
|
|
|
|
Logged
|
|
|
|
Core-Dump
Committer
Hero Member
Offline
Posts: 2663
|
 |
« Reply #7 on: August 22, 2008, 04:54:16 pm » |
|
in your screenshot the shadows looks pretty smooth, when i try your test, the shadow has hard edges.  Is this my ati X1400 which makes problems, or did you use different settings in your screenshot to make the shadow looks smoother?
|
|
|
|
|
Logged
|
|
|
|
|
kevglass
Guest
|
 |
« Reply #8 on: August 22, 2008, 04:58:40 pm » |
|
The bilinear filtering on the shadowmap texture is the thing that should do that, maybe the card doesn't support that on depth textures? You could try upping the resolution but I think it's pretty big already (2048 I think?). Kev PS. Glad it at least ran and rendered for you 
|
|
|
|
« Last Edit: August 22, 2008, 05:00:37 pm by kevglass »
|
Logged
|
|
|
|
Core-Dump
Committer
Hero Member
Offline
Posts: 2663
|
 |
« Reply #9 on: August 22, 2008, 05:11:17 pm » |
|
yeah i tried 8196 but that didn't look much better and i guess its a bit too big performance wise  i'll try to play a bit with those settings and see if i can get it to work better on those cards.
|
|
|
|
|
Logged
|
|
|
|
|
kevglass
Guest
|
 |
« Reply #10 on: August 22, 2008, 05:16:29 pm » |
|
If it's any help, I'm running on a NV7600 (laptop) card. Thats where the shots were taken.
Kev
|
|
|
|
|
Logged
|
|
|
|
|
clovis
|
 |
« Reply #11 on: August 22, 2008, 05:19:34 pm » |
|
Brilliant, thanks for contributing your code. Now it just needs to be added to jME.
Needs avoid lwjgl dependency before fgo to jME core. Like Kev said, jME Texture must suport those parameters.
|
|
|
|
|
Logged
|
|
|
|
|
|
|
vear
|
 |
« Reply #13 on: August 22, 2008, 07:00:16 pm » |
|
NV cards come with free percentage close filtering on shadow maps, ATI cards dont. The solution is to implement shadow mapping and PCF in shader.
|
|
|
|
|
Logged
|
|
|
|
Core-Dump
Committer
Hero Member
Offline
Posts: 2663
|
 |
« Reply #14 on: August 22, 2008, 08:57:05 pm » |
|
interesting PCF slides, i didn't know what it means  edit: doh, now the correct url 
|
|
|
|
« Last Edit: August 22, 2008, 09:50:07 pm by Core-Dump »
|
Logged
|
|
|
|
|