Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

libGDX - Custom shader for TextButton font

Tags:

I've been experimenting with distance field fonts as described by this article: https://github.com/libgdx/libgdx/wiki/Distance-field-fonts

Everything works fine when I'm just rendering the font, but when I try to use the shader on a TextButton, the button just becomes white, because it is applying the shader to the entire button and not just the text. I've looked around but I can't find any information on how to change the shader for just the text of a TextButton so here I am asking; how do I apply a cutom shader to just the text rendering of a TextButton?

Init Code:

textShader = new ShaderProgram(Gdx.files.internal("graphics/shaders/font/font.vert"),
             Gdx.files.internal("graphics/shaders/font/font.frag")); 
//exact same shaders as linked article
stage = new Stage();
stage.getBatch().setShader(textShader);
//Setting the shader for the stage will set the shader for everything in the stage,
//like my labels/buttons etc. This works fine for my labels as they are plain text,
//but my buttons become completely white.

init the rest of my labels, buttons, batches etc...

Render code:

Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL_COLOR_BUFFER_BIT);

render background/other stuff...

stage.act(delta);
stage.draw();

Fragment shader:

#ifdef GL_ES
precision mediump float;
#endif

uniform sampler2D u_texture;

varying vec4 v_color;
varying vec2 v_texCoord;

const float c_width = 0.5;
const float c_edge = 0.1;

const vec2 c_offset = vec2(0);

const float c_borderWidth = 0.5;
const float c_borderEdge = 0.5;

const vec3 c_color = vec3(0.7, 0.3, 0.1);
const vec3 c_outlineColor = vec3(0.3);

void main() {
    float distance = 1.0 - texture(u_texture, v_texCoord).a;
    float alpha = 1.0 - smoothstep(c_width, c_width + c_edge, distance);

    float distance2 = 1.0 - texture(u_texture, v_texCoord + c_offset).a;
    float outlineAlpha = 1.0 - smoothstep(c_borderWidth, c_borderWidth + c_borderEdge, distance2);

    float overallAlpha = alpha + (1.0 - alpha) * outlineAlpha;
    vec3 overallColour = mix(c_outlineColor, c_color, alpha/overallAlpha);

    gl_FragColor = vec4(overallColour, overallAlpha);
}

Vertex shader:

uniform mat4 u_projTrans;

attribute vec4 a_position;
attribute vec2 a_texCoord0;
attribute vec4 a_color;

varying vec4 v_color;
varying vec2 v_texCoord;

void main() {
    gl_Position = u_projTrans * a_position;
    v_texCoord = a_texCoord0;
    v_color = a_color;
}
like image 273
Charanor Avatar asked Apr 09 '16 12:04

Charanor


1 Answers

The distance field shader can't render other stuff normally. In order to use it in scene2D, you'll need to temporarily switch to this shader only when drawing Labels, so you will need to create a subclass of Label that can swap the shader. Maybe something like this:

public class CustomShaderLabel extends Label {
    private ShaderProgram shader;
    public CustomShaderLabel(CharSequence text, Skin skin) {
        super(text, skin);
    }

    public CustomShaderLabel(CharSequence text, Skin skin, String styleName) {
        super(text, skin, styleName);
    }

    public CustomShaderLabel(CharSequence text, Skin skin, String fontName, Color color) {
        super(text, skin, fontName, color);
    }

    public CustomShaderLabel(CharSequence text, Skin skin, String fontName, String colorName) {
        super(text, skin, fontName, colorName);
    }

    public CustomShaderLabel(CharSequence text, LabelStyle style) {
        super(text, style);
    }

    public ShaderProgram getShader() {
        return shader;
    }

    public void setShader(ShaderProgram shader) {
        this.shader = shader;
    }

    public void draw (Batch batch, float parentAlpha) {
        if (shader != null) batch.setShader(shader);
        super.draw(batch, parentAlpha);
        if (shader != null) batch.setShader(null);
    }
}

Keep in mind that this will cause extra batch flushes wherever there are labels, which could become an issue if you have an intricate hierarchy with many labels.

Since TextButton doesn't know to use your custom Label type, you'll either need to use a standard Button and add a Label to it, or perhaps copy-paste most of the TextButton class to create your own version (so you can use TextButton.TextButtonStyles to style your Label within the button).

like image 154
Tenfour04 Avatar answered Sep 28 '22 01:09

Tenfour04