Author Topic: Water Shader  (Read 11864 times)

Offline AGP

  • quad
  • ******
  • Posts: 1726
    • View Profile
Water Shader
« on: September 15, 2011, 11:17:47 pm »
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:
Code: [Select]
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:
Code: [Select]
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);
     }
}
« Last Edit: September 15, 2011, 11:24:42 pm by AGP »

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 12295
    • View Profile
    • http://www.jpct.net
Re: Water Shader
« Reply #1 on: September 15, 2011, 11:28:23 pm »
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.

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 12295
    • View Profile
    • http://www.jpct.net
Re: Water Shader
« Reply #2 on: September 15, 2011, 11:30:23 pm »
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.

Offline AGP

  • quad
  • ******
  • Posts: 1726
    • View Profile
Re: Water Shader
« Reply #3 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.

Offline MrM

  • byte
  • *
  • Posts: 36
    • View Profile
Re: Water Shader
« Reply #4 on: September 16, 2011, 03:03:43 am »
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...

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 12295
    • View Profile
    • http://www.jpct.net
Re: Water Shader
« Reply #5 on: September 16, 2011, 08:10:20 am »
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).

Offline AGP

  • quad
  • ******
  • Posts: 1726
    • View Profile
Re: Water Shader
« Reply #6 on: September 16, 2011, 08:26:41 am »
Do I move it on the normal map in the shader?

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 12295
    • View Profile
    • http://www.jpct.net
Re: Water Shader
« Reply #7 on: September 16, 2011, 08:47:38 am »
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.

Offline AGP

  • quad
  • ******
  • Posts: 1726
    • View Profile
Re: Water Shader
« Reply #8 on: September 16, 2011, 08:56:42 am »
How do I have the GLSLShader class move the map's coordinates?

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 12295
    • View Profile
    • http://www.jpct.net
Re: Water Shader
« Reply #9 on: September 16, 2011, 09:20:43 am »
No, you can do that in the shader code itself.

Offline AGP

  • quad
  • ******
  • Posts: 1726
    • View Profile
Re: Water Shader
« Reply #10 on: September 16, 2011, 10:06:25 am »
But since you're saying that the GLSLShader class is more advanced, couldn't you add such a method to it?

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 12295
    • View Profile
    • http://www.jpct.net
Re: Water Shader
« Reply #11 on: September 16, 2011, 10:25:36 am »
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.

Offline AGP

  • quad
  • ******
  • Posts: 1726
    • View Profile
Re: Water Shader
« Reply #12 on: September 16, 2011, 06:18:16 pm »
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?
Code: [Select]
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();
}
« Last Edit: September 16, 2011, 07:31:05 pm by AGP »

Offline EgonOlsen

  • Administrator
  • quad
  • *****
  • Posts: 12295
    • View Profile
    • http://www.jpct.net
Re: Water Shader
« Reply #13 on: September 16, 2011, 07:57:19 pm »
You already have the different texture coordinates in the varyings waterTex0...4. You can simply modify them if needed.

Offline AGP

  • quad
  • ******
  • Posts: 1726
    • View Profile
Re: Water Shader
« Reply #14 on: September 16, 2011, 08:35:06 pm »
Thanks. I'll try it out and if all goes well put up this example on the wiki.