Как написать текст или поместить изображение поверх трехмерного куба в OpenGL Java в Android (объект используется в дополнении цели) - PullRequest
0 голосов
/ 31 мая 2018

Я занимаюсь разработкой приложения на основе дополненной реальности с использованием EasyAR SDK в Android.Это рендеринг куба на верх изображения цели по умолчанию.Я хочу напечатать что-нибудь на вершине этого куба.И что же мне делать?Я новичок в OpenGL, пожалуйста, помогите.

Если я могу поместить изображение поверх этого куба, это тоже хорошо!Я просто хочу отобразить «Загрузка» в этом кубе, будь то изображение или текст, это не имеет значения!

Это текущая ситуация: Current Output

И мне нужно что-то вроде этого: Desired Result

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

import android.opengl.GLES20;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import cn.easyar.Vec2F;
import cn.easyar.Matrix44F;

public class BoxRenderer {
    private int program_box;
    private int pos_coord_box;
    private int pos_color_box;
    private int pos_trans_box;
    private int pos_proj_box;
    private int vbo_coord_box;
    private int vbo_color_box;
    private int vbo_color_box_2;
    private int vbo_faces_box;

    private String box_vert = "uniform mat4 trans;\n"
            + "uniform mat4 proj;\n"
            + "attribute vec4 coord;\n"
            + "attribute vec4 color;\n"
            + "varying vec4 vcolor;\n"
            + "\n"
            + "void main(void)\n"
            + "{\n"
            + "    vcolor = color;\n"
            + "    gl_Position = proj*trans*coord;\n"
            + "}\n"
            + "\n";

    private String box_frag = "#ifdef GL_ES\n"
            + "precision highp float;\n"
            + "#endif\n"
            + "varying vec4 vcolor;\n"
            + "\n"
            + "void main(void)\n"
            + "{\n"
            + "    gl_FragColor = vcolor;\n"
            + "}\n"
            + "\n";

    private float[] flatten(float[][] a) {
        int size = 0;
        for (int k = 0; k < a.length; k += 1) {
            size += a[k].length;
        }
        float[] l = new float[size];
        int offset = 0;
        for (int k = 0; k < a.length; k += 1) {
            System.arraycopy(a[k], 0, l, offset, a[k].length);
            offset += a[k].length;
        }
        return l;
    }

    private int[] flatten(int[][] a) {
        int size = 0;
        for (int k = 0; k < a.length; k += 1) {
            size += a[k].length;
        }
        int[] l = new int[size];
        int offset = 0;
        for (int k = 0; k < a.length; k += 1) {
            System.arraycopy(a[k], 0, l, offset, a[k].length);
            offset += a[k].length;
        }
        return l;
    }

    private short[] flatten(short[][] a) {
        int size = 0;
        for (int k = 0; k < a.length; k += 1) {
            size += a[k].length;
        }
        short[] l = new short[size];
        int offset = 0;
        for (int k = 0; k < a.length; k += 1) {
            System.arraycopy(a[k], 0, l, offset, a[k].length);
            offset += a[k].length;
        }
        return l;
    }

    private byte[] flatten(byte[][] a) {
        int size = 0;
        for (int k = 0; k < a.length; k += 1) {
            size += a[k].length;
        }
        byte[] l = new byte[size];
        int offset = 0;
        for (int k = 0; k < a.length; k += 1) {
            System.arraycopy(a[k], 0, l, offset, a[k].length);
            offset += a[k].length;
        }
        return l;
    }

    private byte[] byteArrayFromIntArray(int[] a) {
        byte[] l = new byte[a.length];
        for (int k = 0; k < a.length; k += 1) {
            l[k] = (byte) (a[k] & 0xFF);
        }
        return l;
    }

    private int generateOneBuffer() {
        int[] buffer = {0};
        GLES20.glGenBuffers(1, buffer, 0);
        return buffer[0];
    }

    public void init() {
        program_box = GLES20.glCreateProgram();
        int vertShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
        GLES20.glShaderSource(vertShader, box_vert);
        GLES20.glCompileShader(vertShader);
        int fragShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
        GLES20.glShaderSource(fragShader, box_frag);
        GLES20.glCompileShader(fragShader);
        GLES20.glAttachShader(program_box, vertShader);
        GLES20.glAttachShader(program_box, fragShader);
        GLES20.glLinkProgram(program_box);
        GLES20.glUseProgram(program_box);
        pos_coord_box = GLES20.glGetAttribLocation(program_box, "coord");
        pos_color_box = GLES20.glGetAttribLocation(program_box, "color");
        pos_trans_box = GLES20.glGetUniformLocation(program_box, "trans");
        pos_proj_box = GLES20.glGetUniformLocation(program_box, "proj");

        vbo_coord_box = generateOneBuffer();
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo_coord_box);
        float cube_vertices[][] = {
                /* +z */{1.0f / 2, 1.0f / 2, 0.01f / 2}, {1.0f / 2, -1.0f / 2, 0.01f / 2}, {-1.0f / 2, -1.0f / 2, 0.01f / 2}, {-1.0f / 2, 1.0f / 2, 0.01f / 2},
                /* -z */{1.0f / 2, 1.0f / 2, -0.01f / 2}, {1.0f / 2, -1.0f / 2, -0.01f / 2}, {-1.0f / 2, -1.0f / 2, -0.01f / 2}, {-1.0f / 2, 1.0f / 2, -0.01f / 2}
        };
        FloatBuffer cube_vertices_buffer = FloatBuffer.wrap(flatten(cube_vertices));
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, cube_vertices_buffer.limit() * 4, cube_vertices_buffer, GLES20.GL_DYNAMIC_DRAW);

        vbo_color_box = generateOneBuffer();
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo_color_box);
        int cube_vertex_colors[][] = {
                {255, 0, 0, 128}, {0, 255, 0, 128}, {0, 0, 255, 128}, {0, 0, 0, 128},
                {0, 255, 255, 128}, {255, 0, 255, 128}, {255, 255, 0, 128}, {255, 255, 255, 128}};
        ByteBuffer cube_vertex_colors_buffer = ByteBuffer.wrap(byteArrayFromIntArray(flatten(cube_vertex_colors)));
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, cube_vertex_colors_buffer.limit(), cube_vertex_colors_buffer, GLES20.GL_STATIC_DRAW);

        vbo_color_box_2 = generateOneBuffer();
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo_color_box_2);
        int cube_vertex_colors_2[][] = {
                {255, 0, 0, 255}, {255, 255, 0, 255}, {0, 255, 0, 255}, {255, 0, 255, 255},
                {255, 0, 255, 255}, {255, 255, 255, 255}, {0, 255, 255, 255}, {255, 0, 255, 255}};
        ByteBuffer cube_vertex_colors_2_buffer = ByteBuffer.wrap(byteArrayFromIntArray(flatten(cube_vertex_colors_2)));
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, cube_vertex_colors_2_buffer.limit(), cube_vertex_colors_2_buffer, GLES20.GL_STATIC_DRAW);

        vbo_faces_box = generateOneBuffer();
        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, vbo_faces_box);
        short cube_faces[][] = {
                /* +z */{3, 2, 1, 0}, /* -y */{2, 3, 7, 6}, /* +y */{0, 1, 5, 4},
                /* -x */{3, 0, 4, 7}, /* +x */{1, 2, 6, 5}, /* -z */{4, 5, 6, 7}};
        ShortBuffer cube_faces_buffer = ShortBuffer.wrap(flatten(cube_faces));
        GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, cube_faces_buffer.limit() * 2, cube_faces_buffer, GLES20.GL_STATIC_DRAW);
    }

    public void render(Matrix44F projectionMatrix, Matrix44F cameraview, Vec2F size) {
        float size0 = size.data[0];
        float size1 = size.data[1];

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo_coord_box);
        float height = size0 / 1000;
        float cube_vertices[][] = {
                /* +z */{size0 / 2, size1 / 2, height / 2}, {size0 / 2, -size1 / 2, height / 2}, {-size0 / 2, -size1 / 2, height / 2}, {-size0 / 2, size1 / 2, height / 2},
                /* -z */{size0 / 2, size1 / 2, 0}, {size0 / 2, -size1 / 2, 0}, {-size0 / 2, -size1 / 2, 0}, {-size0 / 2, size1 / 2, 0}};
        FloatBuffer cube_vertices_buffer = FloatBuffer.wrap(flatten(cube_vertices));
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, cube_vertices_buffer.limit() * 4, cube_vertices_buffer, GLES20.GL_DYNAMIC_DRAW);

        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
        GLES20.glEnable(GLES20.GL_DEPTH_TEST);
        GLES20.glUseProgram(program_box);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo_coord_box);
        GLES20.glEnableVertexAttribArray(pos_coord_box);
        GLES20.glVertexAttribPointer(pos_coord_box, 3, GLES20.GL_FLOAT, false, 0, 0);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo_color_box);
        GLES20.glEnableVertexAttribArray(pos_color_box);
        GLES20.glVertexAttribPointer(pos_color_box, 4, GLES20.GL_UNSIGNED_BYTE, true, 0, 0);
        GLES20.glUniformMatrix4fv(pos_trans_box, 1, false, cameraview.data, 0);
        GLES20.glUniformMatrix4fv(pos_proj_box, 1, false, projectionMatrix.data, 0);
        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, vbo_faces_box);
        for (int i = 0; i < 6; i++) {
            GLES20.glDrawElements(GLES20.GL_TRIANGLE_FAN, 4, GLES20.GL_UNSIGNED_SHORT, i * 4 * 2);
        }

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo_coord_box);
        float cube_vertices_2[][] = {
                /* +z */{size0 / 4, size1 / 4, size0 / 4}, {size0 / 4, -size1 / 4, size0 / 4}, {-size0 / 4, -size1 / 4, size0 / 4}, {-size0 / 4, size1 / 4, size0 / 4},
                /* -z */{size0 / 4, size1 / 4, 0}, {size0 / 4, -size1 / 4, 0}, {-size0 / 4, -size1 / 4, 0}, {-size0 / 4, size1 / 4, 0}};
        FloatBuffer cube_vertices_2_buffer = FloatBuffer.wrap(flatten(cube_vertices_2));
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, cube_vertices_2_buffer.limit() * 4, cube_vertices_2_buffer, GLES20.GL_DYNAMIC_DRAW);
        GLES20.glEnableVertexAttribArray(pos_coord_box);
        GLES20.glVertexAttribPointer(pos_coord_box, 3, GLES20.GL_FLOAT, false, 0, 0);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo_color_box_2);
        GLES20.glEnableVertexAttribArray(pos_color_box);

        GLES20.glVertexAttribPointer(pos_color_box, 4, GLES20.GL_UNSIGNED_BYTE, true, 0, 0);
        for (int i = 0; i < 6; i++) {
            GLES20.glDrawElements(GLES20.GL_TRIANGLE_FAN, 4, GLES20.GL_UNSIGNED_SHORT, i * 4 * 2);
        }
    }
}

1 Ответ

0 голосов
/ 01 июня 2018

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

В основном мы рисуем текст на растровом изображении и визуализируем это растровое изображениена 2D плоскости.Фон растрового изображения не будет отображаться (используя discard в фрагментном шейдере), если он является предопределенным цветом.

Поэтому сначала нам нужно установить один дополнительный атрибут вершины длякоординаты текстуры.Вот полная настройка, включающая вершины и координаты текстуры для простой 2D-плоскости:

//the geometry with texture coordinates
public int vbs[] = new int[2];

public void initSprite(){
    float vertices[] = {
            1.0f, -1.0f, 0.0f,  //triangle 1
            -1.0f, -1.0f, 0.0f,
            -1.0f, 1.0f, 0.0f,

            -1.0f, 1.0f, 0.0f,  //triangle 2
            1.0f, 1.0f, 0.0f,
            1.0f, -1.0f, 0.0f
    };
    float texcoords[] = {
            1.0f, 1.0f, 0.0f,  //triangle 1
            0.0f, 1.0f, 0.0f,
            0.0f, 0.0f, 0.0f,

            0.0f, 0.0f, 0.0f,  //triangle 2
            1.0f, 0.0f, 0.0f,
            1.0f, 1.0f, 0.0f
    };

    int triangle_count = 2;

    FloatBuffer vertex_pos_buffer;
    FloatBuffer tex_coord_buffer;

    int bytes_per_float = 4;

    //generate buffers on gpu
    GLES20.glGenBuffers(2, vbs,0);

    // Allocate a direct block of memory on the native heap,
    // size in bytes is equal to vertices.length * BYTES_PER_FLOAT.
    // BYTES_PER_FLOAT is equal to 4, since a float is 32-bits, or 4 bytes.
    vertex_pos_buffer = ByteBuffer.allocateDirect(vertices.length * bytes_per_float)
            // Floats can be in big-endian or little-endian order.
            // We want the same as the native platform.
            .order(ByteOrder.nativeOrder())
            // Give us a floating-point view on this byte buffer.
            .asFloatBuffer();

    //Transferring data from the Java heap to the native heap is then a matter of a couple calls:
    // Copy data from the Java heap to the native heap.
    vertex_pos_buffer.put(vertices)
            // Reset the buffer position to the beginning of the buffer.
            .position(0);

    //Bind the vertices buffer and give OpenGL the data
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbs[0]);
    GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, triangle_count * 3* 3 * bytes_per_float, vertex_pos_buffer, GLES20.GL_STATIC_DRAW);
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

    tex_coord_buffer = ByteBuffer.allocateDirect(texcoords.length * bytes_per_float)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer();
    tex_coord_buffer.put(texcoords).position(0);

    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbs[1]);
    GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, triangle_count * 3* 3 * bytes_per_float, tex_coord_buffer, GLES20.GL_STATIC_DRAW);
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}

Далее нам понадобится наша текстура.Мы загружаем фоновое изображение и рисуем поверх него желаемый текст. size - это размер шрифта, который мы хотим использовать, и он должен быть немного меньше высоты фонового растрового изображения. rgb - это значения цвета шрифта:

//the texture we gonna use during rendering
int tex = 0;

public void initTextTexture(String backgroundBitmapPath, String text, float size, int r, int g, int b){
    //load the bitmap
    Bitmap background = loadBitmapRGBA(backgroundBitmapPath);
    //check if image could load
    if(background == null){
        return;
    }

    android.graphics.Bitmap.Config bitmapConfig = background.getConfig();
    // set default bitmap config if none
    if(bitmapConfig == null) {
        bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
    }
    // resource bitmaps are imutable,
    // so we need to convert it to mutable one
    background = background.copy(bitmapConfig, true);


    Canvas canvas = new Canvas(background);
    // new antialised Paint
    Paint paint = new Paint();

    paint.setColor(Color.rgb(r, g, b));
    // text size in pixels
    paint.setTextSize(size);


    // draw text to the Canvas center
    Rect bounds = new Rect();
    paint.getTextBounds(text, 0, text.length(), bounds);
    //left
    int x = 1;
    //center
    int y = (background.getHeight() + bounds.height())/2;
    canvas.drawText(text, x, y, paint);


    //create a texture with the bitmap we just created
    //try to allocate texture on GPU
    int gl_map[] = new int[1];
    GLES20.glGenTextures(1, gl_map, 0);
    tex = gl_map[0];

    //bind texture
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex);
    //move the bitmap to the openGL texture
    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, background, 0);

    //set nearest filter
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
}

private Bitmap loadBitmapRGBA(String path){
    if(path == null){
        return null;
    }
    //replace this with your application/activity context
    AssetManager assetManager = GlobalContext.getAppContext().getAssets();
    InputStream istr = null;
    try {
        istr = assetManager.open(path);
    } catch (IOException e) {
        e.printStackTrace();
    }

    Rect outPadding = new Rect();
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inScaled = false;
    Bitmap image = BitmapFactory.decodeStream(istr, outPadding, options);

    return image;
}

Далее нам нужно нарисовать нашу геометрию с созданной нами текстурой, обратите внимание на glBindTexture :

public void drawTextSprite(){
    //program is the shader programm we gonna use to draw the 2d plane
    GLES20.glUseProgram(program);

    int locPosition = GLES20.glGetAttribLocation(program, "a_Position");
    int locTexcoord = GLES20.glGetAttribLocation(program, "a_TexCoord");

    int locTexture = GLES20.glGetUniformLocation(program, "tex_sampler");
    int locMVPMatrix = GLES20.glGetUniformLocation(program, "u_MVPMatrix");


    //bind the vertex data
    GLES20.glEnableVertexAttribArray(locPosition);
    GLES20.glEnableVertexAttribArray(locTexcoord);
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbs[0]);
    GLES20.glVertexAttribPointer(locPosition, 3, GLES20.GL_FLOAT, false, 0, 0);
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbs[1]);
    GLES20.glVertexAttribPointer(locTexcoord, 3, GLES20.GL_FLOAT, false, 0, 0);

    //bind texture
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex);
    // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
    GLES20.glUniform1i(locTexture, 0);

    //set up the mvp matrix
    float mvp[] = {
            1.0f, 0.0f, 0.0f, 0.0f, 
            0.0f, 1.0f, 0.0f, 0.0f,
            0.0f, 0.0f, 1.0f, 0.0f,
            0.0f, 0.0f, 0.0f, 1.0f
    };
    GLES20.glUniformMatrix4fv(locMVPMatrix, 1, false, mvp, 0);

    //draw 2 triangles
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 2*3);
}

Теперь нам просто нужен наш шейдер:

//vertex shader
uniform lowp mat4 u_MVPMatrix;
attribute lowp vec4 a_Position;
attribute lowp vec3 a_TexCoord;
varying lowp vec3 texc;
void main()
{
    texc = a_TexCoord;
    gl_Position = u_MVPMatrix * a_Position;
}

//fragment shader
uniform lowp sampler2D tex_sampler;
varying lowp vec3 texc;
void main()
{
    lowp vec3 color = texture2D(tex_sampler, texc.st).rgb;
    //test for the background color
    if(color.r == 1.0 && color.g == 0.0 && color.b == 1.0){
        discard; //get rid of the background
    }
    gl_FragColor = vec4(color.r, color.g, color.b, 1.0);
}

И чтобы все настроить, мы вызываем следующие две строки:

initSprite();
initTextTexture("img/FF00FF_TEXT_BG.png","Loading...", 20.0f, 255, 255, 255);

FF00FF_TEXT_BG isхранится в assets / img / и выглядит следующим образом.

FF00FF_TEXT_BG.png

Если мы вызываем drawTextSprite (); во время рендеринга мы должны получить что-то вроде этого:

final output with text

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

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

Надеюсь, это поможет.

...