www.jpct.net

jPCT - a 3d engine for Java => Support => Topic started by: AGP on September 15, 2011, 11:17:47 pm

Title: Water Shader
Post by: AGP 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);
     }
}
Title: Re: Water Shader
Post by: EgonOlsen 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.
Title: Re: Water Shader
Post by: EgonOlsen 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.
Title: Re: Water Shader
Post by: 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.
Title: Re: Water Shader
Post by: MrM 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...
Title: Re: Water Shader
Post by: EgonOlsen 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).
Title: Re: Water Shader
Post by: AGP on September 16, 2011, 08:26:41 am
Do I move it on the normal map in the shader?
Title: Re: Water Shader
Post by: EgonOlsen 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.
Title: Re: Water Shader
Post by: AGP on September 16, 2011, 08:56:42 am
How do I have the GLSLShader class move the map's coordinates?
Title: Re: Water Shader
Post by: EgonOlsen on September 16, 2011, 09:20:43 am
No, you can do that in the shader code itself.
Title: Re: Water Shader
Post by: AGP 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?
Title: Re: Water Shader
Post by: EgonOlsen 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.
Title: Re: Water Shader
Post by: AGP 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();
}
Title: Re: Water Shader
Post by: EgonOlsen 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.
Title: Re: Water Shader
Post by: AGP 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.
Title: Re: Water Shader
Post by: AGP on September 19, 2011, 04:52:01 am
Hey, I seem to have found a nice shader. Problem is, for some reason it causes the world y to be flipped (or perhaps rotated 180 degrees around Z, I'm not sure which but -y is pointing down). The other problem is that nothing I do with the camera now changes the look of the water plane. I've added a billboarded plane to the world. If I move the camera in, the plane grows, if I move it out it shrinks. But the water plane remains unchanged. Very odd. Any insight would be appreciated.

UPDATE: This is the line that holds the water plane in place. But why would anyone write that? What's there to gain with holding it in place?
gl_Position = gl_ProjectionMatrix * gl_Vertex;
UPDATE 2: Apparently you need to write into gl_Position. But what can be passed that doesn't hold the water plane in place?

The vertex shader:
Code: [Select]
varying vec4 texcoord;
//varying vec4 position; //position is equal to texture coordinate :/ (one image plane)
//vec4 gl_ClipVertex;

void main() {
    texcoord = gl_MultiTexCoord0;
    // Transforming The Vertex
   
    //position = gl_Vertex;
   
    gl_Position = gl_ProjectionMatrix * gl_Vertex;
   
    //clipping test
    //vec3 screen_pos = gl_Position.xyz / gl_Position.w;
    //gl_ClipDistance[0] = screen_pos.x + 0.5; //required for clip planes in shader
}

The fragment:
Code: [Select]
//#version 120

uniform float timer;
uniform sampler2D colorMap;
uniform sampler2D reflectedColorMap;
uniform sampler2D stencilMap;
uniform vec3 water_color;
varying vec4 texcoord;

void main () {
    //vec3 water_color = vec3(0.0, 0.4, 0.2); //0.22,0.2 (now uniform through config)
    float in_water; //'boolean'

    vec4 color;
    vec4 stencil = texture2D(stencilMap,texcoord.rg);
   
    if (stencil.r > 0.05) in_water = 1.0;
    else in_water = 0.0;
   
    //distortion begin
    float x_scale = 1.0;
    float z_scale = 1.0;
   
    float used_timer = timer;
    float time_scale = 2.0; //2.0
    float size_scale = 1.6*6.3; //also dependent on radius
   
    if (stencil.r <= 0.15) {
        size_scale *= 6.0;
        time_scale *= 1.5;
    } else {
        size_scale *= stencil.r;
    }

    //timer needs to be 'in period'
    if (stencil.r >= 0.5) { //
        x_scale = ( 0.995 + (sin(2.0*time_scale*3.14159*used_timer - sin(0.5*size_scale*3.14159*stencil.g) + (size_scale*3.14159*stencil.g))/100.0)); //scales btw 0.995 and 1.005
    } //- sin(0.5*size_scale*3.14159*stencil.b)
    z_scale = ( 0.995 + (sin(sin(time_scale*3.14159*used_timer)  + 1.5*sin(0.8*size_scale*3.14159*stencil.b))/150.0));
    vec2 disturbed = vec2(x_scale*texcoord.x, z_scale*texcoord.y);
   
    vec4 reflection = texture2D(reflectedColorMap,disturbed.rg);
    //if (x_scale + z_scale > 2.00099) reflection *= 1.8; //to monitor effects...!
   
    time_scale = 3.0; //2.0
    size_scale = 2.4*6.3 * stencil.r;
   
    //timer needs to be 'in period'
    if (stencil.r >= 0.5) { //- sin(0.25*size_scale*3.14159*stencil.g)
        x_scale = ( 0.995 + (sin(2.0*time_scale*3.14159*used_timer - sin(0.25*size_scale*3.14159*stencil.g) + size_scale*3.14159*stencil.g)/100.0)); //scales btw 0.995 and 1.005
    }
    z_scale = ( 0.995 + (sin(sin(time_scale*3.14159*used_timer) + 1.5*sin(size_scale*3.14159*stencil.b))/100.0));
    vec2 disturbed_2 = vec2(x_scale*texcoord.x, z_scale*texcoord.y);
    //distortion end
   
    vec4 reflection_2 = texture2D(reflectedColorMap,disturbed_2.rg);
   
       
    reflection = (reflection+reflection_2)/2.0;
   
    //'refraction'(for all under-water)
    if (in_water > 0.05) {
        float look_up_range = 0.008; //0.005 //0.008
        //costs performance! (masking to avoid outside water look-ups, alternative another scene clipping)
        if (texture2D(stencilMap,vec2(disturbed.r + look_up_range, disturbed.g + look_up_range)).r > 0.001 &&
            texture2D(stencilMap,vec2(disturbed.r - look_up_range, disturbed.g - look_up_range)).r > 0.001 &&
            texture2D(stencilMap,vec2(disturbed.r, disturbed.g)).r > 0.001) {
            color = texture2D(colorMap,disturbed.rg); //drunken effect without stencil if
        } else {
            color = texture2D(colorMap,texcoord.rg);
        }
    } else {
        color = texture2D(colorMap,texcoord.rg);
    }
   
    //combine reflection and scene at water surfaces
    float reflection_strength = 0.5 * (stencil.r-0.1); //0.1, 0.14, 0.16, 0.17, 0.5
    float disable_refl = stencil.r-0.1;
   
    if (disable_refl <= 0.0) disable_refl = 0.0;
   
    //times inverted color.r for a stronger reflection in darker water parts!
    //used to be 8.0, 6.0, 3.5
    vec3 reflection_color = vec3(1.0, 1.0, 1.0);
    reflection_color =  reflection_strength * disable_refl * reflection.rgb;// * reflection.rgb * in_water * (1.0-(color.r*color.g*color.b));
   
    //more color in darker water in relation to the reflection
    //color darkened
    float difference = (reflection_color.r+reflection_color.g+reflection_color.b)/3.0 - (color.r + color.g + color.b)/5.5;
    if (difference < 0.0) difference = 0.0;
    vec3 regular_color = color.rgb * (1.0-in_water*reflection_strength) + (in_water * ((difference) * water_color));
   
    vec4 out_color = vec4(regular_color, 1.0) + vec4(reflection_color, 1.0);
   
    gl_FragColor = out_color;
   
    //debug section
    //gl_FragColor = texture2D(colorMap,texcoord.rg);
    //gl_FragColor = stencil;
    //gl_FragColor = vec4(vec3(stencil.r), 1.0);   
}
Title: Re: Water Shader
Post by: MrM on September 19, 2011, 04:01:40 pm
Can't seem to get it to work, I'm surely missing something, despite the textures.
Anyways, the flipping might be happening because the normal might not be pointing in the right direction, so try an abs() wherever the 'noise' or 'bump' of the water is controlled, so to speak...

Quote
The other problem is that nothing I do with the camera now changes the look of the water plane.
I'm not sure I quite understand... You mean it's like the water isn't moving? Or it's not bumpy/wavy?

And one more thing... I didn't find any information about lights in the shader. Is the water reflec/refrac taking in any lighting from your world? I'm asking because I'm interested in the result, lights aren't always necessary directly in shader code.
Title: Re: Water Shader
Post by: AGP on September 19, 2011, 06:11:17 pm
The water plane is always in the same position relative to the camera. The line that causes this is
Code: [Select]
gl_Position = gl_ProjectionMatrix * gl_Vertex;
I need to pass gl_Position something different, that allows it to move relative to the camera.
Title: Re: Water Shader
Post by: MrM on September 19, 2011, 07:07:03 pm
Ah, so even when the plane is bilboarded, the Object3D (plane) moves relative with the camera, but the "water texture" on it, does not?  :-\
If that's the case, I get your problem... And I'm not sure it's gonna be as simple as passing something in the position line...

In any case, how about this:

Add this:
uniform mat4 modelViewProjectionMatrix;

And in main:
vec3 position = gl_Vertex;
gl_position = modelViewProjectionMatrix * position;

Just like in EgonOlsen's...
(or you might need to make that one into: gl_ModelViewProjectionMatrix)


If not, I've no clue with just copying and pasting like this... I'll need to make it actually work on my side to do some experimenting.



Title: Re: Water Shader
Post by: AGP on September 19, 2011, 07:17:24 pm
No, sorry, it's two different planes. A billboarded one, for testing, and the water plane. As I move the camera, the billboarded plane moves intuitively (but the entire world's y is flipped so that this non-shader plane's -y points down). The water never moves at all. If I pass Config.farPlane, the water plane disappears, other than that the camera's position has no effect on it.
Title: Re: Water Shader
Post by: AGP on September 19, 2011, 09:20:03 pm
I'm not sure what's different between
Code: [Select]
gl_Position = gl_ProjectionMatrix * gl_Vertex;

and
Code: [Select]
vec3 position = gl_Vertex;
gl_position = modelViewProjectionMatrix * position;//modelViewProjectionMatrix HASN'T BEEN INITIALIZED, SO I GUESS IT'S gl_ProjectionMatrix
It's pretty much the same thing, isn't it?
Title: Re: Water Shader
Post by: MrM on September 20, 2011, 12:52:38 am
Yes, that's a a mistake on my part, I must've read something wrong. I really don't know how can I help you, since I don't really understand. The camera isn't supposed to do anything, and something might need to be called at runtime. I didn't manage to get it working, so I can't really try anything out. I think your best bet would be to wait for a response from one of the more experienced members of the forum...
Title: Re: Water Shader
Post by: EgonOlsen on September 29, 2011, 10:56:28 pm
Not really related to this particular problem, but i've just uploaded a preview release of 1.24 here: http://jpct.de/download/beta/jpctapi_124pre1.zip (http://jpct.de/download/beta/jpctapi_124pre1.zip)

It adds a few changes related to shaders that should make your life easier. The most noticable: It moves the GLSLShader-class into the core (just like jPCT-AE did before). It adds better logging output when shaders don't compile and it adds support for tangent vectors. If your shader needs proper tangent vectors, just add "attribute vec4 tangent;" to your vertex shader and make sure that you've assigned the shader before calling build().
Title: Re: Water Shader
Post by: AGP on September 30, 2011, 06:39:20 am
Awesome, thank you. Did you remember that addition to the docs? And while we're on the subject, why not add the "attribute vec4 tangent;" tidbit to them? Thanks again.
Title: Re: Water Shader
Post by: AGP on September 30, 2011, 06:41:58 am
By the way, not to be too pushy, but consider updating the included lwjgl with the current one. :- )
Title: Re: Water Shader
Post by: EgonOlsen on September 30, 2011, 09:46:45 pm
I've lost a bit track of LWJGL versions...albeit i use the latest in some projects but still 2.2 for jPCT itself. That has the advantage that it still works with older versions AND with newer ones. There's no reason not to bundle the latest version with jPCT though...and i usually do. I'll add it once this version goes final. I'll also fix the mentioned documentation flaws.

However, for now i've uploaded a version of the parallax mapping demo for jPCT-AE ported to desktop jPCT. It requires the 1.24pre-version to run properly. Maybe it helps with this tangent vector thing: http://jpct.de/download/net/misc/ParallaxMapping.zip (http://jpct.de/download/net/misc/ParallaxMapping.zip)