Downloads
|
jME-CircleShader.zip:
|
Download
|
Okay, here we are going to use the circle shader to create a detailed animated ring-wave. We're going to accomplish this using three quads each with it's own circle shader
Material.
We'll begin by creating a few global variables:
Geometry geom;
Geometry geom2;
Geometry geom3;
float radius = 0.17f;
float offset = 0.0f;
Now that we have that let's go ahead and create the base ring in the
simpleInitApp method:
geom = new Geometry("Quad", new Quad(480, 480));
Material mat = new Material(assetManager, "Common/MatDefs/Circle/Circle.j3md");
mat.setColor("Color", new ColorRGBA(0f, 0f, 0f, 0f));
mat.setColor("Color2", new ColorRGBA(0.1f, 0.1f, 1f, 0.35f));
mat.setColor("Color3", new ColorRGBA(0.3f, 0.86f, 1f, 1f));
mat.setColor("Color4", new ColorRGBA(0.8f, 1f, 1f, 1f));
mat.setColor("Color5", new ColorRGBA(0f, 0f, 0f, 0f));
mat.setColor("Color6", new ColorRGBA(0f, 0f, 0f, 0f));
mat.setFloat("Offset", 0f);
mat.setFloat("pos2", 0.06f);
mat.setFloat("pos3", 0.1f);
mat.setFloat("pos4", 0.15f);
mat.setFloat("pos5", 0.17f);
mat.setFloat("Radius", 0.35f);
geom.setMaterial(mat);
geom.setLocalTranslation(80, 0, 0);
mat.setTexture("DistortMap", assetManager.loadTexture("Textures/DistortionMap.png"));
mat.setFloat("DistortX", 0.05f);
mat.setFloat("DistortY", 0.05f);
guiNode.attachChild(geom);

We're creating a simple greenish blue ring that fades in on the front end and then out on the back end. We create a slightly brighter front edge and fade to cooler colors to give the appearance that the front end is hotter and that heat is bleading off over the surface of the wave. In addition to that we're adding a distortion map that will give our ring the appearance of crossing through areas of different densities.
Now we want to animate this ring-wave so in our
simpleUpdate(float tpf) method let's add the following:
radius += 0.5f * tpf;
if (radius >= 0.97f) {
radius = 0.17f;
}
geom.getMaterial().setFloat("pos2", radius - 0.1f);
geom.getMaterial().setFloat("pos3", radius - 0.07f);
geom.getMaterial().setFloat("pos4", radius - 0.03f);
geom.getMaterial().setFloat("pos5", radius);
if (radius >= 0.4f) {
offset += 0.7f * tpf;
if (offset >= radius - 0.13f)
offset = radius - 0.13f;
} else {
offset = 0f;
}
geom.getMaterial().setFloat("Offset", offset);
If you run your application now you should see a nice ring growing in size. While it looks pretty good as-is let's add some more detail to it. Back in the
simpleInitApp method let's create another ring:
geom2 = new Geometry("Quad", new Quad(480, 480));
mat = new Material(assetManager, "Common/MatDefs/Circle/Circle.j3md");
mat.setColor("Color", new ColorRGBA(0f, 0f, 0f, 0f));
mat.setColor("Color2", new ColorRGBA(0.3f, 0.97f, 0.9f, 0.4f));
mat.setColor("Color3", new ColorRGBA(0.3f, 0.97f, 0.9f, 0.4f));
mat.setColor("Color4", new ColorRGBA(0f, 0f, 0f, 0f));
mat.setColor("Color5", new ColorRGBA(0f, 0f, 0f, 0f));
mat.setFloat("Offset", 0.05f);
mat.setFloat("pos2", 0.09f);
mat.setFloat("pos3", 0.16f);
mat.setFloat("pos4", 0.18f);
mat.setFloat("Radius", 0.35f);
geom2.setMaterial(mat);
geom2.setLocalTranslation(80, 0, 0);
mat.setTexture("ColorMap", assetManager.loadTexture("Textures/Electric.jpg"));
mat.setTexture("DistortMap", assetManager.loadTexture("Textures/DistortionMap.png"));
mat.setFloat("DistortX", 0.05f);
mat.setFloat("DistortY", 0.05f);
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.AlphaAdditive);
guiNode.attachChild(geom2);

By and large we're using the same settings as the first ring with a few notable differences. We removed the sixth color as we don't need the long trail on the inner part of the ring here. We also modified the positions of the other colors so that this one has a slightly larger ring area.
Additionally we've added in a
ColorMap and set the material to the
AlphaAdditive blend mode because we want this one added on top of the first one. This is going to add a little bit of electricity to our ring-wave.
Head back to the
simpleUpdate loop and add the following code just after setting the first
geom pos5 property.
geom2.getMaterial().setFloat("pos2", radius - 0.07f);
geom2.getMaterial().setFloat("pos3", radius - 0.02f);
geom2.getMaterial().setFloat("pos4", radius + 0.03f);
geom2.getMaterial().setFloat("Offset", radius - 0.13f);
Again the positioning is slightly different as our ring area is a little larger than the original ring.
One more ring to add to the
simpleInitApp method. This one is going to add a little bit of smoke to our wave.
geom3 = new Geometry("Quad", new Quad(480, 480));
mat = new Material(assetManager, "Common/MatDefs/Circle/Circle.j3md");
mat.setColor("Color", new ColorRGBA(0f, 0f, 0f, 0f));
mat.setColor("Color2", new ColorRGBA(0.3f, 0.3f, 1f, 0.35f));
mat.setColor("Color3", new ColorRGBA(0.3f, 0.3f, 1f, 1f));
mat.setColor("Color4", new ColorRGBA(0.3f, 0.3f, 1f, 1f));
mat.setColor("Color5", new ColorRGBA(0f, 0f, 0f, 0f));
mat.setColor("Color6", new ColorRGBA(0f, 0f, 0f, 0f));
mat.setFloat("Offset", 0f);
mat.setFloat("pos2", 0.05f);
mat.setFloat("pos3", 0.09f);
mat.setFloat("pos4", 0.18f);
mat.setFloat("pos5", 0.2f);
mat.setFloat("Radius", 0.35f);
geom3.setMaterial(mat);
geom3.setLocalTranslation(80, 0, 0);
mat.setTexture("ColorMap", assetManager.loadTexture("Textures/CloudTexture2.png"));
mat.setTexture("DistortMap", assetManager.loadTexture("Textures/DistortionMap.png"));
mat.setFloat("DistortX", 0.05f);
mat.setFloat("DistortY", 0.05f);
guiNode.attachChild(geom3);

Largely the same as our original ring with a slightly larger ring and trail area, we've also changed the colors up a bit and added in a
ColorMap cloud texture.
Let's add our new ring to the
simpleUpdate loop right after that last code we added to the loop:
geom3.getMaterial().setFloat("pos2", radius - 0.11f);
geom3.getMaterial().setFloat("pos3", radius - 0.08f);
geom3.getMaterial().setFloat("pos4", radius - 0f);
geom3.getMaterial().setFloat("pos5", radius + 0.03f);
One additional line to add to the
simpleUpdate loop right after setting the original ring's
Offset property:
geom3.getMaterial().setFloat("Offset", offset - 0.02f);
Your
simpleUpdate loop should look like this:
radius += 0.5f * tpf;
if (radius >= 0.97f) {
radius = 0.17f;
}
geom.getMaterial().setFloat("pos2", radius - 0.1f);
geom.getMaterial().setFloat("pos3", radius - 0.07f);
geom.getMaterial().setFloat("pos4", radius - 0.03f);
geom.getMaterial().setFloat("pos5", radius);
geom2.getMaterial().setFloat("pos2", radius - 0.07f);
geom2.getMaterial().setFloat("pos3", radius - 0.02f);
geom2.getMaterial().setFloat("pos4", radius + 0.03f);
geom2.getMaterial().setFloat("Offset", radius - 0.13f);
geom3.getMaterial().setFloat("pos2", radius - 0.11f);
geom3.getMaterial().setFloat("pos3", radius - 0.08f);
geom3.getMaterial().setFloat("pos4", radius - 0f);
geom3.getMaterial().setFloat("pos5", radius + 0.03f);
if (radius >= 0.4f) {
offset += 0.7f * tpf;
if (offset >= radius - 0.13f)
offset = radius - 0.13f;
} else {
offset = 0f;
}
geom.getMaterial().setFloat("Offset", offset);
geom3.getMaterial().setFloat("Offset", offset - 0.02f);
Go ahead and run your application. The finished product should look like the following:

In practice if you're using a ring-wave like this in your 3d scene, rather than using the
guiNode you may need to place each ring slightly above the other to prevent z-fighting. Depending on how you're using this you may be able to disable depth buffer writing instead:
mat.getAdditionalRenderState().setDepthWrite(false);