Help with getting height from a FFT ocean texture

Started by aero, July 07, 2022, 18:39:01

Previous topic - Next topic


Hello everyone,

I'm here looking for help with an issue I've been losing hair from.

I am currently making a game in Java & lwjgl and everything have so far gone OK but I feel like I've hit a wall with this problem I have.

I am trying to do bouyancy physics for my FFT ocean so for that I need to be able to get the height from a world position. Easy thing I thought since when I generate the ocean I have a texture "dy" that is pretty much the height of all the waves. Here is the code I currently have to get the height
public float getHeight(Vector3f worldPosition, float offset) {
        // Ignore y coordinate
        Vector3f wp = new Vector3f(worldPosition.x, 0, worldPosition.z);
        Vector2f uv = new Vector2f(wp.x, wp.z);
        float x = uv.x;
        float y = uv.y;

        int resolution = fft.getResolution();
        int size = resolution * resolution;

        // We create a floatbuffer to store all pixels from the DY texture
        FloatBuffer fb = BufferUtils.createFloatBuffer(size);
        glBindTexture(GL_TEXTURE_2D, fft.getDy().getId());
        glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_FLOAT, fb);
        // get the val from the x y coordinate
        float val = (x + y * resolution);

        // Keep it at a positive value
        val = Math.abs(val);
        // And keep the value within the size of the buffer 
        val %= size;
        // Get the raw height
        float r = fb.get((int)val);
        // Multiply it by 6 because thats the scale we use in the shader (Not uniform yet)
        float height = r * 6; 
        return height;

please ignore redundant code and bad practices, I will optimize everything later I promise

Some extra info

-It *works* when the world position is 0,0,0 and I get index 0 (duh)
-The resolution of the texture is 512 and is repeated * 50
-The ocean is based on the videos from Oreon Engine
-The ocean is scaled by 6000 and moved to -3000 to be centered around 0,0,0

After some more researching I found a youtube video where the he got the height from all three textures (dx, dy, dz). I tried this aswell but still feel kinda lost.
My brain have a hard time with Floatbuffers.

New functions
public float getHeight(Vector3f worldPosition) {
        return getWaterHeight(worldPosition);

    private float getWaterHeight(Vector3f position) {
        Vector3f p = new Vector3f(position);
        Vector3f displacement = getWaterDisplacement(p);

        p = new Vector3f(position);
        Vector3f d = new Vector3f(displacement);
        displacement = getWaterDisplacement(p.sub(d));
        p = new Vector3f(position);
        d = new Vector3f(displacement);
        displacement = getWaterDisplacement(p.sub(d));
        p = new Vector3f(position);
        d = new Vector3f(displacement);

        return getWaterDisplacement(p.sub(d)).y;

    private Vector3f getWaterDisplacement(Vector3f position) {
        Vector3f p = new Vector3f(position.x, 0, position.z);
        int index = (int)p.x + (int)p.z;
        if(index < 0 || index > (fft.getResolution() * fft.getResolution())) {
            System.err.println("Invalid index: " + index);
            return new Vector3f();

        float y = fbY.get(index);
        float x = fbX.get(index);
        float z = fbZ.get(index);
        return new Vector3f(x, y, z);

Also I store the textures inside floatbuffers every frame for testing

        glBindTexture(GL_TEXTURE_2D, fft.getDy().getId());
        glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_FLOAT, fbY);

        glBindTexture(GL_TEXTURE_2D, fft.getDx().getId());
        glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_FLOAT, fbX);

        glBindTexture(GL_TEXTURE_2D, fft.getDz().getId());
        glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_FLOAT, fbZ);

I appreciate any help or suggestions. Thanks!


I guess this is what you need to do:
Your ocean is 6000f x 6000f.  You repeat your height texture 50 times, that means it consists of 50x50 cells, which are 120f x 120f.

Suppose you are the red dot, and your world position is X: -2820f and Z: -2820f.
First add your offset which is 3000f. So X and Z becomes 180f. Use modulo % and division to get the correct index for your height texture.

private void blabla()
		Vector3f position = new Vector3f(-2820f, 0f, -2820f); // Position red dot
		float offset = 3000f; //We need a number between 0...6000, not -3000..3000
		float width = 6000f; //Ocean's size
		float depth = 6000f;
		float cellCount = 50f; //The height texture is repeated 50 times
		float cellWidth = width / cellCount; //Each cell is 120f
		float cellDepth = depth / cellCount;
		Vector3f correctPosition = new Vector3f(position.x + offset, 0f, position.z + offset);
		float cellX = correctPosition.x / cellWidth; //returns 1.5f
		float cellZ = correctPosition.z / cellDepth;			
		cellX %= 1f; //return 0.5f, now we can calculate our floatbuffer index
		cellZ %= 1f;
		int textureWidth = 512;	//Heightmap texture is 512 pixels by 512 pixels
		int textureHeight = 512;
		int rowsSkipped = (int) ((float)textureHeight * cellZ); //Z position is 0.5f, we need to skip 512*0.5f rows
		int xOffset = (int) ((float)textureWidth * cellX); //X position is 0.5f, we need to skip 512*0.5f pixels
		int index = (rowsSkipped * textureWidth) + xOffset;	//This should be the correct index for your floatbuffer

I hope this helps :)