Reflection shader/processor


Tried to write this one up earlier and got a bit sidetracked. I was playing with the idea of zero’ing out all of the water related features of the SimpleWaterProcessor, to attempt a quick n’ dirty reflection. The image below is what I ended up with. I got pretty close, but I ran into a few issues with the reflection/refraction offset, and that I think the geometry underneath that has the pond texture has the reversal appearance to what a shiny floor would look like. When the camera is above, it’s almost impossible to see, and at an angle it becomes clearer. I think shiny surfaces are the opposite, with more clarity from above and less from the sides/angles.

I did it this way because I figured that reflective floors generally have coats and coats of some protectant on them, that’s the water layer. Then the actual floor is below that, which really, could be another geometry altogether.

I guess my question here, is once I start removing things from the simple water processor to just get the reflection, are there any obvious shortcuts/optimizations that could be taken into consideration. I ‘think’ I understand the logic, though I’m not sure if I understand what’s going on in the shaders. For my purposes I could care less about the environment mapping as my project is entirely indoors :stuck_out_tongue:

Also, are there any plans to take the awesome waterFilter and make it applicable to quads? :stuck_out_tongue: I know some of the tricks it employs certainly cannot be done on a quad by quad basis, but I wonder how much of it could. That filter is amazing. XD



The above shows a strange issue I had when the reflection Offset was greater than -1.

The above shows as close as I was able to get.

1 Like

Well that looks good.

The reflectionOffset is just here to offset the clip plane for reflection rendering from the real quad.

tehflah said:
Also, are there any plans to take the awesome waterFilter and make it applicable to quads? :p I know some of the tricks it employs certainly cannot be done on a quad by quad basis, but I wonder how much of it could. That filter is amazing. XD

Well the water filter use the exact same code for computing the reflection. However, it's a full screen post process filter, and the water rendered is not a geometry. It' can't be applied to a quad as a material.

Hello Again!

Is there anything I can do about the “tearing” I’m getting near the walls @nehon? It’s very distracting. I sorta-kinda need the reflected quad clipping to be precise for another application, where I plan on a third person view of a room with this as the water surface. So it’s essentially a box o’ water with only the top being reflective, and the rest being like, a simple transparent texture of some sort.

I played with this a bit more and added particles! Yay! They reflect too, it’s sorta neat :stuck_out_tongue:

Code below.



Current Test Case: Very messy, and based off a lot of things…


public class TestReflectParticle extends SimpleApplication {

private static TestReflectParticle app;

private static AppSettings settings;

private Node roomNode;

public static void main(String[] args) {

app = new TestReflectParticle();

settings = new AppSettings(true);


settings.setTitle(“Reflected Particles.”);





public void simpleInitApp() {


// Box floor = new Box(new Vector3f(0, -5, 0), 5, 5, 5);

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

// Material floorMat = new Material(assetManager, “Common/MatDefs/”);

PointLight pl = new PointLight();

pl.setPosition(new Vector3f(0, 3, 0));




roomNode = new Node(“RoomRoot”);

// Walls

Box eastWallMesh = new Box(0.5f, 2f, 5f);

Geometry eastWall = new Geometry("(Box) Wall 1 - X5.5", eastWallMesh);

eastWall.move(5.5f, 2.5f, 0);

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

wallMat.setBoolean(“UseMaterialColors”, true);

wallMat.setColor(“Diffuse”, ColorRGBA.Orange);

wallMat.setColor(“Ambient”, new ColorRGBA(0.1f, 0.1f, 0.1f, 1));

wallMat.setColor(“Specular”, ColorRGBA.White);

wallMat.setFloat(“Shininess”, 10f);



Box westWallMesh = new Box(0.5f, 2f, 5f);

Geometry westWall = new Geometry("(Box) Wall 2 - X-5.5", westWallMesh);

westWall.move(-5.5f, 2.5f, 0);


// roomNode.attachChild(westWall);

Box northWallMesh = new Box(5f, 2f, 0.5f);

Geometry northWall = new Geometry("(Box) Wall 3 - Z5.5", northWallMesh);

northWall.move(0, 2.5f, 5.5f);



Box southWallMesh = new Box(5f, 2f, 0.5f);

Geometry southWall = new Geometry("(Box) Wall 4 - Z-5.5", southWallMesh);

southWall.move(0, 2.5f, -5.5f);


// roomNode.attachChild(southWall);


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

fakeFloorMat.setTexture(“DiffuseMap”, assetManager.loadTexture(“Textures/Terrain/Pond/Pond.png”));

// fakeFloorMat.setTexture(“NormalMap”, assetManager.loadTexture(“Textures/Terrain/Pond/Pond_normal.png”));

fakeFloorMat.setFloat(“Shininess”, 8f);

// Floor

Box floor = new Box(5f, 0.5f, 5f);

Geometry floorGeom = new Geometry("(Box) Floor", floor);

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

floorMat.setBoolean(“UseMaterialColors”, true);

floorMat.setColor(“Diffuse”, ColorRGBA.Red);

floorMat.setColor(“Ambient”, new ColorRGBA(0.1f, 0.1f, 0.1f, 1));

floorMat.setColor(“Specular”, ColorRGBA.White);

floorMat.setFloat(“Shininess”, 10f);



Sphere s = new Sphere(16, 16, .1f);

Geometry sG = new Geometry(“Sphere”, s);

Material sM = new Material(assetManager, “Common/MatDefs/Misc/SolidColor.j3md”);

sM.setColor(“m_Color”, new ColorRGBA(0, 0, 1, 0.4f));





sG.move(1.5f, 2, 1.5f);

// Water

// FilterPostProcessor fpp;

// WaterFilter water;

// Vector3f lightDir = new Vector3f(0, -1f, 0); // same as light source

// float initialWaterHeight = 2f; // choose a value for your scene


// fpp = new FilterPostProcessor(assetManager);

// water = new WaterFilter(rootNode, lightDir);

// water.setWaterHeight(initialWaterHeight);

// water.setWaterColor(ColorRGBA.Yellow);

// water.setMaxAmplitude(0);

// water.setSpeed(0);

// water.setShoreHardness(0.5f);

// water.setDeepWaterColor(new ColorRGBA(0.5f, 0.5f, 0, 0.5f));

//// water.setColorExtinction(new Vector3f(.5f, .5f, 0f));

// fpp.addFilter(water);

// viewPort.addProcessor(fpp);

SimpleWaterProcessor waterProcessor = new SimpleWaterProcessor(assetManager);







// waterProcessor.setWaterDepth(8);




//setting the water plane

Vector3f waterLocation=new Vector3f(0,0.501f,0);

waterProcessor.setPlane(new Plane(Vector3f.UNIT_Y,;

WaterUI waterUi=new WaterUI(inputManager, waterProcessor);


Quad quad = new Quad(10.5f, 10.5f);

quad.scaleTextureCoordinates(new Vector2f(0.001f, 0.001f));

Geometry water=new Geometry(“water”, quad);

// water.setShadowMode(ShadowMode.Receive);

water.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X));


// water.setMaterial(wallMat);

water.setLocalTranslation(-5f, 0.501f, 5.5f);



Quad quad2 = new Quad(10, 10);

Geometry floorQuad = new Geometry(“fakeFloor”, quad2);

// water.setShadowMode(ShadowMode.Receive);

floorQuad.setLocalRotation(new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_X));


floorQuad.setLocalTranslation(-5f, 1.98f, 5f);

// roomNode.attachChild(floorQuad);

// Particles

ParticleEmitter fire = new ParticleEmitter(“Emitter”, Type.Triangle, 120);

Material mat_red = new Material(assetManager, “Common/MatDefs/Misc/Particle.j3md”);

mat_red.setTexture(“Texture”, assetManager.loadTexture(“Effects/Explosion/flame.png”));


fire.setImagesX(2); fire.setImagesY(2); // 2x2 texture animation

fire.setEndColor( new ColorRGBA(0.5f, 0.5f, 1f, 1f));

fire.setStartColor(new ColorRGBA(0.25f, 0.25f, 1f, 0.5f));

fire.setInitialVelocity(new Vector3f(0, 1, 0));







fire.setLocalTranslation(new Vector3f(0, 3, 0));


ParticleEmitter debris = new ParticleEmitter(“Debris”, Type.Triangle, 10);

Material debris_mat = new Material(assetManager, “Common/MatDefs/Misc/Particle.j3md”);

debris_mat.setTexture(“Texture”, assetManager.loadTexture(“Effects/Explosion/Debris.png”));


debris.setImagesX(3); debris.setImagesY(3); // 3x3 texture animation



debris.setInitialVelocity(new Vector3f(0, 4, 0));

debris.setStartColor(new ColorRGBA(1f, 1f, 1f, 1f));

debris.setEndColor(new ColorRGBA(1f, 1f, 1f, 1f));



debris.setLocalTranslation(new Vector3f(0, 3, 0));






nehon said:
Well the water filter use the exact same code for computing the reflection. However, it's a full screen post process filter, and the water rendered is not a geometry. It' can't be applied to a quad as a material.

You forgot about our nice quad-based simplewater renderer t.t its ideal for mirrors etc.
normen said:
You forgot about our nice quad-based simplewater renderer t.t its ideal for mirrors etc.

Errrrrr.... I think that's what I'm using. I'm using ONE of the water processors lol.... :p The crazy full-screen one is amazing looking though... I just wish I could use it for bodies of water smaller than oceans. heh. :p But I mean, I understand. A lot of the tricks it employs to look that good just can't be done on a smaller scale. No problemo.

The naming screwed me up real bad when I was first figuring out what was doing what.

1 Like
normen said:
You forgot about our nice quad-based simplewater renderer t.t its ideal for mirrors etc.

How could I forget this Normen??? :p

No I was aware that tehflah was using this one, but he asked about the waterFilter, hence my remark.

@tehflah the cliping is not very precise for some reasons, usually tweaking the clipping offset does the trick. i'm gonna look into it.

Hello again!

Guess I should be a little more specific as well. It’s worst when you’re moving the camera. I’m guessing it has something to do with when things are updated. You’ll see black sheering along the wall connection.

Though, I played with the offsets for a long time and couldn’t find a balance. If it’s too large, it ‘bleeds.’ If it’s too small, the banding/tearing gets really really bad.


oh ok i know what’s the issue.

I had the same with the water filter.

The reflection is computed one frame late (because of the viewports initialization), so there is a noticeable offset when moving the camera, and it’s more noticeable when the frame rate is low.

I resolved this by not adding the view port to the preViewPorts list and rendering it myself with renderManager.renderViewport().

Also be sure that the camera up vector is Y, it can cause issue if the camera’s up axis is oblique.