Gradient màu văn bản


8

Những gì tôi thực sự cố gắng để đạt được: Tôi muốn vẽ văn bản với màu dọc gradient. Tôi đã tìm thấy giải pháp này , nhưng nó không phù hợp với tôi, vì nó có hình vuông màu đen xung quanh phông chữ gradient trong trường hợp của tôi - không biết làm cách nào để loại bỏ nó, vì vậy tôi bắt đầu câu hỏi đơn giản (phần không liên quan) hiểu rõ hơn về vật lý của pha trộn và bộ đệm khung trong opengl và libgdx

Những gì tôi đã cố gắng để hiểu, không liên quan đến mục tiêu của tôi: Tôi có một kết cấu với một hình vuông màu trắng trên đó, tôi vẽ nó trên nền đỏ. Tôi đang cố gắng vẽ một hình vuông màu xanh lá cây lên trên cái màu trắng, hình vuông màu xanh lá cây che một phần cái màu trắng và một phần trên nền màu đỏ (xem hình bên dưới).

Ý định của tôi là: khu vực màu trắng, phía sau hình vuông màu xanh lá cây nên được sơn màu xanh lá cây, nhưng tất cả nền đỏ không nên bị ảnh hưởng và không thay đổi (màu đỏ như nó).

Tôi có thể làm cái này như thế nào?

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

Lý tưởng nhất, hình vuông màu xanh lá cây không nên trong suốt dù thế nào, chỉ nên chặn màu trắng nơi nó che giấu khu vực màu trắng.

Đầu ra tôi nhận được: ảnh chụp màn hình

Cập nhật : Tôi đánh dấu câu trả lời của @Xoppa là chính xác, vì nó giải quyết câu hỏi ban đầu của tôi với kết quả sau:

nhập mô tả hình ảnh ở đây


3
Bạn không thể sử dụng thông tin màu sắc để điều khiển pha trộn theo cách đó. Sự khác biệt duy nhất về các pixel trong bộ đệm là ở kênh G và B, sẽ là 1 cho hình vuông màu trắng và 0 cho nền, nhưng đó không phải là thứ bạn có thể sử dụng khi trộn trực tiếp. Một cách tốt hơn sẽ là vẽ hình chữ nhật màu xanh lá cây vào bộ đệm stpson , và sau đó kết xuất hình vuông màu trắng.
Bartek Banachewicz

Sẽ là một ý tưởng tốt để cập nhật ví dụ / câu hỏi của bạn để người khác có thể thấy những gì bạn thực sự cần
Luis Fernando Frontanilla

Có, cập nhật, cảm ơn
exenza

Bạn có thể gửi một câu hỏi mới với nhiều chi tiết hơn cho trường hợp sử dụng được cập nhật của bạn. (vd
Nicolas

Bạn nói đúng @Nicolas, tôi nên đăng một câu hỏi mới với trường hợp sử dụng trực tiếp
exenza

Câu trả lời:


6

Bạn thực sự có thể sử dụng một số loại mặt nạ để trộn nó bằng cách sử dụng một hình vuông. Vì vậy, trước tiên bạn có thể kết xuất văn bản vào bộ đệm stpson bằng cách sử dụng trình đổ bóng tùy chỉnh để loại bỏ các đoạn có giá trị alpha dưới một ngưỡng nhất định. Sau đó, bạn có thể kết xuất hình vuông bằng cách sử dụng chức năng stprint để chỉ ảnh hưởng đến các đoạn "được chạm" bởi văn bản. Lưu ý rằng điều này cũng liên quan đến nhiều cuộc gọi kết xuất và do đó cũng làm tăng độ phức tạp cho mã cuộc gọi của bạn.

Tuy nhiên, bạn nói rằng bạn thực sự chỉ muốn kết xuất văn bản bằng cách sử dụng gradient. Cho rằng bạn không cần cách tiếp cận phức tạp như vậy và chỉ có thể áp dụng gradient trong cùng một lệnh gọi kết xuất.

Khi bạn vẽ văn bản, bạn thực sự kết xuất nhiều hình vuông nhỏ, cho mỗi ký tự trong văn bản một hình vuông. Mỗi hình vuông này có một họa tiết được áp dụng có chứa ký tự trên nền trong suốt. Nếu bạn mở hình ảnh phông chữ (ví dụ: đây là mặc định ), thì bạn sẽ thấy hình ảnh nguồn này.

Giống như bạn có thể áp dụng một gradient cho một hình vuông bình thường, bạn cũng có thể áp dụng một gradient cho từng hình vuông riêng lẻ tạo nên văn bản. Có nhiều cách để làm điều đó. Những bộ đồ tốt nhất phụ thuộc vào trường hợp sử dụng. Ví dụ: nếu bạn cần một gradient ngang hoặc có văn bản nhiều dòng, thì bạn cần một số bước bổ sung. Vì bạn không chỉ định điều này, tôi sẽ giả định rằng bạn muốn áp dụng một gradient dọc trên một dòng văn bản:

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

Btw, để có câu trả lời tốt hơn, bạn nên bao gồm vấn đề thực tế mà bạn đang cố gắng giải quyết, thay vì tập trung vào những gì bạn nghĩ là giải pháp. Xem thêm: https://stackoverflow.com/help/asking .


Cám ơn vì đã chia sẻ. Vài câu hỏi tiếp theo: 1. đầu tiên cho vòng lặp giả sử lặp lại thông qua cái gọi là "trang kết cấu của glyphs"; Tôi nghĩ đó sẽ là số lượng ký tự trong chuỗi của tôi, nhưng nó thực sự có toàn bộ kết cấu png của phông chữ (đó là lý do tại sao bạn gọi iterator là "trang"?), Vì tôi, vòng lặp này luôn chỉ thực hiện các ký tự
exenza

2. vòng lặp thứ hai trong applyGradient. Tôi cho rằng, đó là nơi bạn đặt độ dốc (bạn đúng tôi muốn theo chiều dọc), nhưng tôi không chắc chắn cách thức hoạt động của nó liên quan đến số ma thuật index +=20(ví dụ: phông chữ giá trị này cụ thể hay tại sao lại là 20?) và hằng số SpriteBatch.CX(chúng có phải là các góc kết cấu không?). Là opengl tiêu chuẩn đỉnh màu?
exenza

3. về tối ưu hóa: là nó okey, nếu tôi di chuyển những thứ từ drawGradientđể các nhà xây dựng lớp bên trong, vì vậy nó không kêu gọi tất cả các bước vẽ (constructor sẽ được gọi là những người ở đâu đó trong create()phương pháp) và nghỉ trong drawGradientchỉ cache.draw(batch)return layoutdây chuyền?
exenza

1
1) Đó là số lượng tệp PNG mà phông chữ của bạn sử dụng. Mỗi tệp PNG yêu cầu một lệnh gọi vẽ riêng và các đỉnh tương ứng. Nó không phụ thuộc vào số lượng ký tự trong chính văn bản. 2) 4 góc x 5 thuộc tính trên mỗi góc (x, y, u, v, color) = 20. Đây là mã hóa cứng trong spritebatch (và nó là shader). 3) Tối ưu hóa là không cần thiết. Nó có thể hoạt động nếu bạn không làm gì khác với phông chữ, nhưng không có gì ngăn cản điều đó. Vì vậy, đó là một lỗi đang chờ để xảy ra. Có lẽ có nhiều cách tốt hơn để làm điều đó. Nhưng đó vẫn sẽ là một tối ưu hóa sớm không cần thiết.
Xoppa

4

Bạn có thể pha trộn giả bằng cách thực hiện một số phép toán ở đây những gì tôi nghĩ ra:

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

Và kết quả là: nhập mô tả hình ảnh ở đây

Và với:

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

nhập mô tả hình ảnh ở đây


Công cụ tuyệt vời, cảm ơn bạn Luis đã phản hồi. Câu hỏi của tôi cung cấp ví dụ khá sai về những gì tôi thực sự đang cố gắng làm. Vùng màu trắng có thể là bất kỳ hình dạng nào, ví dụ như hình tròn hoặc thậm chí phức tạp hơn, thật không may, không phải lúc nào cũng có thể điều chỉnh hình vuông
exenza

Các tiêu chí trong trường hợp sử dụng thực tế của bạn cho nơi bạn không muốn nó vẽ là gì? Bạn có thực sự chỉ muốn vẽ những pixel đã có màu trắng tinh khiết không? Hoặc nơi mà một số họa tiết cụ thể đã được vẽ? Thứ gì khác?
Tenfour04

Tôi sợ cách duy nhất tôi biết cách làm những gì bạn muốn là với shader hoặc pha trộn nâng cao, hãy thử điều tra về mặt nạ LibGDX vì tôi chưa bao giờ sử dụng bất kỳ cách nào trước đây
Luis Fernando Frontanilla

Tôi đã thêm bản cập nhật cho câu hỏi ban đầu, @ Tenfour04
exenza
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.