www.jpct.net

jPCT - a 3d engine for Java => Support => Topic started by: AGP on January 09, 2011, 09:13:08 pm

Title: Water Shader Textures
Post by: AGP on January 09, 2011, 09:13:08 pm
I'm trying to play with the wiki's NormalMap shader. I have a water plane. The example adds two textures, "stones" and "normals" to the TextureManager. Seeing as how I don't know what the textures look like, or what mine should (I would like the water plane's vertices to distort up and down such that it looks remotely like water) I don't know what textures to load. Any help is appreciated.
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 10, 2011, 08:24:36 am
It's a standard texture map and a normal map. The resources for the advanced example contain such a combination. You can find plenty of other examples on the net as well. But keep in mind that when using shaders, you'll loose all fixed function support, i.e. lighting, shadow mapping, projective texturing, fogging...all won't work unless you reproduce the required calculations in your shaders.
Title: Re: Water Shader Textures
Post by: AGP on January 10, 2011, 04:40:28 pm
Fortunately, the ReflectionHelper has been failing me anyway, so I've nothing to lose. :p Anyway, am I supposed to lose the texture as well (because I have been)?
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 10, 2011, 05:25:40 pm
You get all you do in the shader, everything else is lost. The example shaders in the wiki of course do texturing, or otherwise, it wouldn't make much sense. Also keep in mind that some hatdware/drivers simply fail with shaders...like some Intel chips.
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 10, 2011, 05:28:51 pm
Btw: What went wrong with the ReflectionHelper? Its supposed to work even on the lowest chips like crap from Intel and a Geforce2 MX...i've tested both.
Title: Re: Water Shader Textures
Post by: AGP on January 10, 2011, 05:43:19 pm
The program hangs. I've waited for as much as 20 minutes.
Title: Re: Water Shader Textures
Post by: AGP on January 10, 2011, 07:19:15 pm
About the shader, I noticed it strips the sphere. Is that necessary (I'm trying to determine why my plane is losing its texture)?

EDIT: Anyhow, removing strip() didn't solve the texture disappearance problem. And yes, I'm setting the right texture in the shader.

EDIT2: Got the texture back by removing compile()! Is that necessary (other than to speed it up)?
Title: Re: Water Shader Textures
Post by: AGP on January 10, 2011, 07:30:35 pm
So now that I got my texture, how do I animate this thing? Do I have to keep replacing the normal texture (and is that the best way to achieve movement)?
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 10, 2011, 11:51:04 pm
Removing compile()-call will disable shader support. You can't use shaders on uncompiled objects. If your texture doesn't appear when compiling it, you are doing something wrong with the shader or shaders aren't supported by your hardware/driver (some Intel OpenGL drivers don't support GLSL shaders even if the hardware should be able to handle them). You should get some information in the log if that happens. Maybe you can post the log when using compile and shaders?
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 10, 2011, 11:53:18 pm
About the hang with the ReflectionHelper: Seems to be a threading issue in your code. You have to be careful of what you do in which thread when using the AWLGLCanvas, or you'll make one thread wait for the drawing thread which is blocked...which results in an infinite wait. If you can create a simple test case, i can have a closer look.
Title: Re: Water Shader Textures
Post by: AGP on January 11, 2011, 02:30:48 am
It can't be a threading issue since there's a total of two lines referencing it, and the game loop is where render() draw() and display() are called. And even a GeForce 9800 is producing the message "shadow-mapping is not supported." In my opinion, based on these two facts alone (not a threading thing and the GeForce 9800 message) there's something wrong with both ShadowHelper and the ReflectionHelper.
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 11, 2011, 07:32:16 am
Nope, it's not. I have thousands of screen shots from different machines from all over world provided by the Robombs feedback function that show nice shadows. The only plattforms that have trouble doing it correctly are some SiS "gpus" and some linux drivers that go crazy on the shadows. Apart from that, everything works fine.
If shadow mapping doesn't work on a GeForce 9800, check your drivers. Maybe you are using the stock Windows drivers or some other obscure one (which OS btw?).
As said, a log output might help.

And it may very well be a threading issue of some kind, because the AWTGLRenderer has to do it's own threading stuff in the background which might interfere with whatever you are doing. Without a test case, i can't do anything...
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 11, 2011, 12:49:12 pm
EDIT: Anyhow, removing strip() didn't solve the texture disappearance problem. And yes, I'm setting the right texture in the shader.
Remember that the shader needs a light source to work with. Without one, everything will be black.
Title: Re: Water Shader Textures
Post by: AGP on January 11, 2011, 02:35:19 pm
I'll see about a test case, but the output is nearly 10 thousand lines lomg.
Title: Re: Water Shader Textures
Post by: AGP on January 11, 2011, 08:19:45 pm
Very interesting development: this shader works compiled and stripped, yours doesn't. http://forum.bonzaisoftware.com/viewthread.php?tid=10
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 11, 2011, 08:28:00 pm
I quote myself:
Remember that the shader needs a light source to work with. Without one, everything will be black.
Your shader doesn't use a GL light source, mine does. If you don't provide one, everything will be black. Anyway, if it works fine with that one, all is well. I'm sure it's better suited for water then my simple bump mapping shader is. Any screen shots of how the result looks like (just out of interest)?
Title: Re: Water Shader Textures
Post by: AGP on January 11, 2011, 08:41:26 pm
Mine isn't the best texture or normal map, but here you go:

(http://www.agpgames.com/SuperScreenshot.png)
Title: Re: Water Shader Textures
Post by: AGP on January 11, 2011, 08:44:58 pm
PS: We're working on a better-scaled background! :- )

EDIT: Also, I read the comment about the light this morning. Although there were already two lights (one for the moon and one over Superman), I added four more (one to each side of Superman) to the scene and your shader still looked black.
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 11, 2011, 09:25:14 pm
Well, no idea then. Maybe the angle between the light and the surface makes it appear black...who knows. It doesn't really matter now that you seem to have something that works reasonable well.
Title: Re: Water Shader Textures
Post by: AGP on January 12, 2011, 04:34:52 pm
But I'm nowhere near satisfied with this water yet. Any chance I could persuade you to try a water shader? I've tried several and the one I think could work well goes black like yours did. It's important to note that this is happening on three very different computers, with distinctly different video cards.

EDIT: Also, the first shader I made work yesterday produced the odd effect of rotating the texture along with the camera. I'm not sure if it was intentional of if I wasn't doing something I was supposed to be doing.
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 12, 2011, 05:30:32 pm
Then why not post-pone that shader stuff and go with something easier? A simple sin/cos-based vertex controller effect with a base water texture and and environment map on top might look good enough and will work everywhere.
Title: Re: Water Shader Textures
Post by: AGP on January 12, 2011, 06:17:14 pm
Because I'd like to tackle the shader issue at some point and now is as good as any. Truth be told, if the ReflectionHelper would start working I'd be very happy. But also, aren't shaders a lot faster than a simple vertex controller?
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 12, 2011, 06:29:12 pm
Yes, they are faster...but that doesn't mean that not using them is a problem. The reflection helper might not very well suited for your application anyway. It works on planar surfaces. If you can tilt the camera, the reflection will go crazy.
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 12, 2011, 06:32:57 pm
Cheapo water effect (20min of work, so don't expect too much) without shaders. It skips updating the normals in the controller and calculates them every frame, which is costly. But i didn't have the time to figure out how to do it in the controller.
Textures can be found in http://www.jpct.net/download/misc (http://www.jpct.net/download/misc)

Code: [Select]
import com.threed.jpct.Config;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.GenericVertexController;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Matrix;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureInfo;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;
import com.threed.jpct.util.Light;

public class CheapWater {

private World world;

private FrameBuffer buffer;

private Object3D water;

private WaterController wc = null;

public static void main(String[] args) throws Exception {
new CheapWater().loop();
}

public CheapWater() throws Exception {

Config.glForceEnvMapToSecondStage = true;
Config.glUseVBO = true;

world = new World();
world.setAmbientLight(100, 100, 100);

TextureManager tm = TextureManager.getInstance();

tm.addTexture("water", new Texture("water3.jpg"));
tm.addTexture("envmap", new Texture("environment.jpg"));

water = Primitives.getPlane(20, 3);

TextureInfo ti = new TextureInfo(tm.getTextureID("water"));
ti.add(tm.getTextureID("envmap"), TextureInfo.MODE_MODULATE);

wc = new WaterController();

water.setTexture(ti);
water.setEnvmapped(Object3D.ENVMAP_ENABLED);
water.rotateX((float) Math.PI / 2f);
water.rotateMesh();
water.clearRotation();
water.build();
water.compile(true);
water.setTransparency(2);
water.getMesh().setVertexController(wc, false);
water.setTextureMatrix(new Matrix());
world.addObject(water);

world.getCamera().setPosition(0, -50, -50);
world.getCamera().lookAt(water.getTransformedCenter());

Light light=new Light(world);
light.setAttenuation(-1);
light.setIntensity(255, 255, 255);
light.setPosition(new SimpleVector(100,-50,-20));
}

private void loop() throws Exception {
buffer = new FrameBuffer(800, 600, FrameBuffer.SAMPLINGMODE_GL_AA_2X);
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
buffer.enableRenderer(IRenderer.RENDERER_OPENGL);

while (!org.lwjgl.opengl.Display.isCloseRequested()) {
buffer.clear(java.awt.Color.BLUE);

wc.update(0.5f);

water.getMesh().applyVertexController();
water.touch();
water.calcNormals(); // This isn't good...it should actually be done in the VertexController...
water.getTextureMatrix().translate(0.0015f, -0.0015f, 0);

world.renderScene(buffer);
world.draw(buffer);
buffer.update();
buffer.displayGLOnly();
Thread.sleep(10);
}
buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
buffer.dispose();
System.exit(0);
}

private static class WaterController extends GenericVertexController {

private static final long serialVersionUID = 1L;

private float degreeAdd = 0;

public void update(float inc) {
degreeAdd += inc;
}

public void apply() {
SimpleVector[] source = this.getSourceMesh();
SimpleVector[] dest = this.getDestinationMesh();

int end = source.length;

for (int i = 0; i < end; i++) {
SimpleVector s = source[i];
SimpleVector d = dest[i];
float sin = (float) Math.sin((((((degreeAdd + s.x + s.z)/10f) % 360))));
d.set(s.x, s.y + sin*2, s.z);

//@todo
// Update the normals here too. Formula?
}
}
}

}

Title: Re: Water Shader Textures
Post by: AGP on January 12, 2011, 06:40:19 pm
Thanks a lot, I will try this out and report.

But I'd still like to make the shader thing work (that's the appeal to me!).
Title: Re: Water Shader Textures
Post by: AGP on January 12, 2011, 07:41:40 pm
It's definately not fast, but I love that it moves like water on the shores. If it could be sped up, and you could solve the normals formula (to maybe look like it moves more three-dimensionally and less planar), I would have to find another excuse to play with shaders on jpct. :- )

(http://www.agpgames.com/SuperScreenshot2.png)
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 12, 2011, 09:24:30 pm
Here's an improved version. It pre-calculated the normals in the controller based on a formula that i pulled out of my nose. With that change, performance is like this on my machine (Core2 Quad @3.2Ghz, AMD Radeon 5870):

1600 polygons/precalc normals: 4500fps
1600 polygons/real normals: 2500fps

10000 polygons/precalc normals: 2300fps
10000 polygons/real normals: 30fps

...so precalc normals should be fast enough IMHO.

Here's the code. You can now apply an additional scale that changes the waves' height as well as a damping factor that makes the waves narrow or wide.

Code: [Select]
import com.threed.jpct.Config;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.GenericVertexController;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Matrix;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureInfo;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;
import com.threed.jpct.util.Light;

public class CheapWater {

private World world;

private FrameBuffer buffer;

private Object3D water;

private WaterController wc = null;

public static void main(String[] args) throws Exception {
new CheapWater().loop();
}

public CheapWater() throws Exception {

Config.glForceEnvMapToSecondStage = true;
Config.glUseVBO = true;
                Config.glDynamicBatchSize=4000;

world = new World();
world.setAmbientLight(100, 100, 100);

TextureManager tm = TextureManager.getInstance();

tm.addTexture("water", new Texture("water3.jpg"));
tm.addTexture("envmap", new Texture("environment2.jpg"));

water = Primitives.getPlane(40, 1.5f);

TextureInfo ti = new TextureInfo(tm.getTextureID("water"));
ti.add(tm.getTextureID("envmap"), TextureInfo.MODE_MODULATE);

water.setTexture(ti);
water.setEnvmapped(Object3D.ENVMAP_ENABLED);
water.rotateX((float) Math.PI / 2f);
water.rotateMesh();
water.clearRotation();
water.build();
water.compile(true);
water.setTransparency(2);
wc = new WaterController(water, 5, 10, false);
water.getMesh().setVertexController(wc, false);
world.addObject(water);

world.getCamera().setPosition(0, -50, -50);
world.getCamera().lookAt(water.getTransformedCenter());

Light light = new Light(world);
light.setAttenuation(-1);
light.setIntensity(255, 255, 255);
light.setPosition(new SimpleVector(100, -50, -20));
}

private void loop() throws Exception {
buffer = new FrameBuffer(800, 600, FrameBuffer.SAMPLINGMODE_GL_AA_2X);
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
buffer.enableRenderer(IRenderer.RENDERER_OPENGL);

long s = System.nanoTime() / 1000000L;
long ss = System.currentTimeMillis();
int fps = 0;

while (!org.lwjgl.opengl.Display.isCloseRequested()) {
buffer.clear(java.awt.Color.BLUE);

long ticks = ((System.nanoTime() / 1000000L) - s) / 10;

if (ticks > 0) {
s = System.nanoTime() / 1000000L;
wc.update(0.5f * (float) ticks);
water.getMesh().applyVertexController();
}

world.renderScene(buffer);
world.draw(buffer);
buffer.update();
buffer.displayGLOnly();
fps++;
if (System.currentTimeMillis() - ss >= 1000) {
System.out.println(fps + "fps");
fps = 0;
ss = System.currentTimeMillis();
}
}
buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
buffer.dispose();
System.exit(0);
}

private static class WaterController extends GenericVertexController {

private float scale = 0;

private float damping = 0;

private SimpleVector[] preCalcNormals = null;

private SimpleVector[] preCalcNormalsNeg = null;

private float[] lastHeight = null;

private static final long serialVersionUID = 1L;

private float degreeAdd = 0;

private Object3D water = null;

private float lastUpdate = 0;

private boolean realNormals = false;

public WaterController(Object3D water, float scale, float damping, boolean realNormals) {
this.scale = scale;
this.water = water;
this.realNormals = realNormals;
this.damping = damping;
water.setTextureMatrix(new Matrix());
}

/**
* This calculates some normals...these are rather fake and in no way
* comparable to real surface normals. But they should do the trick...
*/
public boolean setup() {
SimpleVector ax = new SimpleVector(-1, 0, 1).normalize();
preCalcNormals = new SimpleVector[(int) (100f * scale)];
preCalcNormalsNeg = new SimpleVector[(int) (100f * scale)];
int end = preCalcNormals.length;
for (int i = 0; i < end; i++) {
float height = -1f + (((float) i) / (end / 2f));
SimpleVector n = new SimpleVector(0, -1, 0);
SimpleVector n2 = new SimpleVector(0, -1, 0);
Matrix m = new Matrix();
Matrix m2 = new Matrix();
if (height <= 0) {
float val = (float) Math.sqrt((height + 1) * (Math.PI / 7f));
m.rotateAxis(ax, val);
m2.rotateAxis(ax, -val);
} else {
float val = (float) Math.sqrt((1 - height) * (Math.PI / 7f));
m.rotateAxis(ax, val);
m2.rotateAxis(ax, -val);
}
n.rotate(m);
n2.rotate(m);
preCalcNormals[i] = n;
preCalcNormalsNeg[i] = n2;
}

SimpleVector[] source = this.getSourceMesh();
lastHeight = new float[source.length];

for (int i = 0; i < source.length; i++) {
lastHeight[i] = 0;
}

return true;
}

public void update(float inc) {
degreeAdd += inc;
lastUpdate = inc;
}

public void apply() {
SimpleVector[] source = this.getSourceMesh();
SimpleVector[] dest = this.getDestinationMesh();

SimpleVector[] destNormals = this.getDestinationNormals();

int end = source.length;
int nEnd = preCalcNormals.length;

for (int i = 0; i < end; i++) {
SimpleVector s = source[i];
SimpleVector d = dest[i];
float sin = (float) Math.sin((degreeAdd + s.x + s.z) / damping);
d.set(s.x, s.y + sin * scale, s.z);

int iHeight = (int) ((sin + 1) * (nEnd / 2));

if (lastHeight[i] > sin) {
destNormals[i].set(preCalcNormalsNeg[iHeight]);
} else {
destNormals[i].set(preCalcNormals[iHeight]);
}

lastHeight[i] = sin;
}

water.touch();
if (realNormals) {
water.calcNormals();
}
float tr = lastUpdate / 333f;
water.getTextureMatrix().translate(tr, -tr, 0);
}
}

}

Title: Re: Water Shader Textures
Post by: AGP on January 12, 2011, 10:02:01 pm
Setup() isn't being called. Where should it be?
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 12, 2011, 10:06:36 pm
It's called by the engine, you don't have to call it yourself.
Title: Re: Water Shader Textures
Post by: AGP on January 12, 2011, 10:11:12 pm
Oh, I see that it's part of the IVertexController interface, sorry.

Is it defaulting to pre-calculated? And although the look on the shore is that of moving water, the water surface itself doesn't move (it's a smooth surface).
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 12, 2011, 10:56:25 pm
The last parameter in the constructor decides between precalc (false) and not (true). Default in the example is false. I don't get that part with shore and surface...if it moves on the shore, the surface moves. Just execute the example and you'll see. Maybe your lighting is done in a way, that it's just not noticable? Try to render wireframe mode and see how that looks like. You should see the movement.

Apart from that, consider to increase Config.glDynamicBatchSize (to 2000 or 4000 or...). It might help speed wise. I've modified the source code in my post too.
Title: Re: Water Shader Textures
Post by: AGP on January 12, 2011, 11:06:09 pm
You might be right about the lighting, I will look around. Thanks for the dynamic batch size suggestion, will do. If you happen to land a better water texture I'd love to have it too.
Title: Re: Water Shader Textures
Post by: AGP on January 13, 2011, 04:55:18 pm
Apply() has been throwing the exception too often, with the message "1500." What could that be?
Title: Re: Water Shader Textures
Post by: EgonOlsen on January 13, 2011, 05:19:40 pm
Some array out of bounds for the precalc normals array. Most likely due to rounding errors in the index calculation. Try to limit the array index with some Math.min and .max to 0-(length-1).