简体   繁体   中英

Simpler way to draw a line in OpenGL-ES on iOS

I've been trying to create a plotting library in OpenGL on iOS. I need to plot live data with good performance. I've used OpenGL before but OpenGL-ES seems to be horribly difficult.

This is the code that I've pieced together based on what I know of OpenGL and what I've found online.

GLfloat* curve = [_dataSource curveForOGLGraphObject:self];
GLuint curveLength = [_dataSource lengthOfCurveForOGLGraphObject:self];

GLfloat lineVertices[curveLength*2];

int i;
for (i = 0; i < curveLength; i++)
{
    lineVertices[i * 2] = i;
    lineVertices[i * 2 + 1] = curve[i];
}

// Have OpenGL generate a buffer name and store it in the buffer object array
glGenBuffers(1, &viewRenderBuffer);

// Bind the buffer object array to the GL_ARRAY_BUFFER target buffer
glBindBuffer(GL_ARRAY_BUFFER, viewRenderBuffer);

// Send the line data over to the target buffer in GPU RAM
glBufferData(
             GL_ARRAY_BUFFER,   // the target buffer
             sizeof(lineVertices),      // the number of bytes to put into the buffer
             lineVertices,              // a pointer to the data being copied
             GL_STATIC_DRAW);   // the usage pattern of the data

// Enable vertex data to be fed down the graphics pipeline to be drawn
glEnableVertexAttribArray(GLKVertexAttribPosition);

// Specify how the GPU looks up the data
glVertexAttribPointer(
                      GLKVertexAttribPosition, // the currently bound buffer holds the data
                      2,                       // number of coordinates per vertex
                      GL_FLOAT,                // the data type of each component
                      GL_FALSE,                // can the data be scaled
                      2*sizeof(float),                     // how many bytes per vertex (2 floats per vertex)
                      NULL);                   // offset to the first coordinate, in this case 0
glLineWidth(3.0f);
glDrawArrays(GL_LINE_STRIP, 0, curveLength); // render

I'm used to using glBegin() and glEnd() but I can understand why they were removed. However, it seems like this is a painful way to draw lines. Especially because using glColor4f seems to have no effect, but assigning a color to each vertex seems crazy in my mind if I just want a solid colored line.

You don't need to provide a color for each vertex of the line. The big difference between old OpenGL on the Desktop and GLES 2.0 is that the former is configurable , while the latter is programmable .

So, where in older OpenGL you'd set a global color for your vertices with glColor4f , you now have flexibility to compute that color in a shader from basically any source of data you want, whether that is a buffer with a color for each vertex, a uniform, hardcoded, or something else altogether.

That means more work, but it also means that you can achieve effects that were impossible before.

Here is an example of how to use just 4 GLFloats to achieve your goal:

//In your Objc code (On the CPU)
GLFloat color[4]; //declared elsewhere
glVertexAttrib4f(MyShadersColorAttributeHandle, color[0], color[1], color[2], color[3]);
//MyShaderColorAttribHandle is acquired when you are setting up your shaders.

//On the GPU: vertex shader

attribute vec3 position;
attribute vec4 color; //corresponds to MyShadersColorAttributeHandle

void main() {
  gl_Position = position;
}

// fragment shader
vary lowp vec4 color;
void main {    
    gl_FragColor = color;
}

Some other observations:

  • Core Plot is a prexisting framework that should fulfill your needs.
  • I'm very dubious that you actually need OpenGL ES to achieve your goal. Core Graphics is most likely good enough, and will be far simpler.
  • It's unclear what you're trying to do, but you probably don't need to make a new render buffer every time you draw. You can (and should) reuse this object, or be getting it from the layer of your view.
  • If your data is updating live, you probably don't want GL_STATIC_DRAW , as this implies that the same data will be reused repeatedly.

You posted very little code and described your problem very poorly. For what you are doing you might as well be using ES1 which has a fixed pipeline and you will not need any shaders and the code will be much similar to what you seem to be used to. Even glColor4f will work.

In any case you should try to achieve as much as being able to draw a triangle or a line for what I care which you can set a color and position to. After that you will easily integrate your lines.

There are quite a few things you must watch for though. As already mentioned you will be capped to some FPS which should be 60 for all significant devices. That is why your drawing engine should collect the data instead of that you feed the data from your system and force the openGL to draw. That means you should rather use a timer to redraw the scene and collect the data that are currently ready. For instance create a class that will hold the vertex data (points and count). Create this class when you get the new data, fill the data and store it at some property. Now when the drawing is free you may collect the same object from property and use it to draw. This will make it possible for some data buffers to be skipped if the openGL becomes to slow and the effect is quite a traditional lag.

As for the buffers you are using I think they are useless in your case. You usually generate buffers on the GPU ( glGenBuffers ) so you may reuse them and reduce the traffic to the GPU. Since you keep creating buffers and pushing the data to them I would say you are only producing an overhead. For constant streaming it might be best just removing the buffers and feed the pointer itself to the glVertexAttribPointer(... , lineVertices) as the last parameter. Another way is to generate a large enough buffer and stream the data to the same buffer all the time, also change the flag to GL_DYNAMIC_DRAW or even GL_STREAM_DRAW . You might want to test these 3 procedures yourself to gain performance if you will need it. And do not forget to delete the buffer once you are done using it.

About drawing the lines themselves there are many reports it is better to draw rectangles as triangle strip to gain performance. This is another thing you might want to check.

And about the ES2+ and colors as already mentioned simply hardcode it into the shader or use an uniform if you want to have it as a constant. Otherwise you can do many things in shaders for instance you could use the pixel position to make the color so that the bottom most point is blue and the top most red creating a nice gradient.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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