iOS. OpenGL. Fill area with gradient under curve line - ios

I need to create a sound wave animation like Siri (SiriAnim)
With OpenGL I'v got a shape of wave:
Here is my code:
#property (strong, nonatomic) EAGLContext *context;
#property (strong, nonatomic) GLKBaseEffect *effect;
// .....
- (void)setupGL {
[EAGLContext setCurrentContext:self.context];
glEnable(GL_CULL_FACE);
self.effect = [[GLKBaseEffect alloc] init];
self.effect.useConstantColor = GL_TRUE;
self.effect.constantColor = GLKVector4Make(0.0f, 1.0f, 0.0f, 1.0f);
}
// .....
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
glClearColor(_curRed, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
[self.effect prepareToDraw];
GLfloat line[1440];
for (int i = 0; i < 1440; i += 4) {
float x = 0.002*i - 0.75;
float K = 8.0f;
float radians = DEGREES_TO_RADIANS(i/2);
float func_x = 0.4 *
pow(K/(K + pow(radians-M_PI,4.0f)), K) *
cos(radians-M_PI);
line[i] = x;
line[i+1] = func_x;
line[i+2] = x;
line[i+3] = -func_x;
}
GLuint bufferObjectNameArray;
glGenBuffers(1, &bufferObjectNameArray);
glBindBuffer(GL_ARRAY_BUFFER, bufferObjectNameArray);
glBufferData(
GL_ARRAY_BUFFER,
sizeof(line),
line,
GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(
GLKVertexAttribPosition,
2,
GL_FLOAT,
GL_FALSE,
2*4,
NULL);
glLineWidth(15.0);
glDrawArrays(GL_LINES, 0, 360);
}
BUT! I'm confused because i can't find any solutions for gradient. After a lot of time of searching I even have a strong suspicion that such task is impossible (because GLKBaseEffect *effect . constantColor i think).
So! Could anyone help me with any solution for this task?
Can this problem be solved with shaders or textures (the worst solution)?
Bless you for your answers!

Although this could be done with a texture, I think the easiest way to accomplish this is by using OpenGL's default color interpolation. If you make the top vertex of the lines you're drawing a light blue, and the bottom vertex a darker blue, the GPU will automatically interpolate the colors between them to gradually change, and produce the gradient effect you're looking for.
The easiest way to implement this in your code is to make room in your buffer, the "lines" array, for the color of every single vertex of the line, and set up your shaders to output this value. That means you'll have to add inputs and outputs for this color to your vertex and pixel shaders. The idea is to pass it from the vertex to the pixel shader, and the pixel shader outputs the value unmodified. The hardware handles the interpolation between colors automatically for you(!).
Many modern OpenGL tutorials have examples of doing this. One free online one is from LearnOpenGL's Shader tutorial. If you have the money, though, my favorite explanation of buffers, shaders, and the pipeline itself is in Graham Sellers' OpenGL SuperBible. If you plan on using OpenGL often and really learning it, it's an invaluable desktop reference.

Related

Framerate dropping when drawing multiple textures in OpenGL ES iOS

Basically what I'm doing is making a simple finger drawing application. I have a single class that takes the input touch points and does all the fun work of turning those touch points into bezier curves, calculating vertices from those, etc. That's all working fine.
The only interesting constraint I'm working with is that I need strokes to blend on on top of each other, but not with themselves. Imagine having a scribbly line that crosses itself and has 50% opacity. Where the line crosses itself, there should be no visible blending (it should all look like the same color). However, the line SHOULD blend with the rest of the drawing below it.
To accomplish this, I'm using two textures. A back texture and a scratch texture. While the line is actively being updated (during the course of the stroke), I disable blending, draw the vertices on the scratch texture, then enable blending, and draw the back texture and scratch texture into my frame buffer. When the stroke is finished, I draw the scratch texture into the back texture, and we're ready to start the next stroke.
This all works very smoothly on a newer device, but on older devices the frame rate takes a severe hit. From some testing, it seems that the biggest performance hit is in drawing the textures to the frame buffer, because they're relatively large textures (due to the iPhone's retina resolution).
Does anybody have any hints on some strategies to work around this? I'm happy to provide more specifics or code, I'm just not sure where to start.
I am using OpenGL ES 2.0, targeting iOS 7.0, but testing on an iPhone 4S
The following is code I'm using to draw into the framebuffers:
- (void)drawRect:(CGRect)rect
{
[self drawRect:rect
ofTexture:_backTex
withOpacity:1.0];
if (_activeSpriteStroke)
{
[self drawStroke:_activeSpriteStroke
intoFrameBuffer:0];
}
}
Those rely on the following few methods:
- (void)drawRect:(CGRect)rect
ofTexture:(GLuint)tex
withOpacity:(CGFloat)opacity
{
_texShader.color = GLKVector4Make(1.0, 1.0, 1.0, opacity);
[_texShader prepareToDraw];
glBindTexture(GL_TEXTURE_2D, tex);
glBindVertexArrayOES(_texVertexVAO);
glBindBuffer(GL_ARRAY_BUFFER, _texVertexVBO);
[self bufferTexCoordsForRect:rect];
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArrayOES(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, tex);
}
- (void)drawStroke:(AHSpriteStroke *)stroke
intoFrameBuffer:(GLuint)frameBuffer
{
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
[self renderStroke:stroke
ontoTexture:_scratchTex
inFrameBuffer:_scratchFrameBuffer];
if (frameBuffer == 0)
{
[self bindDrawable];
}
else
{
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
}
[self setScissorRect:_activeSpriteStroke.boundingRect];
glEnable(GL_SCISSOR_TEST);
[self drawRect:self.bounds
ofTexture:_scratchTex
withOpacity:stroke.lineOpacity];
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
}
- (void)renderStroke:(AHSpriteStroke *)stroke
ontoTexture:(GLuint)tex
inFrameBuffer:(GLuint)framebuffer
{
glBindFramebuffer(GL_FRAMEBUFFER, _msFrameBuffer);
glBindTexture(GL_TEXTURE_2D, tex);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
[stroke render];
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, framebuffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, _msFrameBuffer);
glResolveMultisampleFramebufferAPPLE();
const GLenum discards[] = { GL_COLOR_ATTACHMENT0 };
glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 1, discards);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
And a couple of the helper methods just for completeness so you can follow it:
- (void)bufferTexCoordsForRect:(CGRect)rect
{
AHTextureMap textureMaps[4] =
{
[self textureMapForPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect))
inRect:self.bounds],
[self textureMapForPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect))
inRect:self.bounds],
[self textureMapForPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect))
inRect:self.bounds],
[self textureMapForPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect))
inRect:self.bounds]
};
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(AHTextureMap), textureMaps, GL_DYNAMIC_DRAW);
}
- (AHTextureMap)textureMapForPoint:(CGPoint)point
inRect:(CGRect)outerRect
{
CGPoint pt = CGPointApplyAffineTransform(point, CGAffineTransformMakeScale(self.contentScaleFactor, self.contentScaleFactor));
return (AHTextureMap) { { pt.x, pt.y }, { point.x / outerRect.size.width, 1.0 - (point.y / outerRect.size.height) } };
}
From what I understand you are drawing each quad in a separate draw call.
If your stroke consist of a lot of quads(from sampling the bezier curve) your code will make many draw calls per frame.
Having many draw calls in OpenGL ES 2 on older iOS devices will probably generate a bottle neck on the CPU.
The reason is that draw calls in OpenGL ES 2 can have a lot of overhead in the driver.
The driver tries to organize the draw calls you make into something the GPU can digest and it does this organization using the CPU.
If you intend to draw many quads to simulate a brush stroke you should update a vertex buffer to contain many quads and then draw it with one draw call instead of making a draw call per quad.
You can verify that your bottle neck is in the CPU with the Time Profiler instrument.
You can then check if the CPU is spending most of his time on the OpenGL draw call methods or rather on your own functions.
If the CPU spends most of it's time on the OpenGL draw call methods it is likely because you are making too many draw calls per frame.

glPushMatrix() & glPopMatrix() throw GL_INVALID_OPERATION

i try to draw a waveform from the incoming iphone microphone stream. The extraction of the data was no problem an the drawing works fine. Only when I use the OpenGL Exception Breakpoint xcode throws exceptions at glPushMatrix() & glPopMatrix()with the code GL_INVALID_OPERATION. I searched the internet for some more informations, but the only thing that i found was this:
GL_INVALID_OPERATION is generated if glPushMatrix or glPopMatrix is executed between the execution of glBegin and the corresponding execution of glEnd.
i dont use the commands glBegin oder glEnd, because of that this doesn't help me. Any ideas? What is the problem here? i draw the stuff like this:
- (void)drawPlotWithView:(GLKView*)view drawInRect:(CGRect)rect {
glBindBuffer(GL_ARRAY_BUFFER, _plotVBO);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(XOAudioPlotGLPoint), NULL);
[self.baseEffect prepareToDraw];
glPushMatrix();
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, _plotGraphSize);
glPopMatrix();
[self.baseEffect prepareToDraw];
glPushMatrix();
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(M_PI);
glDrawArrays(GL_TRIANGLE_STRIP, 0, _plotGraphSize);
glPopMatrix();
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
the initialization ist like this:
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.useConstantColor = GL_TRUE;
self.preferredFramesPerSecond = 60;
if (![EAGLContext currentContext]) {
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
} else {
self.context = [EAGLContext currentContext];
}
if (!self.context) {
NSLog(#"Failed to create ES context");
} else {
EAGLContext.currentContext = self.context;
}
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableMultisample = GLKViewDrawableMultisample4X;
glGenBuffers(1, &_plotVBO);
glLineWidth(2.0f);
glPushMatrix and glPopMatrix refer to the built-in matrix stack from the fixed-function OpenGL pipeline -- that functionality isn't in OpenGL ES 2.0.
However, the way that you're using it looks like it's not really doing anything, and what you are doing is in the wrong order. Drawing with GLKBaseEffect takes three steps:
Set the modelview and projection matrices via properties on your GLKBaseEffect instance. There's no "current matrix" or "matrix mode" implicit state like there is in GLES 1.x; just explicitly named and separately stored properties on GLKBaseEffect. (You're already doing this with the lines where you set self.baseEffect.transform.modelviewMatrix.)
Call prepareToDraw on the GLKBaseEffect instance. This binds the matrices, textures, and other state you've set in GLKBaseEffect for use by the shaders that class generates for you. (You're doing this before setting each matrix, so the matrices you're setting aren't taking effect when you want.)
After all that, perform an OpenGL draw command (glDrawArrays, glDrawElements, etc.) to draw with the state you've set.
The one additional thing you might think about is whether you've (elsewhere) set a different modelview matrix on your baseEffect and are using it for other draw calls. In that case, you might want to save the current matrix before drawing with a different matrix, then restore it afterward. A matrix stack is useful for that, and GLKit provides one in the GLKMatrixStack type and related functions. But if these are your only draw calls with that effect, or your other calls create a matrix from scratch like these ones do, there's no need to save/restore.
Continuing my research on the topic, the answer seems to be that OpenGL ES 2.0 indeed does not support matrix stack or push/pop, see here.

OpenGL view disappears while rotating

I am using OpenGL 2.0 to draw a rectangle. Initially the viewport is such that i am looking from above and i can see my rectangle as i expected.
Then i start rotating this rectangle about the x-axis. When the angle of rotation equals -90deg (or +90 deg if rotating in the other direction), the rectangle disappears.
What i expect to see if the bottom surface of the rectangle when i rotate past 90deg/-90deg but instead the view disappears. It does re-appear with the total rotation angle is -270deg (or +270 deg) when the upper surface is just about ready to be shown.
How do i ensure that i can see the rectangle all along (both upper and lower surface has to be visible while rotating)?
Here' the relevant piece of code:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch * touch = [touches anyObject];
if ([touches count] == 1) {
CGPoint currLoc = [touch locationInView:self];
CGPoint lastLoc = [touch previousLocationInView:self];
CGPoint diff = CGPointMake(lastLoc.x - currLoc.x, lastLoc.y - currLoc.y);
rotX = -1 * GLKMathDegreesToRadians(diff.y / 2.0);
rotY = -1 * GLKMathDegreesToRadians(diff.x / 2.0);
totalRotationX += ((rotX * 180.0f)/3.141592f);
NSLog(#"rotX: %f, rotY: %f, totalRotationX: %f", rotX, rotY, totalRotationX);
//rotate around x axis
GLKVector3 xAxis = GLKMatrix4MultiplyVector3(GLKMatrix4Invert(_rotMatrix, &isInvertible), GLKVector3Make(1, 0, 0));
_rotMatrix = GLKMatrix4Rotate(_rotMatrix, rotX, xAxis.v[0], 0, 0);
}
}
-(void)update{
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0, 0, -6.0f);
modelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, _rotMatrix);
self.effect.transform.modelviewMatrix = modelViewMatrix;
float aspect = fabsf(self.bounds.size.width / self.bounds.size.height);
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 0, 10.0f);
self.effect.transform.projectionMatrix = projectionMatrix;
}
- (void)setupGL {
NSLog(#"setupGL");
isInvertible = YES;
totalRotationX = 0;
[EAGLContext setCurrentContext:self.context];
glEnable(GL_CULL_FACE);
self.effect = [[GLKBaseEffect alloc] init];
// New lines
glGenVertexArraysOES(1, &_vertexArray);
glBindVertexArrayOES(_vertexArray);
// Old stuff
glGenBuffers(1, &_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
glGenBuffers(1, &_indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
// New lines (were previously in draw)
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));
_rotMatrix = GLKMatrix4Identity;
// New line
glBindVertexArrayOES(0);
initialized = 1;
}
I am a newbie to OpenGL and i am using the GLKit along with OpenGL 2.0
Thanks.
There are many causes for things not rendering in OpenGL. In this case, it was back face culling (see comments on the question). Back face culling is useful because it can ignore triangles facing away from the camera and save some rasterization/fragment processing time. Since many meshes/objects are watertight and you'd never want to see the inside anyway it's uncommon to actually want two-sided shading. This functionality starts with defining a front/back of a triangle. This is done with the order the vertices are given in (sometimes called winding direction). glFrontFace chooses the direction clockwise/counter-clockwise that defines forwards, glCullFace chooses to cull either front or back (I guess some could argue not much point in having both) and finally to enable/disable:
glEnable(GL_CULL_FACE); //discards triangles facing away from the camera
glDisable(GL_CULL_FACE); //default, two-sided rendering
Some other things I check for geometry not being visible include...
Is the geometry colour the same as the background. Choosing a non-black/white background can be handy here.
Is the geometry actually drawing within the viewing volume. Throw in a simple object (immediate mode helps) and maybe use identity projection/modelview to rule them out.
Is the viewing volume correct. Near/far planes too far apart (causing z-fighting) or 0.0f near planes are common issues. Also when switching to a projection matrix, drawing anything on the Z=0 plane won't be visible any more.
Is blending enabled and everything's transparent.
Is the depth buffer not being cleared and causing subsequent frames to be discarded.
In fixed pipeline rendering, are glTranslate/glRotate transforms being carried over from the previous frame causing objects to shoot off into the distance. Always keep a glLoadIdentity at the top of the display function.
Is the rendering loop structured correctly - clear/draw/swapbuffers
Of course there's heaps more - geometry shaders not outputting anything, vertex shaders transforming vertices to the same position (so they're all degenerate), fragment shaders calling discard when they shouldn't, VBO binding/indexing issues etc. Checking GL errors is a must but never catches all mistakes.

Why do my sprites have a dark shadow/line/frame surrounding the texture?

I'm starting OpenGL with Apple's GLKit hand I'm having some trouble to get my sprites displayed properly. The Problem is that they all are surrounded with thin dark lines. The screen shot below shows two rectangles with a png image textures containing transparency (obviously).
The black shadows, surrounding them are definitely not part of the pngS. The green png is done without anti-aliasing the blue one has an anti-aliased border. The black border is also apparent if I draw only one sprite.
Te relevant part (hope so...) of code is:
//render the scene
-(void)render
{
glClearColor(69./255., 115./255., 213./255., 1.);
glClear(GL_COLOR_BUFFER_BIT);
[shapes enumerateObjectsUsingBlock:^(AAAShape *shape, NSUInteger idx, BOOL *stop)
{
[shape renderInScene:self];
}];
}
//creating and storing the effect inside shape class
-(GLKBaseEffect *)effect
{
if(!effect)
{
effect = [[GLKBaseEffect alloc] init];
}
return effect;
}
//rendering the shape (including effect configuration)
-(void)renderInScene:(AAAScene *)scene
{
//TODO: Storing vertices in Buffer
self.effect.transform.projectionMatrix = scene.projectionMatrix;
self.effect.transform.modelviewMatrix = self.objectMatrix;
if(texture)
{
self.effect.texture2d0.enabled = GL_TRUE;
self.effect.texture2d0.envMode = GLKTextureEnvModeReplace;
self.effect.texture2d0.target = GLKTextureTarget2D;
self.effect.texture2d0.name = texture.name;
}
[self.effect prepareToDraw];
if(texture)
{
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 0, self.textureCoordinates);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, 0, self.vertices);
glDrawArrays(GL_TRIANGLE_FAN, 0, self.vertexCount);
glDisableVertexAttribArray(GLKVertexAttribPosition);
if(texture)
{
glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
glDisable(GL_BLEND);
}
}
Any ideas anyone? Thank you.
This worked for me:
glEnable( GLES20.GL_BLEND );
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
Come on stackoverflowers, 14 hours and no answers ;-). On gamedev it took David 14 minutes to give this great answer. Vote him up!

iOS OpenGL skewed drawing

So I'm having a bit of a problem with an OpenGL 1.1 skewed drawing.
Background:
Basically the app is a painting app (some code borrowed from glPaint) in which the user can draw with various colors and point widths. When they exit the drawing screen I use glReadPixels to persist the pixel color data in RGBA format. When they come back to continue drawing I take the color data from disk, put it into a colorPointer and I generate an array of vertices like so:
typedef struct _vertexStruct{ GLfloat position[2];} vertexStruct;
vertexStruct vertices[VERTEX_SIZE];
And the loop
GLfloat row = 0.0f;
GLfloat col = 768.0f;
for (int i = 0; i < (768 * 1024); i++) {
if (row == 1024.0f) {
col-- ;
row = 0.0f;
}
else {
row++;
}
vertices[i].position[0] = row;
vertices[i].position[1] = [self bounds].size.height - col;
}
And here are the actual drawing calls:
glVertexPointer(2, GL_FLOAT, sizeof(vertexStruct),&vertices[0].position);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(pixelData.data), pixelData.data);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_POINTS, 0, VERTEX_SIZE);
glDisableClientState(GL_COLOR_ARRAY);
// Display the buffer
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
So, the drawing succeeds but it is skewed off to the left of where it should be. I thought that I was compensating for OpenGL(I'm using standard bottom=0,left=0 coord system) --> UIKit coordinate system differences with the
vertices[i].position[1] = [self bounds].size.height - col;
call in the loop but this may just be a naive assumption. Anyone have any clues as to what I'm doing wrong or perhaps what I need to be doing addition to have the drawing appear in the right place?? Thanks in advance!
UPDATE: Solved, I just drew the saved image to a texture (NPOT texture)! If anyone else has worries about drawing NPOT textures, it should work, worked for me at least, with the only caveat being that it's not supported on earlier devices...

Resources