渐变色文字

Gradient color text

我实际尝试实现的目标: 我想用渐变垂直颜色绘制文本。我找到了 this 解决方案,但它不太适合我,因为在我的例子中它的渐变字体周围有黑色方块 - 不知道如何摆脱它,所以我开始简单(不相关的部分)问题以更好地理解 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();
    }
}

理想情况下,绿色方块不应该是透明的,只是应该在它隐藏白色区域的地方阻挡白色。

我得到的输出:

更新: 我将@Xoppa 的回答标记为正确,因为它解决了我原来的问题,结果如下:

你可以通过做一些数学来伪造混合,这是我想出的:

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();
    }
}

结果是:

还有:

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

您确实可以使用某种遮罩来使用正方形混合它。为此,您可以首先使用自定义着色器将文本渲染到模板缓冲区,该着色器会丢弃 alpha 值低于特定阈值的片段。之后,您可以使用模板函数渲染正方形,以仅影响文本的片段 "touched"。请注意,这确实涉及多个渲染调用,因此也会增加调用代码的复杂性。

但是,您说您实际上只是想使用渐变来渲染文本。为此,您不需要这种复杂的方法,只需在同一个渲染调用中应用渐变即可。

当您绘制文本时,您实际上渲染了许多小方块,文本中的每个字符对应一个方块。每个正方形都应用了一个纹理区域,其中包含透明背景上的字符。如果您打开字体图像(例如 this is the default),您将看到此源图像。

就像您可以将渐变应用于普通方块一样,您也可以将渐变应用于构成文本的每个单独的方块。有多种方法可以做到这一点。哪种最适合取决于用例。例如,如果您需要水平渐变或多行文本,则需要一些额外的步骤。由于您没有指定这一点,我将假设您要在单行文本上应用垂直渐变:

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();
    }
}

顺便说一句,为了获得更好的答案,您应该包括您试图解决的实际问题,而不是专注于您认为的解决方案。另见:https://whosebug.com/help/asking.