Water Shader

Started by AGP, September 15, 2011, 11:17:47 PM

Previous topic - Next topic

AGP

I've decided to try my hand at a water shader again. Problem is a million visibility lists are being created and I'm running out of memory. Egon, I've e-mailed you the source and maps. Any help from anyone is appreciated.

The frame:

import java.awt.*;
import java.awt.event.*;

import com.threed.jpct.*;

public class WaterShaderTest extends Frame implements WindowListener {
     public World theWorld;
     private FrameBuffer buffer;
     public Object3D waterPlane;
     private Camera theCamera;
     private boolean gameLoop;
     private Canvas glCanvas;
     private NormalMapShader shader;

     public WaterShaderTest() {
this.setTitle("BR's");
theWorld = new World();
buffer = new FrameBuffer(1024, 768, FrameBuffer.SAMPLINGMODE_GL_AA_2X);
glCanvas = buffer.enableGLCanvasRenderer();
waterPlane = Loader.loadOBJ("WaterPlane.obj", null, 1f)[0];
waterPlane.build();
theWorld.addObject(waterPlane);
theCamera = theWorld.getCamera();
theCamera.setPosition(0, -20, 0);
theCamera.lookAt(waterPlane.getTransformedCenter());
try {
     shader = new NormalMapShader(theWorld, buffer, waterPlane);
}
catch (Exception e) {System.err.println("Problem loading shader: "+e.getMessage());System.exit(1);}
this.add(glCanvas);
this.addWindowListener(this);
this.setSize(1024, 768);
this.setVisible(true);
shaderLoop();
     }

     private void draw() {
buffer.clear(Color.RED);
theWorld.renderScene(buffer);
theWorld.draw(buffer);
buffer.displayGLOnly();
glCanvas.repaint();
     }

     private void shaderLoop() {
gameLoop = true;
while (gameLoop) {
     draw();
     Thread.yield();
}
this.dispose();
System.exit(0);
     }

     public void windowClosing(WindowEvent e) {
if (e.getWindow() == this)
     gameLoop = false;
     }
     public void windowClosed(WindowEvent e) {}
     public void windowOpened(WindowEvent e) {}
     public void windowActivated(WindowEvent e) {}
     public void windowDeactivated(WindowEvent e) {}
     public void windowIconified(WindowEvent e) {}
     public void windowDeiconified(WindowEvent e) {}

     public static void main(String[] args) {
new WaterShaderTest();
     }
}


The shader code:

import com.threed.jpct.*;
import java.nio.*;
import org.lwjgl.*;
import org.lwjgl.opengl.*;

public class NormalMapShader {//TAKEN FROM jpct's Wiki. I ADAPTED IT TO MAKE IT NOT BE A STANDALONE APP
     private World world;
     private FrameBuffer buffer;
     private Object3D object;
     private long time = System.nanoTime()/1000000L;
     private int fps = 0;

     public NormalMapShader(World world, FrameBuffer buffer, Object3D object) throws Exception {
Config.glTrilinear = true;
Config.specTerm = 10;
Config.specPow = 4;
this.world = world;
this.buffer = buffer;
this.object = object;

TextureManager.getInstance().addTexture("Maps\\OceanNightSmall.png", new Texture("Maps\\OceanNightSmall.png"));
TextureManager.getInstance().addTexture("Maps\\WaterNormalsSmall.png", new Texture("Maps\\WaterNormalsSmall.png"));

TextureInfo ti = new TextureInfo(TextureManager.getInstance().getTextureID("Maps\\OceanNightSmall.png"));
ti.add(TextureManager.getInstance().getTextureID("Maps\\WaterNormalsSmall.png"), TextureInfo.MODE_MODULATE);//MODULATE/BLEND (ORIGINALLY MOD)
object.setTexture(ti);

// object.setLighting(Object3D.LIGHTING_NO_LIGHTS);
// object.setCulling(false);
// object.setAdditionalColor(new java.awt.Color(128, 255, 230));

object.compile();
object.setSpecularLighting(true);
object.strip();
object.setRenderHook(new NormalMapping());
     }
}
class NormalMapping implements IRenderHook {//THE SHADER "FRAMEWORK"
     private String fragSource = Loader.loadTextFile("NewestWaterFragmentShader.glsl");//SHADER #1 WaterFrag
     private String vertexSource = Loader.loadTextFile("NewestWaterVertexShader.glsl");//SHADER #2 WaterVert
     private int prg = 0;
     private int fragShade = 0;
     private int vertShade = 0;
     private boolean init = false;
     private int locColor = 0;
     private int locNormal = 0;
     private int locRadius = 0;

     public void beforeRendering(int polyID) {
if (!init) {
     init();
}
ARBShaderObjects.glUseProgramObjectARB(prg);
ARBShaderObjects.glUniform1iARB(locColor, 0);
ARBShaderObjects.glUniform1iARB(locNormal, 1);
ARBShaderObjects.glUniform1fARB(locRadius, 0.0005f);
     }

     public void afterRendering(int polyID) {
ARBShaderObjects.glUseProgramObjectARB(0);
     }
     public void clear() {}//called if this IRenderHook implemention has been used to render current object-(part) but won't be used for the next one. Can be used to optimize some things. The idea is optimize setup/teardown work that would otherwise have to take place in every call to before- and afterRendering()
     public void onDispose() {
ARBShaderObjects.glDeleteObjectARB(fragShade);
ARBShaderObjects.glDeleteObjectARB(vertShade);
ARBShaderObjects.glDeleteObjectARB(prg);
     }

     public boolean repeatRendering() {
return false;
     }

     private void init() {
prg = ARBShaderObjects.glCreateProgramObjectARB();
fragShade = ARBShaderObjects.glCreateShaderObjectARB(ARBFragmentShader.GL_FRAGMENT_SHADER_ARB);

byte[] src = fragSource.getBytes();
ByteBuffer shader = BufferUtils.createByteBuffer(src.length);
shader.put(src);
shader.flip();

ARBShaderObjects.glShaderSourceARB(fragShade, shader);
ARBShaderObjects.glCompileShaderARB(fragShade);
ARBShaderObjects.glAttachObjectARB(prg, fragShade);

vertShade = ARBShaderObjects.glCreateShaderObjectARB(ARBVertexShader.GL_VERTEX_SHADER_ARB);

src = vertexSource.getBytes();
ByteBuffer shader2 = BufferUtils.createByteBuffer(src.length);
shader2.put(src);
shader2.flip();

ARBShaderObjects.glShaderSourceARB(vertShade, shader2);
ARBShaderObjects.glCompileShaderARB(vertShade);
ARBShaderObjects.glAttachObjectARB(prg, vertShade);

ARBShaderObjects.glLinkProgramARB(prg);

Logger.log("Shader compiled!", Logger.MESSAGE);

locColor = getLocation("colorMap");
locNormal = getLocation("normalMap");
locRadius = getLocation("invRadius");

init = true;
     }

     private int getLocation(String n) {
byte[] nb=n.getBytes();
ByteBuffer name = BufferUtils.createByteBuffer(nb.length+1);
name.put(nb);
name.put((byte)0);
name.flip();
return ARBShaderObjects.glGetUniformLocationARB(prg, name);
     }
}

EgonOlsen

I bet that you have some exception that you swallow somewhere (most likely in your drawing method/game loop) or otherwise, you wouldn't create a million visiblitity lists.

EgonOlsen

BTW: Why are you using an explicit implementation of the shader using gl commands and not the GLSLShader-class? Is anything wrong with it? Because it covers a lot of stuff that the example implementation in the wiki doesn't.

AGP

I started this last year. I honestly don't remember why I used that one, I'm just trying to continue what I started. Yesterday I made a little test case with the GLSLShader class which renders. I'm not sure how to make the water "move," though.

MrM

Sorry for not being helpful, but this just popped to mind so I though I might as well share it:

WaterTextureEffect()

You can check it out in the DOC...

EgonOlsen

Quote from: AGP on September 15, 2011, 11:35:30 PM
I started this last year. I honestly don't remember why I used that one, I'm just trying to continue what I started. Yesterday I made a little test case with the GLSLShader class which renders. I'm not sure how to make the water "move," though.
Just for moving, you can use a texture matrix (can be set on the Object3D IIRC).

AGP

Do I move it on the normal map in the shader?

EgonOlsen

It will be applied to the first texture stage IIRC. If you are in a shader anyway, you are free to alter the texture coordinates in any way you like. Maybe that's the better solution.

AGP

How do I have the GLSLShader class move the map's coordinates?

EgonOlsen

No, you can do that in the shader code itself.

AGP

But since you're saying that the GLSLShader class is more advanced, couldn't you add such a method to it?

EgonOlsen

No, that doesn't make any sense. The GLSLShader class has no clue about what you are doing in your shader. Once you are in shader world, you are on your own.

AGP

#12
Oh, you mean to modify the shader itself, as opposed to "framework," which is what I thought you meant. I guess I can do that. Any idea where it should go in the following shader code?

varying vec4 waterTex0;
varying vec4 waterTex1;
varying vec4 waterTex2;
varying vec4 waterTex3;
varying vec4 waterTex4;
uniform vec4 viewpos, lightpos;
uniform float time, time2;
//unit 0 = water_reflection
//unit 1 = water_refraction
//unit 2 = water_normalmap
//unit 3 = water_dudvmap
//unit 4 = water_depthmap

void main(void) {
    vec4 mpos, temp;
    vec4 tangent = vec4(1.0, 0.0, 0.0, 0.0);
    vec4 norm = vec4(0.0, 1.0, 0.0, 0.0);
    vec4 binormal = vec4(0.0, 0.0, 1.0, 0.0);

    mat4 mvp = gl_ModelViewProjectionMatrix;
    mat4 mtx = gl_TextureMatrix[0];

    temp = viewpos - gl_Vertex;
    waterTex4.x = dot(temp, tangent);
    waterTex4.y = dot(temp, binormal);
    waterTex4.z = dot(temp, norm);
    waterTex4.w = 0.0;

    temp = lightpos - gl_Vertex;
    waterTex0.x = dot(temp, tangent);
    waterTex0.y = dot(temp, binormal);
    waterTex0.z = dot(temp, norm);
    waterTex0.w = 0.0;

    mpos = mvp * gl_Vertex;

    vec4 t1 = vec4(0.0, -time, 0.0,0.0);
    vec4 t2 = vec4(0.0, -time2, 0.0,0.0);

    waterTex1 = gl_MultiTexCoord0 + t1;
    waterTex2 = gl_MultiTexCoord0 + t2;

    waterTex3 = mpos;

    gl_Position = ftransform();
}

EgonOlsen

You already have the different texture coordinates in the varyings waterTex0...4. You can simply modify them if needed.

AGP

Thanks. I'll try it out and if all goes well put up this example on the wiki.