Reputation: 169
I try to draw a circle, but somehow it gets all messed up.
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
EDIT1: Full code now. I tried to use GLushort instead of GLubyte to use all 360 points but its crashing when drawing with something else then GL_POINTS.
EDIT2: Updated to current Code. Current pic also added.I guess there's still smth wrong. I reduced points to 180 (+1 for mid) so i dont get in trouble with GLubyte. Therefor I just took every second value, calculating the vertices.
Upvotes: 0
Views: 1138
Reputation: 43329
They are in fan-order, but then you pass GL_TRIANGLE_FAN
as the primitive type to glDrawElements (...)
. This is not a matter of redundancy, it is simply wrong. You have 3N indices, which means the only logical primitive mode to use with these indices is GL_TRIANGLES
.
As it stands right now, you could solve this simply by replacing GL_TRIANGLE_FAN
with GL_TRIANGLES
in your call to glDrawElements (...)
, but this will not reap the benefits of a primitive mode that is optimized for triangle adjacency.
GL_TRIANGLE_FAN
correctly.
- (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);
}
I can conclude that you were sort of on the right track, except you forgot that fans are not defined by 3 indices per-triangle; each additional triangle in a fan shares a common central vertex and re-uses the last vertex. Thus, the number of indices is N+2, where N is the number of triangles.
What you want to do is walk the perimeter of the circle with your indices, you will generate a total of V-2 triangles where V is the number of vertices on the perimeter of your circle. If you correct your indices to do this, then you should get a nice triangulated circle (or something that approximates one, anyway).
Each triangle shares the central vertex A
, and re-uses the last vertex addressed. Thus after defining ABC
each following triangle only requires 1 point (e.g. 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
Another way of thinking about this is that each triangle shares an edge radiating from a central vertex with the prior triangle; like a paper fan.
I believe this should fix your problem, the number of indices will actually match the number of vertices in a proper fanned circle. My discussion of the number of triangles in relation to vertices above may have generated some confusion, the number of triangles is not actually defined anywhere in the code.
- (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;
}
Now, when it comes time to draw the fan you will use points-many indices. Incidentally, you do not even need indexed vertex rendering to draw a fan like this efficiently; the indices are all sequential. glDrawArrays (GL_TRIANGLE_FAN, 0, <num_points>)
will get the job done.
While we are on the topic of efficiency, I would like to take this opportunity to point out that GLubyte
is not a hardware supported index type on desktop GPUs. The GPU will wind up performing a bunch of unaligned memory accesses if you use 8-bit indices, so any client storage benefits are offset by added run-time overhead on the GPU.
This is something I see a lot of. While it is always good to use the smallest type possible in GL, you need to consider the smallest type the GPU can use natively. The problem is that the detail of hardware supported index types is rarely discussed anywhere and only those who have been in the industry for a long time seem to know this detail. Using the fantastic new OpenGL Debug Output facilities, most drivers will print a performance warning when you use a GLubyte
index type, so hopefully this detail will start to receive more attention.
Upvotes: 4
Reputation: 45342
You are using
GLubyte ind[points*3];
but you have
int points = 360;
as ubyte is just 8 bit, you can adress up to 256 different vertices, which explains that you don't get a full circle but only 256 degrees. However, I think there might be something else wrong, as I don't see how this mistake would explain all of the issues in your screenshots...
Upvotes: 1