Рендеринг нескольких пикселей с использованием OpenGL ES 2.0 - PullRequest
0 голосов
/ 06 июня 2018

С самого начала я очень новичок в мире OpenGL, но мне нужно использовать его для оптимизации рендеринга в Android.Я должен визуализировать блок или группу смежных пикселей в 2D-пространстве, используя OpenGL ES 2.0.Однако я нашел несколько подходящих решений ( здесь и здесь ), и я попробовал оба из них, но не могу достичь желаемого результата.

Во-первых, пиксель всегда находится в начале координат (в центре или {0, 0}), и я не могу переместить его оттуда.Я бы предпочел разместить его в верхнем левом углу экрана.

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

Подводя итог: Я просто хочу расположить пиксели смежно, например: первый пиксель, начиная с верхнего левого угла, второйодин должен быть сразу после первого пикселя на оси X и так далее.При достижении конечного поля экрана новый пиксель должен начинаться с новой строки (Y + 1).

Код, который я использую:

package point.example.point;

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class PointRenderer implements GLSurfaceView.Renderer {
  private float[] mModelMatrix = new float[16];
  private float[] mViewMatrix = new float[16];
  private float[] mProjectionMatrix = new float[16];
  private float[] mMVPMatrix = new float[16];
  private int mMVPMatrixHandle;
  private int mPositionHandle;

  float[] vertices = {
      0.0f, 0.0f, 0.0f
  };
  FloatBuffer vertexBuf;

  @Override
  public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
    vertexBuf = ByteBuffer.allocateDirect(vertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
    vertexBuf.put(vertices).position(0);

    // Set the background clear color to black.
    GLES20.glClearColor(0f, 0f, 0f, 1f);

    float eyeX = 0.0f;
    float eyeY = 0.0f;
    float eyeZ = 0.0f;

    float centerX = 0.0f;
    float centerY = 0.0f;
    float centerZ = -5.0f;

    float upX = 0.0f;
    float upY = 1.0f;
    float upZ = 0.0f;

    // Set the view matrix. This matrix can be said to represent the camera position.
    // NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and
    // view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose.
    Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);

    final String vertexShader =
        "uniform mat4 u_MVPMatrix;      \n"
            + "attribute vec4 a_Position;     \n"
            + "void main()                    \n"
            + "{                              \n"
            + "   gl_Position = u_MVPMatrix   \n"
            + "               * a_Position;   \n"
            + "   gl_PointSize = 10.0;       \n"
            + "}                              \n";

    final String fragmentShader =
        "precision mediump float;       \n"
            + "void main()                    \n"
            + "{                              \n"
            + "   gl_FragColor = vec4(1.0,    \n"
            + "   1.0, 1.0, 1.0);             \n"
            + "}                              \n";

    // Load in the vertex shader.
    int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);

    if (vertexShaderHandle != 0) {
      // Pass in the shader source.
      GLES20.glShaderSource(vertexShaderHandle, vertexShader);

      // Compile the shader.
      GLES20.glCompileShader(vertexShaderHandle);

      // Get the compilation status.
      final int[] compileStatus = new int[1];
      GLES20.glGetShaderiv(vertexShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

      // If the compilation failed, delete the shader.
      if (compileStatus[0] == 0) {
        GLES20.glDeleteShader(vertexShaderHandle);
        vertexShaderHandle = 0;
      }
    }

    if (vertexShaderHandle == 0) {
      throw new RuntimeException("Error creating vertex shader.");
    }

    // Load in the fragment shader shader.
    int fragmentShaderHandle = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

    if (fragmentShaderHandle != 0) {
      // Pass in the shader source.
      GLES20.glShaderSource(fragmentShaderHandle, fragmentShader);

      // Compile the shader.
      GLES20.glCompileShader(fragmentShaderHandle);

      // Get the compilation status.
      final int[] compileStatus = new int[1];
      GLES20.glGetShaderiv(fragmentShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

      // If the compilation failed, delete the shader.
      if (compileStatus[0] == 0) {
        GLES20.glDeleteShader(fragmentShaderHandle);
        fragmentShaderHandle = 0;
      }
    }

    if (fragmentShaderHandle == 0) {
      throw new RuntimeException("Error creating fragment shader.");
    }

    // Create a program object and store the handle to it.
    int programHandle = GLES20.glCreateProgram();

    if (programHandle != 0) {
      // Bind the vertex shader to the program.
      GLES20.glAttachShader(programHandle, vertexShaderHandle);
      // Bind the fragment shader to the program.
      GLES20.glAttachShader(programHandle, fragmentShaderHandle);
      // Bind attributes
      GLES20.glBindAttribLocation(programHandle, 0, "a_Position");
      // Link the two shaders together into a program.
      GLES20.glLinkProgram(programHandle);
      // Get the link status.
      final int[] linkStatus = new int[1];
      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
      // If the link failed, delete the program.
      if (linkStatus[0] == 0) {
        GLES20.glDeleteProgram(programHandle);
        programHandle = 0;
      }
    }

    if (programHandle == 0) {
      throw new RuntimeException("Error creating program.");
    }

    // Set program handles. These will later be used to pass in values to the program.
    mMVPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVPMatrix");
    mPositionHandle = GLES20.glGetAttribLocation(programHandle, "a_Position");

    // Tell OpenGL to use this program when rendering.
    GLES20.glUseProgram(programHandle);
  }

  @Override
  public void onSurfaceChanged(GL10 glUnused, int width, int height) {
    // Set the OpenGL viewport to the same size as the surface.
    GLES20.glViewport(0, 0, width, height);

    // Create a new perspective projection matrix. The height will stay the same
    // while the width will vary as per aspect ratio.
    final float ratio = (float) width / height;
    final float left = -ratio;
    final float right = ratio;
    final float bottom = -1.0f;
    final float top = 1.0f;
    final float near = 1.0f;
    final float far = 100.0f;

    Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
  }

  @Override
  public void onDrawFrame(GL10 glUnused) {
    GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

    Matrix.setIdentityM(mModelMatrix, 0);
    //Push to the distance - note this will have no effect on a point size
    Matrix.translateM(mModelMatrix, 0, 0.0f, 0.0f, -5.0f);
    Matrix.multiplyMV(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
    Matrix.multiplyMV(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);

    //Send the vertex
    GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, vertexBuf);
    GLES20.glEnableVertexAttribArray(mPositionHandle);

    //Draw the point
    GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1);

  }
}

И вот визуальный результат:

the result from the applied code

1 Ответ

0 голосов
/ 01 августа 2018

То, что вы пытаетесь выполнить - нарисовать растр пикселей с определенными размерами, измеренными в пикселях - на самом деле не похоже на то, для чего был разработан API OpenGL.

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

Видите ли, OpenGL имеет свою собственную систему координат, которая не зависит от разрешения экрана.Для 2D-чертежа без глубины обычно по умолчанию используется значение (-1,-1)-(1,1).Это облегчает создание независимой от разрешения визуализации.Однако вам нужны пиксели, которые зависят от разрешения .

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

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

Для реальной скорости вам необходимо преобразовать прямоугольную область, которую вы хотите заполнить, в два треугольника и иметь OpenGLнарисовать те.Пиксельное окрашивание, называемое фрагментами в OpenGL, должно выполняться в фрагментном шейдере, который работает в графическом процессоре (= быстро).В вашем GLSL вы можете рассчитать цвет для каждого фрагмента.Это хорошее решение, если ваши смежные пиксели имеют один цвет или градиент - на самом деле это своего рода расчетный шаблон.

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

...