ShapeRenderer produces pixelated shapes using LibGDX - java

When I use a ShapeRenderer, it always comes out pixelated. But if I draw the shape in photoshop with the same dimensions, it's very smooth and clean-looking.
My method is just as follows:
package com.me.actors;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.scenes.scene2d.Actor;
public class bub_actors extends Actor {
private ShapeRenderer shapes;
private Texture text;
private Sprite sprite;
public bub_actors(){
shapes = new ShapeRenderer();
text = new Texture(Gdx.files.internal("data/circle.png"));
sprite = new Sprite();
sprite.setRegion(text);
}
#Override
public void draw(SpriteBatch batch, float parentAlpha) {
batch.draw(sprite, 200, 200, 64, 64);
shapes.begin(ShapeType.FilledCircle);
shapes.filledCircle(50, 50, 32);
shapes.setColor(Color.BLACK);
shapes.end();
}
}
Here's an image of the output:
Any ideas as to why this happens? Is it possible to make the ShapeRenderer look like the image (so I don't have to create a SpriteBatch of different-colored circles...).

The difference is anti-aliasing that Photoshop applies to the image it generates. If you zoom in on the edges of the two circles, you'll see the anti-aliased one has some semi-black pixels around the edge, where the ShapeRenderer generated circle just shows pixels entirely on or off.
The Libgdx ShapeRenderer was designed for being a quick and simple way to get debugging shapes on the screen, it does not support anti-aliasing. The easiest way to get consistent anti-aliased rendering it to use a texture. (Its also possible with an OpenGL shader.)
That said, you do not have to create different sprites just to render different colored circles. Just use a white circle with a transparent background, and then render it with a color. (Assuming you want a variety of solid-colored circles).

Here is really simple way to achieve smooth & well-looking shapes without using a texture and SpriteBatch.
All you have to do is to render couple of shapes with slightly larger size and
lower alpha channel along with the first one.
The more passes the better result, but, of course, consider ppi of your screen.
...
float alphaMultiplier = 0.5f; //you may play with different coefficients
float radiusStep = radius/200;
int sampleRate = 3;
...
//do not forget to enable blending
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
shapeRenderer.begin(ShapeType.Filled);
//first rendering
shapeRenderer.setColor(r, g, b, a);
shapeRenderer.circle(x, y, radius);
//additional renderings
for(int i=0; i<sampleRate; i++) {
a *= alphaMultiplier;
radius += radiusStep;
shapeRenderer.setColor(r, g, b, a);
shapeRenderer.circle(x, y, radius);
}
shapeRenderer.end();
...
Here is a screenshot of what can you achieve.

If you're not teetering on the edge of losing frames, you can enable antialiasing in your launcher. You can increase the sample count for better results, but it's really diminishing returns.
LWJGL3 : config.setBackBufferConfig(8, 8, 8, 8, 16, 0, 2);
LWJGL2 : config.samples = 2;
GWT : config.antialiasing = true;
Android: config.numSamples = 2;
iOS : config.multisample = GLKViewDrawableMultisample._4X;

Related

libGDX Draw viewport only partly while cutting off the rest

This problem seemed very obvious for me to solve, but whatever I try, it doesn't work. What I'm trying to do is to incorporate a mini-version of my PlayScreen in a ScrollPane as a tutorial where you can read text and try it out immediately.
Because I didn't find any better solution to add this to the Table inside the ScrollPane, I edited the draw() method of the PlayScreen to take the ScrollPane.getScrollPercentY() and offset the camera of the PlayScreen accordingly.
What I want to do now is to only render only part of the viewport that would be normally visible in the real game. Subsequently, I want to be able to control the size and position of this "window".
I also want to be able to resize and move the content, while cutting off the edges that are not visible to the camera. This is what I tried inside the PlayScreenDraw:
public void draw(final float yOffset,
final int xTiles,
final int yTiles) {
view.getCamera().position.y = yTiles / 2f - yOffset * yTiles / HEIGHT; // HEIGHT = 800
view.getCamera().position.x = xTiles / 2f;
view.setWorldSize(xTiles, yTiles); //Do i even need to change the world size?
b.setProjectionMatrix(view.getCamera().combined);
b.begin();
...
b.end();
view.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
What this gives me, in terms of the picture above, is this
How do I need to change the viewport and/or the camera? Btw., this is how i set the two up:
cam = new OrthographicCamera();
cam.setToOrtho(false, WIDTH, HEIGHT); // WIDTH = 8, HEIGHT = 16
batch.setProjectionMatrix(cam.combined);
view = new FitViewport(WIDTH, HEIGHT, cam);
The Pixmap class can help you achieve what you want since you stated that you wanted to "cut off" the parts outside of the green selection box.
You need to render what the camera sees to an FBO and then get the pixmap from the FBO itself.
Framebuffer Objects are OpenGL Objects, which allow for the creation of user-defined Framebuffers. With them, one can render to non-Default Framebuffer locations, and thus render without disturbing the main screen.
-- OpenGL wiki
// Construct an FBO and keep a reference to it. Remember to dispose of it.
FrameBuffer fbo = new FrameBuffer(Format.RGBA8888, width, height, false);
public void render() {
//Start rendering to the fbo.
fbo.begin();
//From the camera's perspective.
batch.setProjectionMatrix(camera.combined);
batch.begin();
//Draw whatever you want to draw with the camera.
batch.end();
// Finished drawing, get pixmap.
Pixmap pixmap = ScreenUtils.getFrameBufferPixmap(0, 0, width, height);
//Stop drawing to your fbo.
fbo.end();
}
After getting the pixmap you can iterate through the pixels and set the alpha of the pixels outside your green selection window to 0 making them invisible or "cutting them off"

Libgdx: Drawing lots of particles

What is the best way to draw lots of particles (circles) moving in the background in LibGDX?
200 particles running in the background is what I can get out of my app. Anything above will get my app to stutter. Ive actually tested an App where it's possible to run up to 200.000 particles in the background without having to sacrifice fps. This is my Game class in short:
public Array<Particles> particlesArray;
SpriteBatch batch;
OrthographicCamera camera;
Texture sParticlesTexture;
public void create(){
camera = new OrthographicCamera();
camera.setToOrtho(false, 1080, 1920);
batch = new SpriteBatch;
Pixmap pixmap = new Pixmap(Particles.MAX_PARTICLE_RADIUS*2, Particles.MAX_PARTICLE_RADIUS*2, Pixmap.Format.RGBA4444);
pixmap.setColor(Color.WHITE);
pixmap.fillCircle(pixmap.getWidth() / 2, pixmap.getHeight() / 2, Particles.MAX_PARTICLE_RADIUS);
sParticlesTexture = new Texture(pixmap);
pixmap.dispose();
size = random(2, Particles.MAX_PARTICLE_RADIUS+1);
for(int i=0; i<200; i++){
particlesArray.add(new Particles(random(size, width-size),
random(0, height),
0,
random(0.15f*height, 0.25f*height)*0.15f*size,
size));
}
public void render(float deltaTime){
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//update camera and draw in camera
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
drawFallingObjects(particlesArray, batch);
batch.end()
moveParticles(particlesArray, deltaTime);
}
public <T extends Objects> void drawFallingObjects(Array<T> objects, SpriteBatch batch){
for(T item: objects){
item.draw(batch);
}
}
public void moveParticles(Array<Particles> particlesArray, float deltaTime){
for(Particles item: particlesArray){
size = random(2, Particles.MAX_PARTICLE_RADIUS+1);
item.move(deltaTime);
//creating particles if out of scale
if(item.y+item.mDiameter<0){
item.x = random(size, width-size);
item.y = height+20;
item.vy = random(0.15f*height, 0.25f*height)*0.15f*size;
item.mDiameter = size;
}
}
}
And this my Particles class:
import com....sParticlesTexture;
public class Particles{
public static int MAX_PARTICLE_RADIUS = 4;
public Particles(float x, float y, float vx, float vy, float mDiameter){
super(x, y, vx, vy, mDiameter);
radius = mDiameter/2;
}
#Override
public void draw(SpriteBatch batch){
batch.draw(sParticlesTexture, x-radius, y-radius, mDiameter, mDiameter);
}
#Override
public void move(float deltaTime){
y -= ceil(vy*deltaTime);
x += ceil(vx*deltaTime);
}
public void dispose() {
sParticlesTexture.dispose();
}
All Particles objects use one and the same texture. This improves a lot instead of creating hundred different textures. So what can be done now? I've googled a lot. What would help in my case? A Framebuffer, shader? And how should I implement these in my game? What about CPUSpriteBatch?
I also came across the particle system from LibGDX but it doesn't work differently than what I do.
First of all have a look at Particle effect which is much more efficient. https://github.com/libgdx/libgdx/wiki/2D-ParticleEffects
If you are not trying to get that kind of effect and want to use a lot of particles, you may not want to perform such large number of calculations in the Java. Rather use NDK and calculate the values from C/C++.
As Nabin said, libgdx has a particle system in place already which is already tuned to be efficient. Libgdx also has a tool called the 2D Particle editor which allows you to view and edit particles before you add them to your application. A guide on the Editor can be found on the libgdx site and gamedevelopment.blog.
From the code samples you provided, I think you could also possibly use a shader to create the same effect. The bonus to this is its all done on the GPU. Some example shaders can be found on Shadertoy and guide on shaders from GamesFromScratch or GLSL Shader Tutorial for Libgdx

How do I avoid "anti-aliasing effect" when creating a PGraphics object that's smaller than the window, then scale it up to the windows size?

When ran, the program displays a 3D sphere rendered in a P3D environment in the PGraphics object 'g', which is shown by taking the rendered PGraphics object and displaying it through the image() method in the main graphics context, which happens to be P2D.
The purpose of the program is to show how window size doesn't always correlate with render size. If you play an old Widows98 game in full screen, the game most likely will be rendered at 480p no matter what, so taking it into full screen just decreases the pixels per inch, plus making the image appear blurry. Which is fine, since fullscreen at 480p is preferred over windowed mode ( esp. if you're on 4K X_X )
the mouse's y position in the window changes the 3d camera's field of view, and the x position changes the rendering resolution of the P3D context used to display the sphere. Additionally, the P3D context is drawn in the main (P2D) context through the image() method, and is 'forcefully'. displayed at the size of the window. So if the P3D render resolution is smaller than the window, then it will start to look blurry and more pixelated, and if the render resolution is larger, you get a strange sharpening effect.
Now, my program works fine as it is, but. Another purpose of the program is shadowed by this issue, it's how the 'crispness' of the sphere fades as the render resolution decreases. You might say that it's clearly shown, but what I'm looking for is an image where there is no "anti-alias" effect going on. I want the image to preserve the pixels as the resolution gets smaller, so you can see the actual shape of the sphere at say, 50 x 50 pixels.
The noSmooth() method doesn't seem to work, and before you tell me to just do
g.loadPixels();
and then do a double for loop to draw the raw pixels to the 2d context. No, it's sloppy. I know that there must be some reason why this blurring is going on. I'm hoping that it's the image() method and that I should be using a different method or I should add another method before it to remove image blurring.
PGraphics g;
void setup(){
size(1000,1000,P2D);
frameRate(1000);
noSmooth();
}
void draw(){
background(200);
float res = map(mouseX,0,width,0.75,128);
if (res==0) {
res=1;
}
g = createGraphics((int)(width/res),(int)(height/res),P3D);
g.noSmooth(); // is this thing working?????
float cameraZ = ((height/2.0) / tan(PI*60.0/360.0));
g.beginDraw();
g.perspective(radians(map(mouseY,0,height,0.1,160)), width/height, cameraZ/10.0, cameraZ*10.0);
g.camera(g.width/2.0, g.height/2.0, (height/2.0) / tan(PI*30.0 / 180.0), g.width/2.0, g.height/2.0, 0, 0, 1, 0);
g.background(200);
g.translate(g.width/2 ,g.height/2);
g.sphere(100);
g.endDraw();
image(g, 0, 0, width, height); // this is where it all comes together
text("rendering resolution: "+g.width+" x "+g.height,0,14);
text("fps: "+frameRate,0,14*2);
}
Replace g.noSmooth() with ((PGraphicsOpenGL)g).textureSampling(2);
Credits go to Vallentin as I oddly enough had the same question with the P3D renderer
(Edit: This solution fixes the problem in the default renderer, but the OP is using the P2D renderer. The solution should be similar, so if somebody knows how to change the image interpolation mode in opengl, that's the answer.)
This is not really caused by anti-aliasing. It's caused by image scaling.
Also, it's much easier to help if you provide a MCVE, like this one:
PGraphics buffer;
void setup() {
size(1000, 1000);
buffer = createGraphics(100, 100);
buffer.noSmooth();
buffer.beginDraw();
buffer.background(255);
buffer.line(0, 0, width, height);
buffer.endDraw();
}
void draw() {
background(0);
image(buffer, 0, 0, mouseX, mouseY);
}
This code exhibits the same problem, but it's much easier to understand and work with.
Anyway, tracing through Processing's code, we can see that the image() function eventually calls the imageImpl() function in the PGraphics class here.
This function then draws your image using this code:
beginShape(QUADS);
texture(img);
vertex(x1, y1, u1, v1);
vertex(x1, y2, u1, v2);
vertex(x2, y2, u2, v2);
vertex(x2, y1, u2, v1);
endShape();
The endShape() function is then implemented in the renderer, specifically the PGraphicsJava2D class, which calls the drawShape() function here:
protected void drawShape(Shape s) {
if (fillGradient) {
g2.setPaint(fillGradientObject);
g2.fill(s);
} else if (fill) {
g2.setColor(fillColorObject);
g2.fill(s);
}
if (strokeGradient) {
g2.setPaint(strokeGradientObject);
g2.draw(s);
} else if (stroke) {
g2.setColor(strokeColorObject);
g2.draw(s);
}
}
Finally, that shows us that the Graphics2D.fill() function is being called, which is what actually draws your function.
The "problem" is that Graphics2D.fill() is scaling your image using an algorithm that causes some blurriness. We can consult the Java API and Google to figure out how to fix that though.
Specifically, this tutorial shows you how to set various rendering hints to change the scaling algorithm. We can use that in Processing like this:
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import processing.awt.PGraphicsJava2D;
PGraphics buffer;
void setup() {
size(1000, 1000);
buffer = createGraphics(100, 100);
buffer.noSmooth();
buffer.beginDraw();
buffer.background(255);
buffer.line(0, 0, width, height);
buffer.endDraw();
}
void draw() {
if (mousePressed) {
Graphics2D g2d = ((PGraphicsJava2D)g).g2;
g2d.setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
}
background(0);
image(buffer, 0, 0, mouseX, mouseY);
}
First, we import the classes we're going to need. Then we get to the Graphics2D instance in the renderer, and finally we call its setRenderingHint() function. I wrapped it in an if(mousePressed) so you could easily see the difference. When you click the mouse, interpolation is set to nearest neighbor, and you no longer see the blurriness.
Also notice that my code uses the g variable that's inherited from the PApplet superclass, so you would have to change your g variable so it's no longer hiding it.

Sprite image appears stretched and quality is poor, can't get in correct position GDXLib

I am quite new to programming so bear with me here...
I am making a 2d basic game just to practice programming in android studio and can't get my sprite to the correct position on the screen. Also when I draw the sprite it appears stretched and the quality is very poor. Any help is appreciated!
public class MyGdxGame extends ApplicationAdapter {
SpriteBatch batch;
Texture background;
Texture ball;
#Override
public void create () {
batch = new SpriteBatch();
background = new Texture("gamebackground.png");
ball = new Texture("ball2.png");
}
#Override
public void render () {
batch.begin();
batch.draw(background, 0,0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch.draw(ball, 0,0, Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
batch.end();
}
You need to keep the original width/height ratio:
instead of scaling it to half the screen size, define your scaling like that:
float scaleFactor = 2.0f;
batch.draw(ball, 0,0, ball.getWidth()*scaleFactor, ball.getHeight*scaleFactor);
If your image is "blurry", and you want the individual pixels to stay crisp, try loading the texture like that:
ball = new Texture("ball2.png");
ball.setFilter(TextureFilter.Nearest, TextureFilter.Nearest);
This prevents (default) linear interpolation when scaling the texture.

Polygon is created far away the specified position, LibGDX

I've been looking for solutions in google with no success, I'm creating a small library (just a wrapper) for Box2D in LibGDX and I'm drawing a texture for each body, taking as base the Body.getPosition() vector, however, I see polygon's getPosition() is different respect to CircleShapes and the walls (which were created with setAsBox() method).
Here's an image:
http://i.stack.imgur.com/NzooG.png
The red points are the center of mass, the cyan circles are the geometric center (right?) given by body.getPosition(), as you can see I can adapt the texture to the body in terms of position, rotation and scale but this does not happen with polygons (except the ones made with setAsBox()).
Basically, what I want is to get the cyan circle in the AABB centre of the regular polygons. here's a runnable example:
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.math.Vector2;
public class MyGdxGame extends ApplicationAdapter {
Tabox2D t;
float w ,h;
#Override
public void create () {
w = Gdx.graphics.getWidth();
h = Gdx.graphics.getHeight();
t = Tabox2D.getInstance();
t.debug = true;
t.newBall("d", 100, 200, 25);// Ball.
t.newBox("s", 10, 10, w - 20, 50);// Floor.
t.newHeptagon("d", new Vector2(200, 200), 40);
}
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
t.update(0);// Don't move anything just to see the cyan circle.
t.draw();
}
}
Tabox2D class is here: https://github.com/tavuntu/tabox2d
NOTE: this was tested with the last version of Android Studio and LibGDX.
Thanks in advance!
Looks to me like you are positioning the shapes on the centre point, instead of the body. The centre of the shapes should be 0,0, not center.x,y. Fixture positions are relative to the body.
OK, so I think I solved this maybe in an inelegant way. What I did was:
Create the polygons points around the origin
Get AABB center of polygon and centroid (not necessarily the same)
Translate points to make AABB center the shape center, so the image can be drawn respect to this and not the center of mass.
The code needs a lot of cleaning but it seems to work just well, updated code in github repo, thanks!
The change was basically translate points from the original centroid to AABB center:
for(int i = 0; i < pts.length - 1; i += 2) {
pts[i] -= aabbCenter.x;
pts[i + 1] -= aabbCenter.y;
}

Categories

Resources