OpenGL圈无效

时间:2013-10-12 18:53:39

标签: ios opengl-es

我尝试绘制一个圆圈,但不知怎的,这一切都搞砸了。

    typedef struct {
    float Position[3];
    float Color[4];
} Vertex;

Vertex* Vertices; //vertices store information for each "point" used to draw a triangle
GLubyte* Indices; //Used to reuse vertices
//const GLushort Indices[] = {
//    0, 1, 2,
//    2, 3,
//    3, 4
//};
int points = 181;

@interface CometGLViewController (){
    float _curRed;
    BOOL _increasing;
    GLuint _vertexBuffer;
    GLuint _indexBuffer;
    GLuint _vertexArray;
    float _rotation;
}

@property (strong, nonatomic) EAGLContext *context;
@property (strong, nonatomic) GLKBaseEffect *effect;

@end

@implementation CometGLViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

# pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];

    if (!self.context) {
        NSLog(@"Failed to create ES context");
    }

    GLKView *view = (GLKView *)self.view;
    view.context = self.context;
    view.drawableMultisample = GLKViewDrawableMultisample4X;
    [self setupVertices];
    [self setupGL];
}

- (void)viewDidUnload
{
    [super viewDidUnload];

    [self tearDownGL];

    if ([EAGLContext currentContext] == self.context) {
        [EAGLContext setCurrentContext:nil];
    }
    self.context = nil;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - GLKViewDelegate

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClearColor(0, 0, 0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    [self.effect prepareToDraw];

    glBindVertexArrayOES(_vertexArray);
    glDrawElements(GL_TRIANGLES, points * 2 + 1, GL_UNSIGNED_BYTE, 0);
}

#pragma mark - GLKViewControllerDelegate

- (void)update
{
    if (_increasing) {
        _curRed += 1.0 * self.timeSinceLastUpdate;
    } else {
        _curRed -= 1.0 * self.timeSinceLastUpdate;
    }
    if (_curRed >= 1.0) {
        _curRed = 1.0;
        _increasing = NO;
    }
    if (_curRed <= 0.0) {
        _curRed = 0.0;
        _increasing = YES;
    }

    //Rotate
    float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
    GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 4.0f, 15.0f);
    self.effect.transform.projectionMatrix = projectionMatrix;

    GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -6.0f);
    //_rotation += 90 * self.timeSinceLastUpdate;
    //modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, GLKMathDegreesToRadians(_rotation), 0, 0, 1);
    self.effect.transform.modelviewMatrix = modelViewMatrix;
}

#pragma mark - OpenGL stuff
- (void)setupGL {

    [EAGLContext setCurrentContext:self.context];
    glEnable(GL_CULL_FACE);
    self.effect = [[GLKBaseEffect alloc] init];

// ----- Setup textures
//    NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
//                              [NSNumber numberWithBool:YES],
//                              GLKTextureLoaderOriginBottomLeft,
//                              nil];
//    
//    NSError * error;
//    NSString *path = [[NSBundle mainBundle] pathForResource:@"tile_floor" ofType:@"png"];
//    GLKTextureInfo * info = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
//    if (info == nil) {
//        NSLog(@"Error loading file: %@", [error localizedDescription]);
//    }
//    
//    self.effect.texture2d0.name = info.name;
//    self.effect.texture2d0.enabled = true;

    glGenVertexArraysOES(1, &_vertexArray);
    glBindVertexArrayOES(_vertexArray);
// ----- create new buffer,  work with "vertexBuffer", glBufferData sends data for opengl usage
    glGenBuffers(1, &_vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * points, Vertices, GL_STATIC_DRAW);

    glGenBuffers(1, &_indexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLubyte) * (points * 2 + 1), Indices, GL_STATIC_DRAW);

// ----- Setup vertices attributes
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *) offsetof(Vertex, Position));
    glEnableVertexAttribArray(GLKVertexAttribColor);
    glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *) offsetof(Vertex, Color));
}

- (void)tearDownGL {

    [EAGLContext setCurrentContext:self.context];

    glDeleteBuffers(1, &_vertexBuffer);
    glDeleteBuffers(1, &_indexBuffer);
    glDeleteVertexArraysOES(1, &_vertexArray);

    self.effect = nil;

}

- (void)setupVertices
{
    Vertices = malloc(sizeof(Vertex) * points);
    Vertex v = {{0, 0, 0}, {0, 0, 1, 1}};
    Vertices[0] = v;
    GLubyte ind[points *2 +1];
    ind[0] = 0;
    int indPos = 1;

    for(int i = 1; i < points; ++i){
        float theta = 2.0 * 3.1415926 * ( ((float)i-1)*2 / (float)points );
        Vertex v = {{cosf(theta), sinf(theta), 0}, {1, 1, 0, 1}};
        Vertices[i] = v;

        ind[indPos] = i;
        if(i != points-1)
            ind[indPos + 1] = i+1;
        else
            ind[indPos +1] = 1;
        indPos = indPos +2;

        NSLog(@"%f und %f", cosf(theta), sinf(theta));
        NSLog(@"%i und %i", i, i+1);
    }
    Indices = ind;
}

#pragma mark - Touch control

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    self.paused = !self.paused;
}

@end

pic pic2

EDIT1 :完整代码。 我尝试使用GLushort而不是GLubyte来使用所有360点,但在使用其他东西绘制时崩溃然后GL_POINTS。

EDIT2 :已更新至当前代码。目前的照片也补充说。我猜这仍然是错误的。 我将积分减少到180(中间+1)所以我没有遇到GLubyte的问题。因此,我只计算每一秒值,计算顶点。

2 个答案:

答案 0 :(得分:4)

您正在使用的基本模式的索引是错误的。

它们是粉丝顺序,但是您将GL_TRIANGLE_FAN作为基本类型传递给glDrawElements (...)。这不是冗余问题,而是完全错误的。您有3N索引,这意味着与这些索引一起使用的唯一逻辑基本模式是GL_TRIANGLES

就目前而言,您可以通过在GL_TRIANGLE_FAN的调用中将GL_TRIANGLES替换为glDrawElements (...)来解决此问题,但这不会获得原始模式的好处,针对三角形邻接进行了优化。

<小时/> 我将在下面解释一个更好的解决方案,它将减少索引数组的必要大小并正确使用GL_TRIANGLE_FAN

根据以下代码判断:

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClearColor(0, 0, 0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    [self.effect prepareToDraw];

    glBindVertexArrayOES(_vertexArray);
    glDrawElements(GL_TRIANGLE_FAN, points * 3, GL_UNSIGNED_SHORT, 0);
}

我可以断定你在正确的轨道上排序,除了你忘了粉丝不是由每个三角形的3个索引定义的;风扇中的每个附加三角形共享一个公共中心顶点并重新使用最后一个顶点。因此,索引的数量是N + 2,其中N是三角形的数量。

你想要做的是用你的指数走圆圈的周长,你将生成总共V-2个三角形,其中V是圆周长的顶点数。如果你更正你的指数来做到这一点,那么你应该得到一个漂亮的三角形圆(或者近似于一个的东西)。


从视觉上看,这就是三角扇的工作原理:

每个三角形共享中心顶点A,并重新使用最后一个寻址的顶点。因此,在定义ABC每个后续三角形后,只需要1分(例如DEF)。

Indices:     A,B,C,D,E,F     [Count: 6]
Triangles:  (A,B,C)
            (A) (C,D)
            (A)   (D,E)
            (A)     (E,F)    [N=4]  -->  4+2 = 6

另一种思考方式是每个三角形共享一个从中心顶点到先前三角形辐射的边缘;像纸扇一样。


更新

我相信这应该解决你的问题,索引的数量实际上会与正确的扇形圈中的顶点数量相匹配。我对上面与顶点相关的三角形数量的讨论可能产生了一些混淆,三角形的数量实际上并没有在代码中的任何地方定义。

- (void)setupVertices
{
    Vertices = malloc(sizeof(Vertex) * points);
    Vertex v = {{0, 0, 0}, {0, 0, 1, 1}};
    Vertices[0] = v;
    GLubyte ind[points];
    ind[0] = 0;
    int indPos = 1;

    for(int i = 1; i < points; ++i){
        float theta = 2.0 * 3.1415926 * ( ((float)i-1)*2 / (float)points );
        Vertex v = {{cosf(theta), sinf(theta), 0}, {1, 1, 0, 1}};
        Vertices[i] = v;

        ind[indPos] = i;

        NSLog(@"%f und %f", cosf(theta), sinf(theta));
        NSLog(@"%i und %i", i, i+1);
    }
    Indices = ind;
}

现在,当需要绘制风扇时,您将使用点数指数。顺便说一句,你甚至不需要索引顶点渲染来有效地绘制这样的风扇;指数都是连续的。 glDrawArrays (GL_TRIANGLE_FAN, 0, <num_points>)将完成工作。

虽然我们谈的是效率问题,但我想借此机会指出GLubyte不是桌面GPU上的硬件支持索引类型。如果使用8位索引,GPU将最终执行一堆未对齐的内存访问,因此任何客户端存​​储优势都会被GPU上增加的运行时开销所抵消。

这是我看到的很多东西。尽管在GL中使用可能的最小类型始终是好的,但您需要考虑GPU本身可以使用的最小类型。问题是硬件支持的索引类型的细节很少在任何地方讨论,只有那些长期在业界的人似乎知道这个细节。使用奇妙的新OpenGL调试输出工具,大多数驱动程序将在您使用GLubyte索引类型时打印性能警告,因此希望此详细信息将开始受到更多关注。

答案 1 :(得分:1)

您正在使用

GLubyte ind[points*3];

但你有

int points = 360;

因为ubyte只有8位,你可以处理多达256个不同的顶点,这说明你没有得到一个完整的圆,但只有256度。但是,我认为可能还有其他问题,因为我没有看到这个错误如何解释截图中的所有问题......