0

我试着画一个圆圈,但不知怎的,一切都搞砸了。

    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

图片 图2

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

EDIT2:更新到当前代码。还添加了当前图片。我想还是有问题。我将积分减少到 180(中间 +1),所以我不会遇到 GLubyte 的麻烦。因此,我只取每一秒的值,计算顶点。

4

2 回答 2

4

对于您使用的原始模式,您的索引是错误的。

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

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


我将在下面解释一个更好的解决方案,它将减少索引数组的必要大小并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 个点(例如D, E, F)。

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索引类型时打印性能警告,因此希望这个细节会开始受到更多关注。

于 2013-10-12T21:03:04.137 回答
1

您正在使用

GLubyte ind[points*3];

但是你有

int points = 360;

由于 ubyte 只有 8 位,因此您最多可以寻址 256 个不同的顶点,这说明您没有得到一个完整的圆,而只有 256 度。但是,我认为可能还有其他问题,因为我看不出这个错误将如何解释您的屏幕截图中的所有问题......

于 2013-10-12T19:10:01.317 回答