LibGDX ShapeRenderer.filledRect() Transparency

Hey guys,

I’m trying to draw a filled rectangle with a horizontal gradient that goes from a color to transparent. I’m using the ShapeRenderer.filledRect() method that takes x, y, width, height, as well as 4 corner colors to create a gradient:

shapeRenderer.filledRect(-25f, -25f, 50, 50, Color.RED, Color.CLEAR, Color.CLEAR, Color.RED);

What I would expect is a rectangle that’s red on the left, completely clear on the right, and a gradient between the two. Instead what I get is a rectangle that’s red on the right and completely black (and completely opaque, not transparent at all) on the right.

I’ve put together a little example using the default class created by LibGDX. Here I’m using a green transparency just to demonstrate:

package com.me.mygdxgame;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;

public class MyGdxGame implements ApplicationListener {
	private OrthographicCamera camera;
	private SpriteBatch batch;
	private Texture texture;
	private Sprite sprite;
	
	private ShapeRenderer shapeRenderer;
	
	Color greenClear = new Color(0, 1, 0, 0);
	
	@Override
	public void create() {		
		float w = Gdx.graphics.getWidth();
		float h = Gdx.graphics.getHeight();
		
		camera = new OrthographicCamera(100, 100);
		batch = new SpriteBatch();
		
		shapeRenderer = new ShapeRenderer();
		
		texture = new Texture(Gdx.files.internal("data/libgdx.png"));
		texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
		
		TextureRegion region = new TextureRegion(texture, 0, 0, 512, 275);
		
		sprite = new Sprite(region);
		sprite.setSize(100, 100);
		sprite.setOrigin(sprite.getWidth()/2, sprite.getHeight()/2);
		sprite.setPosition(-sprite.getWidth()/2, -sprite.getHeight()/2);
	}

	@Override
	public void dispose() {
		batch.dispose();
		texture.dispose();
	}

	@Override
	public void render() {		
		Gdx.gl.glClearColor(1, 1, 1, 1);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		
		batch.setProjectionMatrix(camera.combined);
		
		batch.begin();
		sprite.draw(batch);
		batch.end();
		
		shapeRenderer.setProjectionMatrix(camera.combined);
		
		shapeRenderer.begin(ShapeType.FilledRectangle);
		shapeRenderer.filledRect(-25f, -25f, 50, 50, Color.RED, greenClear, greenClear, Color.RED);
		shapeRenderer.end();
	}

	@Override
	public void resize(int width, int height) {
	}

	@Override
	public void pause() {
	}

	@Override
	public void resume() {
	}
}

Actually, I just realized that no transparency is drawn at all by ShapeRenderer. Changing the above code to the following results in a completely blue rectangle being drawn, despite having an alpha value of .5.

shapeRenderer.setProjectionMatrix(camera.combined);
		
		shapeRenderer.setColor(0, 0, 1, .5f);
		
		shapeRenderer.begin(ShapeType.FilledRectangle);
		shapeRenderer.filledRect(-25f, -25f, 50, 50);
		
		//shapeRenderer.filledRect(-25f, -25f, 50, 50, Color.RED, greenClear, greenClear, Color.RED);
		shapeRenderer.end();

In my actual program I’m displaying images with transparent gradients, so I know it’s at least possible- and if I can’t figure out how to do it using this method, I’ll just use another image. But I’ve looked through the source code of that filledRect() method, and it seems to be applying the opacity correctly… so why doesn’t it show up?

Is there something I’m missing? I’ve tried googling but I can’t find anything that seems to resemble my problem. Any help is very appreciated!

ShapeRenderer doesn’t enable transparency for you. So you need to do this:

Gdx.gl.glEnable(GL10.GL_BLEND);
Gdx.gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);

... render shapes ...

Gdx.gl.glDisable(GL10.GL_BLEND);

However, if you just want a simple gradient rectangle, it will be more performant to use SpriteBatch. This way you can include the rectangle in the same batch as the rest of your sprites and without incurring any state changes like above.

Here is a utility method you can use:

private static float[] verts = new float[20];

public static void drawGradient(SpriteBatch batch, TextureRegion tex, float x, float y,
		float width, float height, Color a, Color b, boolean horiz) {
	float ca = a.toFloatBits();
	float cb = b.toFloatBits();

	int idx = 0;
	verts[idx++] = x;
	verts[idx++] = y;
	verts[idx++] = horiz ? ca : cb; // bottom left
	verts[idx++] = tex.getU(); //NOTE: texture coords origin is top left
	verts[idx++] = tex.getV2();

	verts[idx++] = x;
	verts[idx++] = y + height;
	verts[idx++] = ca; // top left
	verts[idx++] = tex.getU();
	verts[idx++] = tex.getV();

	verts[idx++] = x + width;
	verts[idx++] = y + height;
	verts[idx++] = horiz ? cb : ca; // top right
	verts[idx++] = tex.getU2();
	verts[idx++] = tex.getV();

	verts[idx++] = x + width;
	verts[idx++] = y;
	verts[idx++] = cb; // bottom right
	verts[idx++] = tex.getU2();
	verts[idx++] = tex.getV2();

	batch.draw(tex.getTexture(), verts, 0, verts.length);
}

Notice you need to pass a TextureRegion here. If you just want to make a gradient using an untextured rectangle, you need to sample a 1x1 white pixel from your sprite sheet. I’ve explained the process here:

The resulting code, using the “libgdx.png” texture:
http://www.java-gaming.org/?action=pastebin&id=390

Ohh thanks a lot, I didn’t realize you had to enable transparency, especially because transparent images worked automagically.

I’ll have to do some profiling to see if I can use the first method you mentioned. If not, I’ll probably just use a 100-pixel wide, 1-pixel tall image containing the transparency gradient and stretch that out to the height I need, since it’s just a basic horizontal gradient.

Thanks again for your help!

Did you see the code I posted? Your texture only needs to be 1x1 pixels. Ideally you would include this 1x1 white image inside a sprite sheet (alongside your GUI, fonts, game characters, etc) so that everything is batched in a single draw call, and no extra texture binds are needed.

You can then draw the 1x1 pixel with any size and colors you want.