Градиент цветной текст - PullRequest
       76

Градиент цветной текст

8 голосов
/ 17 февраля 2020

Чего я на самом деле пытаюсь достичь: Я бы хотел нарисовать текст с градиентным вертикальным цветом. Я нашел это решение, но оно мне не совсем подходит, так как в моем случае оно имеет черный квадрат вокруг градиентного шрифта - не знаю, как от него избавиться, поэтому я начал с простого ( нерелевантная часть) вопрос, чтобы лучше понять физику смешивания и кадрового буфера в opengl и libgdx

Что я пытался понять, не относящееся к моей цели: У меня есть текстура с белым на нем я рисую поверх красного фона. Я пытаюсь нарисовать зеленый квадрат поверх белого, зеленый квадрат частично покрывает белый и частично поверх красного фона (см. Рисунок ниже).

Мое намерение таково: белая область, которая находится за зеленым квадратом, должна быть окрашена в зеленый цвет, но весь красный фон не должен быть затронут и оставаться неизменным (красным как есть).

Как я могу это сделать?

package com.mygdx.game;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;

public class Game extends ApplicationAdapter {
    SpriteBatch batch;
    Texture img;
    private int height;
    private int width;
    private ShapeRenderer shapeRenderer;

    @Override
    public void create() {
        batch = new SpriteBatch();
        img = new Texture("white.png");
        width = Gdx.graphics.getWidth();
        height = Gdx.graphics.getHeight();
        shapeRenderer = new ShapeRenderer();
        shapeRenderer.setAutoShapeType(true);
    }

    @Override
    public void render() {
        Gdx.gl.glClearColor(1, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        batch.begin();
        batch.draw(img, width / 7, height / 4);
        batch.end();

        Gdx.gl.glEnable(GL20.GL_BLEND);
        Gdx.gl.glBlendFunc(GL20.GL_ONE, GL20.GL_SRC_COLOR);
        shapeRenderer.begin();
        shapeRenderer.set(ShapeRenderer.ShapeType.Filled);
        shapeRenderer.setColor(Color.GREEN);
        shapeRenderer.rect(width / 2 - 100, height / 4 - 50, 200, 200);
        shapeRenderer.end();
        Gdx.gl.glDisable(GL20.GL_BLEND);
    }

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

В идеале зеленый квадрат в любом случае не должен быть прозрачным, просто должен блокировать белый цвет там, где он скрывает белую область.

Вывод Я Получение: screenshot

Обновление : я отмечаю ответ @Xoppa как правильный, так как он решает мой первоначальный вопрос со следующим результатом:

enter image description here

Ответы [ 2 ]

6 голосов
/ 01 марта 2020

Вы действительно можете использовать какую-то маску, чтобы смешать ее, используя квадрат. Для этого вы можете сначала отобразить текст в буфере трафарета с помощью пользовательского шейдера, который отбрасывает фрагменты со значением альфа ниже определенного порога. После этого вы можете визуализировать квадрат, используя функцию трафарета, чтобы воздействовать только на фрагменты, «затронутые» текстом. Обратите внимание, что это включает в себя несколько вызовов рендеринга и, следовательно, увеличивает сложность вашего кода вызова.

Однако вы говорите, что на самом деле вы просто хотите рендерить текст с использованием градиента. Для этого вам не нужен такой сложный подход, и вы можете просто применить градиент внутри одного и того же вызова рендеринга.

Когда вы рисуете текст, вы фактически визуализируете много маленьких квадратов, для каждого символа в тексте один квадрат. Каждый из этих квадратов имеет нанесенную область текстуры, которая содержит символ на прозрачном фоне. Если вы откроете изображение шрифта (например, , это значение по умолчанию ), то вы увидите это исходное изображение.

Так же, как вы можете применить градиент к нормальному квадрату, вы также можете примените градиент к каждому из тех отдельных квадратов, которые составляют текст. Есть несколько способов сделать это. Какой из них лучше подходит, зависит от варианта использования. Например, если вам нужен горизонтальный градиент или многострочный текст, вам понадобятся дополнительные шаги. Поскольку вы не указали это, я предполагаю, что вы хотите применить вертикальный градиент к одной строке текста:

public class MyGdxGame extends ApplicationAdapter {
    public static class GradientFont extends BitmapFont {
        public static void applyGradient(float[] vertices, int vertexCount, float color1, float color2, float color3, float color4) {
            for (int index = 0; index < vertexCount; index += 20) {
                vertices[index + SpriteBatch.C1] = color1;
                vertices[index + SpriteBatch.C2] = color2;
                vertices[index + SpriteBatch.C3] = color3;
                vertices[index + SpriteBatch.C4] = color4;
            }
        }

        public GlyphLayout drawGradient(Batch batch, CharSequence str, float x, float y, Color topColor, Color bottomColor) {
            BitmapFontCache cache = getCache();
            float tc = topColor.toFloatBits();
            float bc = bottomColor.toFloatBits();
            cache.clear();
            GlyphLayout layout = cache.addText(str, x, y);
            for (int page = 0; page < cache.getFont().getRegions().size; page++) {
                applyGradient(cache.getVertices(page), cache.getVertexCount(page), bc, tc, tc, bc);
            }
            cache.draw(batch);
            return layout;
        }
    }

    SpriteBatch batch;
    GradientFont font;
    float topColor;
    float bottomColor;

    @Override
    public void create () {
        batch = new SpriteBatch();
        font = new GradientFont();
    }

    @Override
    public void render () {
        Gdx.gl.glClearColor(1, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        batch.begin();
        font.drawGradient(batch, "Hello world", 0, 100, Color.GREEN, Color.BLUE);
        batch.end();
    }

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

Кстати, чтобы получить лучшие ответы, вы должны включить реальную проблему вы пытаетесь решить, вместо того, чтобы сосредоточиться на том, что вы считаете решением. Смотрите также: { ссылка }.

4 голосов
/ 18 февраля 2020

Вы можете подделать смешивание, выполнив некоторую математику, вот что я придумал:

import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;

public class CalculatedMask extends Game {

    private SpriteBatch batch;          // The SpriteBatch to draw the white image
    private ShapeRenderer renderer;     // The ShapeRenderer to draw the green rectangle
    private Texture img;                // The texture of the image
    private Rectangle imgBounds;        // The bounds of the image
    private Rectangle squareBounds;     // The bounds of the square
    private float width;                // The width of the screen
    private float height;               // The height of the screen
    private float squareX;              // The x position of the green square
    private float squareY;              // The y position of the green square
    private float squareWidth;          // The width of the green square
    private float squareHeight;         // The height of the green square

    @Override
    public void create() {
        width = Gdx.graphics.getWidth();
        height = Gdx.graphics.getHeight();
        batch = new SpriteBatch();
        renderer = new ShapeRenderer();
        renderer.setAutoShapeType(true);

        img = new Texture("pixel.png");                 // A 1x1 white pixel png
        imgBounds = new Rectangle();                    // The white image bounds
        imgBounds.setPosition(width / 7f, height / 4f); // Position the white image bounds
        imgBounds.setSize(400f, 300f);                  // Scale the white image bounds
        calculateRectangle();
    }

    private void calculateRectangle() {
        // Here we define the green rectangle's original position and size
        squareBounds = new Rectangle();
        squareX = width / 2f - 300f;
        squareY = height / 4f - 50f;
        squareWidth = 200f;
        squareHeight = 200f;
        // Adjust green square x position
        squareBounds.x = MathUtils.clamp(squareX, imgBounds.x, imgBounds.x + imgBounds.width);
        // Adjust green square y position
        squareBounds.y = MathUtils.clamp(squareY, imgBounds.y, imgBounds.y + imgBounds.height);
        // Adjust green square width
        if (squareX < imgBounds.x) {
            squareBounds.width = Math.max(squareWidth + squareX - imgBounds.x, 0f);
        } else if (squareX + squareWidth > imgBounds.x + imgBounds.width) {
            squareBounds.width = Math.max(imgBounds.width - squareX + imgBounds.x, 0f);
        } else {
            squareBounds.width = squareWidth;
        }
        // Adjust green square height
        if (squareY < imgBounds.y) {
            squareBounds.height = Math.max(squareHeight + squareY - imgBounds.y, 0f);
        } else if (squareY + squareHeight > imgBounds.y + imgBounds.height) {
            squareBounds.height = Math.max(imgBounds.height - squareY + imgBounds.y, 0f);
        } else {
            squareBounds.height = squareHeight;
        }
    }

    @Override
    public void render() {
        // Clear previous frame
        Gdx.gl.glClearColor(1, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        // Draw the white image
        batch.begin();
        batch.draw(img, imgBounds.x, imgBounds.y, imgBounds.width, imgBounds.height);
        batch.end();
        // Draw the green rectangle without affecting background
        renderer.begin();
        renderer.setColor(Color.GREEN);

        // Debug so we can see the real green rectangle
        renderer.set(ShapeRenderer.ShapeType.Line);
        renderer.rect(squareX, squareY, squareWidth, squareHeight);
        // Draw the modified green rectangle
        renderer.set(ShapeRenderer.ShapeType.Filled);
        renderer.rect(squareBounds.x, squareBounds.y, squareBounds.width, squareBounds.height);

        renderer.end();
    }
}

И результаты: enter image description here

И с:

squareX = width / 2f + 100f;
squareY = height / 4f + 150f;

enter image description here

...