2016-09-28 12 views
0

Я хотел нарисовать квадрат с OpenGL ES 2.0 и нанести на него динамический текст. Я пытаюсь совместить инструкции в сообщении this (который мне пришлось доставить до OpenGL ES 2.0) и четырех уроков Learn OpenGL ES Tutorial.Как нарисовать текст на квадрате с Android и OpenGL ES 2.0

У меня есть активность только с помощью GLSurfaceView:

public class TexturedSquareDrawActivity extends Activity { 

    private GLSurfaceView mGLView; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     mGLView = new MyGLSurfaceViewTexture(this); 
     setContentView(mGLView); 
    } 
} 

Мои GLSurfaceView только что создали визуализатор и устанавливает его:

public class MyGLSurfaceViewTexture extends GLSurfaceView { 
    private final MyGLRendererTexture mRenderer; 

    public MyGLSurfaceViewTexture(Context context){ 
     super(context); 

     // Create an OpenGL ES 2.0 context 
     setEGLContextClientVersion(2); 

     mRenderer = new MyGLRendererTexture(context); 

     // Set the Renderer for drawing on the GLSurfaceView 
     setRenderer(mRenderer); 
    } 
} 

Тогда я определяю TextureSquare класс как это:

public class TexturedSquare { 

    private final Context mContext; 
    private FloatBuffer vertexBuffer; 
    private ShortBuffer drawListBuffer; 

    private int mProgram; 

    private final String vertexShaderCode = 
      // This matrix member variable provides a hook to manipulate 
      // the coordinates of the objects that use this vertex shader 
      "uniform mat4 uMVPMatrix;" + 
        "attribute vec4 vPosition;" + 
        "attribute vec2 a_TexCoordinate;" + 
        "varying vec2 v_TexCoordinate;" + 
        "void main() {" + 
        // the matrix must be included as a modifier of gl_Position 
        // Note that the uMVPMatrix factor *must be first* in order 
        // for the matrix multiplication product to be correct. 
        " gl_Position = uMVPMatrix * vPosition;" + 
        " v_TexCoordinate = a_TexCoordinate;" + 
        "}"; 

    private final String fragmentShaderCode = 
      "precision mediump float;" + 
        "uniform sampler2D u_Texture;" + 
        "uniform vec4 vColor;" + 
        "varying vec2 v_TexCoordinate;" + 
        "void main() {" + 
//     " gl_FragColor = vColor;" + 
        " gl_FragColor = vColor * texture2D(u_Texture, v_TexCoordinate);" + 
        "}"; 
    private int mMVPMatrixHandle; 


    private int mPositionHandle; 
    private int mColorHandle; 


    // number of coordinates per vertex in this array 
    static final int COORDS_PER_VERTEX = 3; 

    private short drawOrder[] = {0, 1, 2, 0, 2, 3}; // order to draw vertices 


    private final float[] mColor; 

    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex 


    /** 
    * Store our model data in a float buffer. 
    */ 
    private final FloatBuffer mCubeTextureCoordinates; 

    /** 
    * This will be used to pass in the texture. 
    */ 
    private int mTextureUniformHandle; 

    /** 
    * This will be used to pass in model texture coordinate information. 
    */ 
    private int mTextureCoordinateHandle; 

    /** 
    * Size of the texture coordinate data in elements. 
    */ 
    private final int mTextureCoordinateDataSize = 2; 

    /** 
    * This is a handle to our texture data. 
    */ 
    private int mTextureDataHandle; 

    // S, T (or X, Y) 
    // Texture coordinate data. 
    // Because images have a Y axis pointing downward (values increase as you move down the image) while 
    // OpenGL has a Y axis pointing upward, we adjust for that here by flipping the Y axis. 
    // What's more is that the texture coordinates are the same for every face. 
    final float[] cubeTextureCoordinateData = 
      { 
        // Front face 
        0.0f, 0.0f, 
        0.0f, 1.0f, 
        1.0f, 0.0f, 
        0.0f, 1.0f, 
        1.0f, 1.0f, 
        1.0f, 0.0f, 
      }; 

    public TexturedSquare(Context context, final float[] squareCoords, final float[] color) { 
     mContext = context; 
     mColor = color; 

     // initialize vertex byte buffer for shape coordinates 
     ByteBuffer bb = ByteBuffer.allocateDirect(
       // (# of coordinate values * 4 bytes per float) 
       squareCoords.length * 4); 
     bb.order(ByteOrder.nativeOrder()); 
     vertexBuffer = bb.asFloatBuffer(); 
     vertexBuffer.put(squareCoords); 
     vertexBuffer.position(0); 

     // initialize byte buffer for the draw list 
     ByteBuffer dlb = ByteBuffer.allocateDirect(
       // (# of coordinate values * 2 bytes per short) 
       drawOrder.length * 2); 
     dlb.order(ByteOrder.nativeOrder()); 
     drawListBuffer = dlb.asShortBuffer(); 
     drawListBuffer.put(drawOrder); 
     drawListBuffer.position(0); 

     mCubeTextureCoordinates = ByteBuffer.allocateDirect(cubeTextureCoordinateData.length * 4) 
       .order(ByteOrder.nativeOrder()).asFloatBuffer(); 
     mCubeTextureCoordinates.put(cubeTextureCoordinateData).position(0); 

     linkShaderCode(); 
    } 

    private void linkShaderCode() { 
     int vertexShader = MyGLRendererTexture.loadShader(GLES20.GL_VERTEX_SHADER, 
       vertexShaderCode); 
     int fragmentShader = MyGLRendererTexture.loadShader(GLES20.GL_FRAGMENT_SHADER, 
       fragmentShaderCode); 

     // create empty OpenGL ES Program 
     mProgram = GLES20.glCreateProgram(); 

     // add the vertex shader to program 
     GLES20.glAttachShader(mProgram, vertexShader); 

     // add the fragment shader to program 
     GLES20.glAttachShader(mProgram, fragmentShader); 

     // creates OpenGL ES program executables 
     GLES20.glLinkProgram(mProgram); 
    } 

    public void draw(float[] mvpMatrix) { 
     // Add program to OpenGL ES environment 
     GLES20.glUseProgram(mProgram); 

     // get handle to vertex shader's vPosition member 
     mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); 

     // Enable a handle to the triangle vertices 
     GLES20.glEnableVertexAttribArray(mPositionHandle); 

     // Prepare the square coordinate data 
     // Tell OpenGL how to handle the data in the vertexBuffer 
     GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, 
       GLES20.GL_FLOAT, false, 
       vertexStride, vertexBuffer); 

     // get handle to fragment shader's vColor member 
     mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); 

     // Set color for drawing the square 
     // Pass the color to the shader 
     GLES20.glUniform4fv(mColorHandle, 1, mColor, 0); 

     // get handle to shape's transformation matrix 
     mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); 

     // Pass the projection and view transformation to the shader 
     GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0); 

     // Load the texture 
//  mTextureDataHandle = TextureHelper.loadTexture(mContext, R.drawable.background); 
     mTextureDataHandle = TextureHelper.loadText(mContext, ""); 

     mTextureUniformHandle = GLES20.glGetUniformLocation(mProgram, "u_Texture"); 
     mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgram, "a_TexCoordinate"); 


     GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle); 
     GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize, GLES20.GL_FLOAT, false, 
       0, mCubeTextureCoordinates); 

     // Set the active texture unit to texture unit 0. 
     GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 

     // Bind the texture to this unit. 
     GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle); 

     // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0. 
     GLES20.glUniform1i(mTextureUniformHandle, 0); 

     GLES20.glDrawElements(
       GLES20.GL_TRIANGLES, drawOrder.length, 
       GLES20.GL_UNSIGNED_SHORT, drawListBuffer); 

     // Disable vertex array 
     GLES20.glDisableVertexAttribArray(mPositionHandle); 
    } 
} 

Мой рендерер рисует два квадрата. Первый из них должен быть текстурированной:

public class MyGLRendererTexture implements GLSurfaceView.Renderer { 

    // mMVPMatrix is an abbreviation for "Model View Projection Matrix" 
    private final float[] mMVPMatrix = new float[16]; 
    private final float[] mProjectionMatrix = new float[16]; 
    private final float[] mViewMatrix = new float[16]; 
    private final Context mContext; 

    private TexturedSquare mTexturedSquare; 
    private TexturedSquare mTexturedSquare2; 

    static float squareCoords[] = { 
      -0.5f, 0.5f, 0.0f, // top left 
      -0.5f, -0.5f, 0.0f, // bottom left 
      0.5f, -0.5f, 0.0f, // bottom right 
      0.5f, 0.5f, 0.0f}; // top right 
    // Set color with red, green, blue and alpha (opacity) values 
    float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f}; 

    static float squareCoords2[] = { 
      -1.0f, 0.7f, 0.0f, // top left 
      -1.0f, 0.8f, 0.0f, // bottom left 
      -0.8f, 0.8f, 0.0f, // bottom right 
      -0.8f, 0.7f, 0.0f}; // top right 
    // Set color with red, green, blue and alpha (opacity) values 
    float color2[] = {0.11111111f, 0.26953125f, 0.52265625f, 1.0f}; 

    public MyGLRendererTexture(
      Context context) { 
     mContext = context; 
    } 


    public static int loadShader(int type, String shaderCode) { 

     // create a vertex shader type (GLES20.GL_VERTEX_SHADER) 
     // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) 
     int shader = GLES20.glCreateShader(type); 

     // add the source code to the shader and compile it 
     GLES20.glShaderSource(shader, shaderCode); 
     GLES20.glCompileShader(shader); 

     return shader; 
    } 

    @Override 
    public void onSurfaceCreated(GL10 unused, EGLConfig config) { 
     // Set the background frame color 
     GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 
     // initialize a triangle 
     // initialize a square 
     mTexturedSquare = new TexturedSquare(mContext, squareCoords, color); 
     mTexturedSquare2 = new TexturedSquare(mContext, squareCoords2, color2); 
    } 


    @Override 
    public void onSurfaceChanged(GL10 unused, int width, int height) { 
     GLES20.glViewport(0, 0, width, height); 

     float ratio = (float) width/height; 

     // this projection matrix is applied to object coordinates 
     // in the onDrawFrame() method 
     Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7); 
    } 

    @Override 
    public void onDrawFrame(GL10 unused) { 
     // Redraw background color 
     GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); 

     // Set the camera position (View matrix) 
     Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); 

     // Calculate the projection and view transformation 
     Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0); 

     mTexturedSquare.draw(mMVPMatrix); 
     mTexturedSquare2.draw(mMVPMatrix); 
    } 
} 

И, наконец, у меня есть вспомогательный класс задающие вспомогательные методы я использую в верхнем коде.

public class TextureHelper { 
    public static int loadTexture(final Context context, final int resourceId) { 
     final int[] textureHandle = new int[1]; 

     GLES20.glGenTextures(1, textureHandle, 0); 

     if (textureHandle[0] != 0) { 
      final BitmapFactory.Options options = new BitmapFactory.Options(); 
      options.inScaled = false; // No pre-scaling 

      // Read in the resource 
      final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options); 

      // Bind to the texture in OpenGL 
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]); 

      // Set filtering 
      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); 

      // Load the bitmap into the bound texture. 
      GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); 

      // Recycle the bitmap, since its data has been loaded into OpenGL. 
      bitmap.recycle(); 
     } 

     if (textureHandle[0] == 0) { 
      throw new RuntimeException("Error loading texture."); 
     } 

     return textureHandle[0]; 
    } 

    public static int loadText(final Context context, String text) { 
     final int[] textureHandle = new int[1]; 

     GLES20.glGenTextures(1, textureHandle, 0); 

     if (textureHandle[0] != 0) { 

      // Create an empty, mutable bitmap 
      Bitmap bitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_4444); 
       // get a canvas to paint over the bitmap 
      Canvas canvas = new Canvas(bitmap); 
      bitmap.eraseColor(0); 

      // get a background image from resources 
      // note the image format must match the bitmap format 
      Drawable background = context.getResources().getDrawable(R.drawable.background); 
      background.setBounds(0, 0, 256, 256); 
      background.draw(canvas); // draw the background to our bitmap 

      // Draw the text 
      Paint textPaint = new Paint(); 
      textPaint.setTextSize(32); 
      textPaint.setAntiAlias(true); 
      textPaint.setARGB(0xff, 0x00, 0x00, 0x00); 
      // draw the text centered 
      canvas.drawText(text, 16,112, textPaint); 

      // Bind to the texture in OpenGL 
      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]); 

      // Set filtering 
      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); 

      // Load the bitmap into the bound texture. 
      GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); 

      // Recycle the bitmap, since its data has been loaded into OpenGL. 
      bitmap.recycle(); 
     } 

     if (textureHandle[0] == 0) { 
      throw new RuntimeException("Error loading texture."); 
     } 

     return textureHandle[0]; 
    } 
} 

Но текстура рисуется для обоих треугольников, из которых состоит квадрат. Как я могу просто нарисовать текстуру один раз, расположенную горизонтально внутри квадрата?

Я понимаю, что квадрат рисуется двумя треугольниками. И я понимаю, что текстура размещена одинаково. Но я не знаю, как сообщить OpenGL, чтобы разместить эту текстуру только один раз в квадрате.

EDIT:

Я теперь редактировал координаты текстуры для:

final float[] cubeTextureCoordinateData = 
{ 
    -0.5f, 0.5f, 
    -0.5f, -0.5f, 
    0.5f, -0.5f, 
    0.5f, 0.5f 
} 

в результате этого:

First

Эти координаты:

-1.0f, 1.0f, 
-1.0f, -1.0f, 
1.0f, -1.0f, 
1.0f, 1.0f 

результат в этом:

Second

Эти координаты:

0.5f, -0.5f, 
0.5f, 0.5f, 
-0.5f, 0.5f, 
-0.5f, -0.5f 

результат в этом:

Third

И эти координаты:

1.0f, -1.0f, 
1.0f, 1.0f, 
-1.0f, 1.0f, 
-1.0f, -1.0f 

результат в этом:

Fourth

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

texture

Почему это делится на четыре части?

+1

Не уверен, что это проблема, но ваши координаты текстуры куба имеют противоположное вращение. Обычно вы хотите следовать правилу правой руки. Ваш первый набор вращается слева, а не справа. – Ben

+0

Да, может быть. Пожалуйста, взгляните на мое редактирование. Может быть, вы можете точно сказать, какие текстурные координаты я должен использовать для правильного вращения? – unlimited101

+1

Почему -1 к 1. Я думаю, вы хотите использовать от 0 до 1. Итак, {1, 0}, {1,1}, {0, 1}, {0, 0}. Важно помнить, что в opengl текстурные координаты идут от 0 до 1. – Ben

ответ

1

GLES устанавливает текстуры для повторения по умолчанию, поэтому вам нужно изменить параметр.

GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); 
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); 

Также учебники вы используете довольно хорошо здесь документация OpenGL ES, который является довольно полезным. https://www.khronos.org/opengles/sdk/docs/man/

+0

Спасибо, но со мной новые координаты. Я не вижу разницы между реализацией и не внедрением этого кода. Но, может быть, повторение текстуры повторяется, хотя я не вижу повторного рисования? Могу ли я сохранить эти параметры для проблем с производительностью? – unlimited101

+0

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

0

Чтобы нарисовать любой компонент пользовательского интерфейса в openGL, вам нужно создать холст и использовать его в openGL.

Bitmap textedBitmap = drawTextToBitmap(); 
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, textedBitmap, 0); 



private Bitmap drawTextToBitmap() { 
    // TODO Auto-generated method stub 

    Bitmap bitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_4444); 
    // get a canvas to paint over the bitmap 
    Canvas canvas = new Canvas(bitmap); 
    bitmap.eraseColor(android.graphics.Color.TRANSPARENT); 

    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); 



    TextPaint textPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); 
    Paint textPaint = new Paint(); 
    textPaint.setStyle(Paint.Style.FILL); 
    textPaint.setAntiAlias(true); 
    textPaint.setColor(Color.BLACK); 
    textPaint.setTextSize(10); 

    TextView tv = new TextView(context); 
    tv.setTextColor(Color.BLACK); 
    tv.setTextSize(10); 

    String text = "DEMO TEXT"; 

    tv.setText(text); 
    tv.setEllipsize(TextUtils.TruncateAt.END); 
    tv.setMaxLines(4); 
    tv.setGravity(Gravity.BOTTOM); 
    tv.setPadding(8, 8, 8, 50); 
    tv.setDrawingCacheEnabled(true); 
    tv.measure(MeasureSpec.makeMeasureSpec(canvas.getWidth(), 
      MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
      canvas.getHeight(), MeasureSpec.EXACTLY)); 
    tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight()); 



    LinearLayout parent = null; 
    if (bitmap != null && !bitmap.isRecycled()) { 
     parent = new LinearLayout(context); 

     parent.setDrawingCacheEnabled(true); 
     parent.measure(MeasureSpec.makeMeasureSpec(canvas.getWidth(), 
       MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
       canvas.getHeight(), MeasureSpec.EXACTLY)); 
     parent.layout(0, 0, parent.getMeasuredWidth(), 
       parent.getMeasuredHeight()); 

     parent.setLayoutParams(new LinearLayout.LayoutParams(
       LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 
     parent.setOrientation(LinearLayout.VERTICAL); 

     parent.setBackgroundColor(context.getResources().getColor(R.color.transpernt)); 


     parent.addView(tv); 

    } else { 
     // write code to recreate bitmap from source 
     // Write code to show bitmap to canvas 
    } 

    canvas.drawBitmap(parent.getDrawingCache(), 0, 0, textPaint); 

    tv.setDrawingCacheEnabled(false); 
    iv.setDrawingCacheEnabled(false); 
    parent.setDrawingCacheEnabled(false); 

    return bitmap; 

} 

 Смежные вопросы

  • Нет связанных вопросов^_^