简体   繁体   中英

How do I draw thousands of squares with glkit, opengl es2?

I'm trying to draw up to 200,000 squares on the screen. Or a lot of squares basically. I believe I'm just calling way to many draw calls, and it's crippling the performance of the app. The squares only update when I press a button, so I don't necessarily have to update this every frame.

Here's the code i have now:

- (void)glkViewControllerUpdate:(GLKViewController *)controller
{

//static float transY = 0.0f;
//float y = sinf(transY)/2.0f;
//transY += 0.175f;

GLKMatrix4 modelview = GLKMatrix4MakeTranslation(0, 0, -5.f);
effect.transform.modelviewMatrix = modelview;

//GLfloat ratio = self.view.bounds.size.width/self.view.bounds.size.height;
GLKMatrix4 projection = GLKMatrix4MakeOrtho(0, 768, 1024, 0, 0.1f, 20.0f);    
effect.transform.projectionMatrix = projection;
_isOpenGLViewReady = YES;
}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
if(_model.updateView && _isOpenGLViewReady)
{

    glClear(GL_COLOR_BUFFER_BIT);
    [effect prepareToDraw];
    int pixelSize = _model.pixelSize;
    if(!_model.isReady)
        return;
    //NSLog(@"UPDATING: %d, %d", _model.rows, _model.columns);
    for(int i = 0; i < _model.rows; i++)
    {
        for(int ii = 0; ii < _model.columns; ii++)
        {
            ColorModel *color = [_model getColorAtRow:i andColumn:ii];
            CGRect rect = CGRectMake(ii * pixelSize, i*pixelSize, pixelSize, pixelSize);
            //[self drawRectWithRect:rect withColor:c];
            GLubyte squareColors[] = {
                color.red, color.green, color.blue, 255,
                color.red, color.green, color.blue, 255,
                color.red, color.green, color.blue, 255,
                color.red, color.green, color.blue, 255
            };

            // NSLog(@"Drawing color with red: %d", color.red);


            int xVal = rect.origin.x;
            int yVal = rect.origin.y;
            int width = rect.size.width;
            int height = rect.size.height;
            GLfloat squareVertices[] = {
                xVal, yVal, 1,
                xVal + width, yVal, 1,
                xVal,  yVal + height, 1,
                xVal + width,  yVal + height, 1
            };    

            glEnableVertexAttribArray(GLKVertexAttribPosition);
            glEnableVertexAttribArray(GLKVertexAttribColor);

            glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 0, squareVertices);
            glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, squareColors);

            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

            glDisableVertexAttribArray(GLKVertexAttribPosition);
            glDisableVertexAttribArray(GLKVertexAttribColor);


        }
    }   
    _model.updateView = YES;
}

First, do you really need to draw 200,000 squares? Your viewport only has 786,000 pixels total. You might be able to reduce the number of drawn objects without significantly impacting the overall quality of your scene.

That said, if these are smaller squares, you could draw them as points with a pixel size large enough to cover your square's area. That would require setting gl_PointSize in your vertex shader to the appropriate pixel width. You could then generate your coordinates and send them all to be drawn at once as GL_POINTS. That should remove the overhead of the extra geometry of the triangles and the individual draw calls you are using here.

Even if you don't use points, it's still a good idea to calculate all of the triangle geometry you need first, then send all that in a single draw call. This will significantly reduce your OpenGL ES API call overhead.

One other thing you could look into would be to use vertex buffer objects to store this geometry. If the geometry is static, you can avoid sending it on each drawn frame, or only update a part of it that has changed. Even if you just change out the data each frame, I believe using a VBO for dynamic geometry has performance advantages on the modern iOS devices.

Can you not try to optimize it somehow? I'm not terribly familiar with graphics type stuff, but I'd imagine that if you are drawing 200,000 squares chances that all of them are actually visible seems to be unlikely. Could you not add some sort of isVisible tag for your mySquare class that determines whether or not the square you want to draw is actually visible? Then the obvious next step is to modify your draw function so that if the square isn't visible, you don't draw it.

Or are you asking for someone to try to improve the current code you have, because if your performance is as bad as you say, I don't think making small changes to the above code will solve your problem. You'll have to rethink how you're doing your drawing.

It looks like what your code is actually trying to do is take a _model.rows × _model.columns 2D image and draw it upscaled by _model.pixelSize . If -[ColorModel getColorAtRow:andColumn:] is retrieving 3 bytes at a time from an array of color values, then you may want to consider uploading that array of color values into an OpenGL texture as GL_RGB / GL_UNSIGNED_BYTE data and letting the GPU scale up all of your pixels at once.

Alternatively, if scaling up the contents of your ColorModel is the only reason that you're using OpenGL ES and GLKit, you might be better off wrapping your color values into a CGImage and allowing UIKit and Core Animation do the drawing for you. How often do the color values in the ColorModel get updated?

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