glow effect with ShapeRenderer (libgdx) - libgdx

I read about glow/particle effecta with sprites, which are working well.
Is there a simple way to create the similar blur effect with simple shaperender functionality like circles ?
#Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
batch.end();
shapeRenderer.begin(ShapeRenderer.ShapeType.Point);
shapeRenderer.setAutoShapeType(true);
drawCircle();
shapeRenderer.end();
batch.begin();
}
private void drawCircle() {
shapeRenderer.setColor(Color.WHITE);
shapeRenderer.set(ShapeRenderer.ShapeType.Filled);
// apply effect ??
shapeRenderer.circle(10,10,2);
}

You can't use textures with default ShapeRender.
There are several way to do this but the easiest is to use ShapeDrawer library. It adds some "ShapeRenderer like" capabilities to a SpriteBatch and more. see https://github.com/earlygrey/shapedrawer

Yes, you could create a "glow" effect, this could be achieved by drawing using decreasing alpha values past the boundaries.
Firstly, to define the glow-effect parameters, let's determine the maximum boundary of the glowing object, where the glow ends, as being auraSize for the size of the aura.
Next, we need to know the size of the solid portion of the object, the bit that doesn't glow, and let's call this objSize for the size of the object.
To draw this, we can use a simple for loop and some if statements:
private void drawCircle() {
shapeRenderer.set(ShapeRenderer.ShapeType.Filled);
//Draw circles upto the size of aura
for(int radius = 0; radius < auraSize; radius++) {
/*
* Checks if radius fits object size, sets colour to solid white if so
* Interpolates alpha value to 0 based on distance to aura covered by radius otherwise
*/
if(radius <= objSize) {
shapeRenderer.setColor(Color.WHITE);
} else {
shapeRenderer.setColor(new Color(1, 1, 1,
MathUtils.lerp(1, 0, (radius - objSize) / (auraSize / objSize))));
}
//Draws the circle at the current radius
shapeRenderer.circle(10, 10, radius);
}
}

Related

How to crop texture based on the other object it touched?

I have a line actor that might have other object that intersect with it, And I need to crop out that part.
Above is the image actor
this rectangle is also a image actor might appear randomly along the lines.
And this is the sample of the result I wanted to get. I need advice on how to achieve this with libgdx.
[EDIT]
As suggest I am trying to use fbo to draw into a buffer. Below is the code I am currently working on.
#Override
public void draw(Batch batch, float parentAlpha) {
fbo.begin();
getStage().getViewport().apply();
Gdx.gl.glClearColor(0f,0f,0f,0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.draw(trLine,position.x,position.y);
batch.flush();
fbo.end();
getStage().getViewport().apply();
batch.draw(fbo.getColorBufferTexture(),0,0);
}
I am able to buffer the draw into the buffer and draw it later but it happen to be different size. below is the code for creation and dispose of fbo. and it is outside of the draw loop.
fbo = new FrameBuffer(Pixmap.Format.RGBA8888,getStage().getViewport().getWidth(),getStage().getViewport().getHeight(),false,true);
[SOLVED FBO]
Below is the coding that have working fbo but the blending is not working as expected. Will keep trying until it works.
fbo.begin();
Gdx.gl.glClearColor(0f,0f,0f,0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(trLine,position.x,position.y);
batch.end();
int srcFunc = batch.getBlendSrcFunc();
int dstFunc = batch.getBlendDstFunc();
batch.enableBlending();
batch.begin();
batch.setBlendFunction(GL20.GL_ONE, GL20.GL_FUNC_REVERSE_SUBTRACT);
for(int i = 0 ; i < cropRectangles.size() ; i++){ batch.draw(cropTexture.get(i),cropRectangles.get(i).x,cropRectangles.get(i).y);
}
batch.end();
fbo.end();
getStage().getViewport().apply();
//reset blending before drawing the desire result
batch.begin();
batch.setBlendFunction(srcFunc, dstFunc);
batch.draw(fbo.getColorBufferTexture(),0,0);
batch.end();
But the output is not getting any blending effect. it is still a rectangle with filled white color.
[SOLVED FULL CODE]
I finally apply the equation correctly and able to reset it so it doesn't affect other things that I draw after this.
fbo.begin();
Gdx.gl.glClearColor(0f,0f,0f,0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(trLine,position.x,position.y);
batch.end();
int srcFunc = batch.getBlendSrcFunc();
int dstFunc = batch.getBlendDstFunc();
batch.enableBlending();
batch.begin();
batch.setBlendFunction(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glBlendEquation(GL20.GL_FUNC_REVERSE_SUBTRACT);
for(int i = 0 ; i < cropRectangles.size() ; i++){
batch.draw(cropTexture.get(i),cropRectangles.get(i).x,cropRectangles.get(i).y);
}
batch.end();
batch.flush();
fbo.end();
Gdx.gl.glBlendEquation(GL20.GL_FUNC_ADD);
getStage().getViewport().apply();
batch.begin();
batch.setBlendFunction(srcFunc, dstFunc);
batch.draw(fbo.getColorBufferTexture(),0,0);
batch.end();
You can use blend mode to achieve this.Your rectangle should have 2 parts.
Outer part and transparent part.
Outer part is your actual part going to be draw as usual.
Transparent part will be another rectangle with a full alpha and you should use blending for this part.
Visual Blending Tool
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
This mode clearing intersection area, it seems like correct mode .
You can easly find example usages of blending in libgdx.
SpriteBatch sb = (SpriteBatch)batch;
// draw our destination image
sb.draw(dst, 0, 0);
sb.end();
// remember SpriteBatch's current functions
int srcFunc = sb.getBlendSrcFunc();
int dstFunc = sb.getBlendDstFunc();
// Let's enable blending
sb.enableBlending();
sb.begin();
// blend them
sb.setBlendFunction(GL20.GL_ONE, GL20.ONE_MINUS_SRC_ALPHA);
sb.draw(src, 0, 0);
// Reset
sb.end();
sb.begin();
sb.setBlendFunction(srcFunc, dstFunc);
Additionally you must change blend equation as well.
And its not unique for sprite batch so we need to change for all game.
//Equation for effect you want
Gdx.gl.glBlendEquation(GL20.GL_FUNC_REVERSE_SUBTRACT);
//After draw you should also reset this
Gdx.gl.glBlendEquation(GL20.GL_FUNC_ADD);
Now we should take this drawn to FrameBufferObject because transparent area will show background color of your spritebatch.
If it's okey for you then it's done but you want to see another texture at this transparent area like background image or something then we have one more step.
You should read this article for whats the purpose of FBO(FrameBufferObject)
Frame Buffer from official wiki
You need to use this for merge your sprites and transparent areas so you can use those as whole image and see through background images from transparent area.
Maybe using second viewport or sprite batch would be easier and much more efficient according to your game.
One solution for this situation, fill that rectangle with solid background color(i mean make one rectangle inside that rectangle ring). It will cropped out that part.

Scale and transform in LibGDX

I have to do something like this in my Android App with LibGDX:
The ball is a Image that has a drag listener and added to a Stage:
ballImage.addListener(new DragListener() {
public void drag(InputEvent event, float x, float y, int pointer) {
ballImage.moveBy(x - ballImage.getWidth() / 2, y - ballImage.getHeight() / 2);
}
});
which works great. But I have a problem with scaling the ball. I can't scale the ball and maintain it's position and drag stop working normally (ball jump outside screen just on touch). I tried something like this:
massSlider.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) {
float x = ballImage.getX() - ballImage.getWidth() / 2;
float y = ballImage.getY() - ballImage.getHeight() / 2;
ballImage.moveBy(x, y);
ballImage.setScale(massSlider.getValue());
ballImage.moveBy(-x, -y);
}
});
Also, I was trying to use scaleBy()/setScale() and moveBy()/setPosition(), or to not move the ball at all before and after scaling, but nothing seems to work. What I do wrong?
Scaling is usually something that you want to (and easily can) avoid. It is nice to use scaling e.g. for actions to create a short pop out or pop in effect. But apart from that it's usually much better to adjust the size instead. So instead of using setScale(a) you do: setSize(a * unscaledWidth, a * unscaledHeight). Note that you will have to keep track of the unscaled width and height for this.

In libgdx, correct placement of ShapeRenderer begin and end

My question is whether I should place sRenderer.begin(ShapeType.Filled); and sRenderer.end(); outside of Shape? so they're not called for every Shape.draw()
Or is the approach below ok in terms of performance?
Snippet from one of my Shapes ..
private Body body; // initialized elsewhere
private float width = 1.0f;
private float height = 1.0f;
public void draw(ShapeRenderer sRenderer) {
sRenderer.begin(ShapeType.Filled);
sRenderer.setColor(1.0f, 0.0f, 0.0f, 1.0f);
sRenderer.identity();
sRenderer.translate(getBody().getPosition().x, getBody().getPosition().y, 0);
sRenderer.rotate(0.0f, 0.0f, 1.0f, (float) Math.toDegrees(getBody().getAngle()));
sRenderer.rect(-getWidth(), -getHeight(), getWidth() * 2, getHeight() * 2);
sRenderer.end();
}
In my Level class, theres a draw() method called every frame e.g
for (Body body : bodies) {
if (body.getUserData() instanceof Shape){
((Shape) body.getUserData()).draw(getShapeRenderer());
}
}
Each time you call begin(),identity(), translate(), rotate(), or etc., it will trigger a new flush to the GPU the next time you draw something with the shape renderer. So in your case, moving begin() and end() out of the loop won't have a very significant impact.
If this is a bottle neck, you could try calculating your rectangle corners independently from the shape renderer and submitting four lines to the shape renderer, using world space coordinates instead of local coordinates like you're doing now. Then move begin() and end() out of the loop like you suggested and benchmark the difference. I'm not sure which would be faster in your case. This would reduce draw calls but you'd be translating more points on the CPU as well.

Libgdx blending contrast issue in render();

In a Libgdx 1.x Stage, my goal is to apply a dark semi transparent mask on my screen except an area that should display the original screen.
My global render method:
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setShader(null);
update();
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
batch.begin();
darkener.draw(batch, 0.7f);
spotlight.draw(batch, 1f);
batch.end();
}
Both instances of objects Darkener and Spotlight are Sprites made from a pure white 4x4 texture:
spotlight = new Sprite(new Texture(Gdx.files.internal("data/ui/whitepixel.png")));
darkener = new Sprite(new Texture(Gdx.files.internal("data/ui/whitepixel.png")));
I give my Darkener object the size of the screen, and my Spotlight object the size of the area I want not to be darkened.
Here is the draw method of my object darkener:
public void draw(Batch batch, float alpha) {
darkener.draw(batch, alpha);
}
And the draw method of my Spotlight object:
public void draw(Batch batch, float alpha) {
batch.setBlendFunction(GL20.GL_DST_COLOR, GL20.GL_SRC_ALPHA);
spotlight.draw(batch, alpha);
batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
}
My problem is about blending:
With the parameters:
darkener.draw(batch, 0.7f);
spotlight.draw(batch, 1f);
The preserved (spotlight) area is darker than the original.
With the parameters:
darkener.draw(batch, 0.3f);
spotlight.draw(batch, 1f);
The preserved (spotlight) area is brighter (saturated) than the original.
With the paremeters:
darkener.draw(batch, 0.5f);
spotlight.draw(batch, 1f);
The preserved (spotlight) area is exactly like the original (so, as I want it). The problem is that I want to be able to set different values for my Darkener either < 0.5f or > 0.5f.
What I am doing wrong?
The blend function you're using for the spotlight takes the current pixel brightness and multiplies it by (1+alpha).
So if you darken to 0.7 of brightness (using a darkener alpha of 0.3), you want the spotlight to multiply the brightness by 1/0.7 = 1.429 so you should use a spotlight alpha of 0.429. So:
spotlightAlpha = 1/(1-darkenerAlpha) - 1; //assuming darkener RGB is black
The problem is if you darken to less than 0.5 brightness (darkner alpha > 0.5), because alpha cannot be greater than 1. You would have to run the spotlight with alpha 1 repeatedly until the brightness is above 0.5 and then one more time with the appropriate value.
You will lose color precision the darker you go so this method may not be suitable. Might want to consider drawing 8 dark boxes instead. And if you want the spotlight to be non-rectangular you can make its sprite inverted to fill in the middle rectangle.

Draw a triangle with a grid inside

I'm attempting to draw a triangle that has a grid inside. Much like graphing paper. I have the grid, and the triangle drawn, but I don't know how to remove the excess grid. The grid currently is drawn in a for loop until it gets to the end of the triangle's length. The problem is, this will draw a box 'grid' on top of the triangle. Can I draw on top of the excess to get rid of it? Should I draw the lines differently or draw multiple triangles inside the large one to simulate a grid? Assistance would be appreciated.
public class TrianglePanel extends JPanel{
final int pixelParameter = 20;
final int HEIGHT = 800, WIDTH = HEIGHT;
public TrianglePanel(){
setPreferredSize(new Dimension(WIDTH,HEIGHT));
setBackground(Color.white);
}
public void paint(Graphics g){
int [] xPoints = {0,WIDTH/2,0};
int [] yPoints = {HEIGHT,HEIGHT,HEIGHT/2};
g.drawPolygon(xPoints, yPoints, 3); // Draws the triangle
for (int i = pixelParameter; i < WIDTH/2; i += pixelParameter){
g.drawLine(i, HEIGHT, i, (HEIGHT/2)); // Draws vertical lines
g.drawLine(0, HEIGHT - i, (WIDTH/2), HEIGHT - i); // Draws horizontal lines
}
}
}
Custom painting is done by overriding the paintComponent() method, not the paint() method.
Don't forget to invoke super.paintComponent() at the start so the background gets painted.
Create a Polygon object. Then you can try using the Graphics.setClip(Shape) method before you attempt to draw the grid lines.