[ANSWERED] OpenGL Texutre animating

In Opengl, how would you go about modifying the colour value of each pixel on a texture? Take, for example, in Minecraft, where the water pulses changing colour. Im not interested in the alogirthm behind changing the colour data for the pixels, I’m more inerested in how you would go about applying the moddified texture to an object.

I realise you could probably regenerate the texture each frame but that seems slow and inefficient. Is there a better way to do it?

To be honest I’d be very surprised if they were actually changing the colours in the texture itself, it’s more likely that the effect is implemented in the shader, possibly using alpha maps or something like that to combine the texture and the coloured pulses you mention. Mind you I’ve never played Minecraft so I could be completely wrong.

If you did want to modify a texture then glTexSubImage() can be used to modify all or part of an uploaded texture, but as you say if you were doing that every frame I expect it wouldn’t work too well.

Here’s the decompiled Minecraft water class from MCP. In here you can see them changing the individual pixel values. How would they go about applying those pixels to an object?

It is decompiled so it could be hard to read, but im just trying to show you how they implement pixel changing

package net.minecraft.src;

public class TextureWaterFX extends TextureFX
{
    /** red RGB value for water texture */
    protected float[] red = new float[256];

    /** green RGB value for water texture */
    protected float[] green = new float[256];

    /** blue RGB value for water texture */
    protected float[] blue = new float[256];

    /** alpha RGB value for water texture */
    protected float[] alpha = new float[256];
    private int tickCounter = 0;

    public TextureWaterFX()
    {
        super(Block.waterMoving.blockIndexInTexture);
    }

    public void onTick()
    {
        ++this.tickCounter;
        int var1;
        int var2;
        float var3;
        int var5;
        int var6;

        for (var1 = 0; var1 < 16; ++var1)
        {
            for (var2 = 0; var2 < 16; ++var2)
            {
                var3 = 0.0F;

                for (int var4 = var1 - 1; var4 <= var1 + 1; ++var4)
                {
                    var5 = var4 & 15;
                    var6 = var2 & 15;
                    var3 += this.red[var5 + var6 * 16];
                }

                this.green[var1 + var2 * 16] = var3 / 3.3F + this.blue[var1 + var2 * 16] * 0.8F;
            }
        }

        for (var1 = 0; var1 < 16; ++var1)
        {
            for (var2 = 0; var2 < 16; ++var2)
            {
                this.blue[var1 + var2 * 16] += this.alpha[var1 + var2 * 16] * 0.05F;

                if (this.blue[var1 + var2 * 16] < 0.0F)
                {
                    this.blue[var1 + var2 * 16] = 0.0F;
                }

                this.alpha[var1 + var2 * 16] -= 0.1F;

                if (Math.random() < 0.05D)
                {
                    this.alpha[var1 + var2 * 16] = 0.5F;
                }
            }
        }

        float[] var12 = this.green;
        this.green = this.red;
        this.red = var12;

        for (var2 = 0; var2 < 256; ++var2)
        {
            var3 = this.red[var2];

            if (var3 > 1.0F)
            {
                var3 = 1.0F;
            }

            if (var3 < 0.0F)
            {
                var3 = 0.0F;
            }

            float var13 = var3 * var3;
            var5 = (int)(32.0F + var13 * 32.0F);
            var6 = (int)(50.0F + var13 * 64.0F);
            int var7 = 255;
            int var8 = (int)(146.0F + var13 * 50.0F);

            if (this.anaglyphEnabled)
            {
                int var9 = (var5 * 30 + var6 * 59 + var7 * 11) / 100;
                int var10 = (var5 * 30 + var6 * 70) / 100;
                int var11 = (var5 * 30 + var7 * 70) / 100;
                var5 = var9;
                var6 = var10;
                var7 = var11;
            }

            this.imageData[var2 * 4 + 0] = (byte)var5;
            this.imageData[var2 * 4 + 1] = (byte)var6;
            this.imageData[var2 * 4 + 2] = (byte)var7;
            this.imageData[var2 * 4 + 3] = (byte)var8;
        }
    }
}

They are simply re-uploading the modified texture part into the terrain-texture-atlas on the graphicscard.
And because of performance reasons this happens only if the texture is really modified.
The textures are stored in a way, so they can be directly uploaded to the graphicscard.

The whole thing is very (very) easy to implement if you know how to upload a texture to opengl.

  • Longor1996

I would probably just make a texture atlas aka spritesheet and change the texture coordinates onTick()…

OK, so how does one go about uploading a texture to OpenGL

I understand you can regenerate the texture and add the glTexImage2d(); function with the pixels array, or is this something different?

Thats correct.
Minecraft simply uploads the updated texture parts per glTexImage2d();

Here is a little code snippet that does the trick (Made by myself a year ago, still using it):


	/**Updates an GLTexture with the given update Data. (Copys the given BufferedImage into the given texture at the given position.**/
	public static void updateTexture(GLTexture texture,BufferedImage bufferedImage,int x,int y){
		//Create Buffer
        	ByteBuffer imageData = ByteBuffer.allocateDirect(bufferedImage.getWidth() * bufferedImage.getHeight() * 4);
        	
        	//Get values
        	int width = bufferedImage.getWidth();
        	int height = bufferedImage.getHeight();
        	
        	//Setup moar Buffers
        	int rawPictureBuffer[] = new int[width * height];
        	byte outputPictureBuffer[] = new byte[width * height * 4];
        	
        	//Put the Picture into the Raw Picture Buffer
        	bufferedImage.getRGB(0, 0, width, height, rawPictureBuffer, 0, width);
        	
        	//Go trough the pictures RGBA data and copy it into  the output buffer
        	for (int k = 0; k < rawPictureBuffer.length; k++)
        	{
			//Get the Color Values
			int i1 = rawPictureBuffer[k] >> 24 & 0xff;
			int k1 = rawPictureBuffer[k] >> 16 & 0xff;
			int i2 = rawPictureBuffer[k] >> 8 & 0xff;
			int k2 = rawPictureBuffer[k] & 0xff;
			
			//Put into Output Buffer
			outputPictureBuffer[k * 4 + 0] = (byte)k1;
			outputPictureBuffer[k * 4 + 1] = (byte)i2;
			outputPictureBuffer[k * 4 + 2] = (byte)k2;
			outputPictureBuffer[k * 4 + 3] = (byte)i1;
		}
		
		//Put the Output Buffer into the ByteBuffer
		imageData.clear();
		imageData.put(outputPictureBuffer);
		imageData.position(0).limit(outputPictureBuffer.length);
		
		texture.bind();
		GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D,0,x,y,width,height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, imageData);
	}

GLTexture is a very small class that i use to store textures.
Shouldn’t be hard to make your own version of it.
Also, this code is not the most performant, i did never optimize it because i never use that many animated-textures.

Have fun coding.

  • Longor1996

Edit:
Almost forgot: You can easely replace the BufferedImage with a ByteBuffer/Byte-Array/Integer-Array or something.

Ahh yes, of course thank you. For some reason i thought glTeximage 2D had to be encapsulated in the textures generation code, thanks a lot!