简体   繁体   English

使用OpenGL ES 2.0渲染多个像素

[英]Render multiple pixels using OpenGL ES 2.0

To tell from the beginning, I'm very novice within OpenGL world but I need to use it for some rendering optimisations in Android. 首先,我是OpenGL世界的新手,但是我需要将其用于Android中的某些渲染优化。 I have to render a block or a bunch of contiguous pixels in a 2D space using OpenGL ES 2.0. 我必须使用OpenGL ES 2.0在2D空间中渲染一个块或一堆连续的像素。 I've found some suitable solutions, however ( here and here ) and I've tried both of them, but I cannot reach the desired result. 但是,我找到了一些合适的解决方案( 在这里这里 ),并且我都尝试了它们,但是我无法达到预期的结果。

The first thing is that the pixel is always in the origin (center or {0, 0}) and I cannot move it from there. 第一件事是像素始终位于原点(中心或{0,0})中,而我不能从原点移动它。 I would prefer to place it to top-left corner of the screen. 我希望将其放置在屏幕的左上角。

The second thing is that I cannot draw multiple pixels. 第二件事是我不能绘制多个像素。 I would like to spawn multiple pixels, not only one. 我想产生多个像素,而不仅仅是一个。

To summarize: I just want to place the pixels contiguously, for example: first pixel starting from top-left corner, the second one should be immediately after the first pixel on X axis and so on. 总结:我只想连续放置像素,例如:第一个像素从左上角开始,第二个像素应紧靠X轴上的第一个像素,依此类推。 When the end margin of the screen is met, then, the new pixel should start on a new line (Y+1). 当达到屏幕的末尾空白时,新像素应从新行(Y + 1)开始。

The code that I'm using is: 我正在使用的代码是:

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

  }
}

And here's the visual result: 这是视觉效果:

应用代码的结果

What you are trying to accomplish - painting a raster of pixels with specific dimensions measured on pixels - doesn't really appear to be what the OpenGL API was designed to do. 您要完成的任务-绘制以像素为单位测量特定尺寸的像素的栅格-似乎并不是OpenGL API设计的目的。

You're looking for a speed gain compared to canvas drawing, and OpenGL is indeed the go-to place for fast drawing through the GPU, though filling rectangular areas on a canvas can be pretty fast. 与画布绘图相比,您正在寻找一种提高速度的方法,并且OpenGL确实是通过GPU快速绘图的理想之地,尽管在画布上填充矩形区域可能非常快。 Unless your contiguous pixels are all different colors? 除非您的连续像素都是不同的颜色?

You see, OpenGL has its own coordinate system which does not depend on the screen resolution. 您会看到,OpenGL有自己的坐标系,它不依赖于屏幕分辨率。 For 2D drawing, with no depth, it usually defaults to (-1,-1)-(1,1) . 对于没有深度的2D绘图,通常默认为(-1,-1)-(1,1) This makes it easier to create resolution-independent visualizations. 这样可以更轻松地创建与分辨率无关的可视化文件。 However, you need pixels, which are resolution dependent. 然而,你需要的像素,这依赖分辨率。

You could use glViewport to convert the default coordinate system to your desired resolution, then translate your vertices in that coordinate system. 您可以使用glViewport将默认坐标系转换为所需的分辨率,然后在该坐标系中平移顶点。

However, drawing individual points for a large contiguous area will be slow. 但是,为较大的连续区域绘制单个点将很慢。 At the very least, place all your points in a vertex buffer and draw that buffer once. 至少,将所有点放置在顶点缓冲区中,并绘制一次该缓冲区。 You could define a color for each vertex to suit your needs. 您可以为每个顶点定义颜色以适合您的需求。 But even this won't be spectacularly fast, since OpenGL must perform calculations for each of your vertices. 但是,即使这样也不会很快,因为OpenGL必须为您的每个顶点执行计算。

For real speed, you'll need to convert the rectangular area you want to fill to two triangles and have OpenGL draw those. 为了获得真正的速度,您需要将要填充的矩形区域转换为两个三角形,并用OpenGL绘制它们。 The pixel coloring - called fragments in OpenGL- must be done in a fragment shader that runs in the GPU (= fast). 像素着色-在OpenGL中称为片段-必须在GPU中运行的片段着色器中完成着色(=快速)。 In your GLSL you can calculate a color for each fragment. 在您的GLSL中,您可以为每个片段计算颜色。 This is a good solution if your contiguous pixels have a single color or a gradient - some kind of calculatable pattern really. 如果您的连续像素具有单一颜色或渐变(实际上是某种可计算的图案),则这是一个很好的解决方案。

If the pixel color data has no pattern and comes out of an array, you're out of luck. 如果像素颜色数据没有图案并且来自数组,则表示您不走运。 In that case, consider using an OpenGL texture of the right size and have OpenGL draw it on the triangles. 在这种情况下,请考虑使用适当大小的OpenGL纹理,并让OpenGL在三角形上绘制它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM