2

从一开始就说明,我在 OpenGL 世界中非常新手,但我需要使用它在 Android 中进行一些渲染优化。我必须使用 OpenGL ES 2.0 在 2D 空间中渲染一个块或一堆连续像素。但是,我找到了一些合适的解决方案(此处此处),并且我都尝试了它们,但我无法达到预期的结果。

第一件事是像素总是在原点(中心或 {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);

  }
}

这是视觉结果:

应用代码的结果

4

1 回答 1

0

您正在尝试完成的工作 - 绘制具有在像素上测量的特定尺寸的像素栅格 - 看起来并不是 OpenGL API 的设计目的。

与画布绘图相比,您正在寻找速度增益,而 OpenGL 确实是通过 GPU 快速绘图的首选之地,尽管在画布上填充矩形区域可能非常快。除非你的连续像素都是不同的颜色?

你看,OpenGL 有它自己的坐标系,它不依赖于屏幕分辨率。对于没有深度的 2D 绘图,它通常默认为(-1,-1)-(1,1). 这使得创建与分辨率无关的可视化变得更加容易。但是,您需要像素,这取决于分辨率。

您可以使用glViewport将默认坐标系转换为所需的分辨率,然后在该坐标系中转换您的顶点。

但是,为大的连续区域绘制单个点会很慢。至少,将所有点放在顶点缓冲区中并绘制一次该缓冲区。您可以为每个顶点定义一种颜色以满足您的需要。但即使这样也不会非常快,因为 OpenGL 必须为每个顶点执行计算。

为了真正的速度,您需要将要填充的矩形区域转换为两个三角形并让 OpenGL 绘制它们。像素着色 - 在 OpenGL 中称为片段 - 必须在 GPU 中运行的片段着色器中完成(= 快速)。在您的 GLSL 中,您可以计算每个片段的颜色。如果您的连续像素具有单一颜色或渐变,这是一个很好的解决方案 - 确实是某种可计算的模式。

如果像素颜色数据没有图案并且来自数组,那么您就不走运了。在这种情况下,请考虑使用正确大小的 OpenGL 纹理并让 OpenGL 在三角形上绘制它。

于 2018-08-01T19:02:04.037 回答