Drawing a text along a path in QuartzCore - objective-c

Say I have an array of points that form a line and a text. How can I go about drawing the text along this line in
- (void)drawRect:(CGRect)rect
of a UIView?
I am able to draw the path without a problem. Is there a standard method that I overlooked or a framework that would allow me to draw the text along that path? Ideally I would like to do this using only QuartzCore/CoreGraphics.
I tried calculating the width of each character and rotating every character. This kind of works, but I was wondering if there is a more elegant method.

I believe you can do this in Mac OS X, but the closest you'll come on the iPhone is CGContextShowGlyphsWithAdvances and this wont even rotate.
It shouldn't be too hard to use a loop and draw each character using something like the following. This is adapted from Apple's documentation and not tested, so beware:
CGContextSelectFont(myContext, "Helvetica", 12, kCGEncodingMacRoman);
CGContextSetCharacterSpacing(myContext, 10);
CGContextSetTextDrawingMode(myContext, kCGTextFillStroke);
CGContextSetRGBFillColor(myContext, 0, 0, 0, 1);
CGContextSetRGBStrokeColor(myContext, 0, 0, 0, 1);
NSUInteger charIndex = 0;
for(NSString *myChar in arrayOfChars) {
char *cString = [myChar UTF8String];
CGPoint charOrigin = originForPositionAlongPath(charIndex, myPath);
CGFloat slope = slopeForPositionAlongPath(charIndex, myPath);
CGContextSetTextMatrix(myContext, CGAffineTransformMakeRotation(slope));
CGContextShowTextAtPoint(myContext, charOrigin.x, charOrigin.y, cString, 1);
}
Edit: Here's an idea of the PositionAlongPath functions. Again, they aren't tested, but should be close. originAlong... returns (-1, -1) if you run out of path.
CGPoint originForPositionAlongPath(int index, NSArray *path) {
CGFloat charWidth = 12.0;
CGFloat widthToGo = charWidth * index;
NSInteger i = 0;
CGPoint position = [path objectAtIndex:i];
while(widthToGo >= 0) {
//out of path, return invalid point
if(i >= [path count]) {
return CGPointMake(-1, -1);
}
CGPoint next = [path objectAtIndex:i+1];
CGFloat xDiff = next.x - position.x;
CGFloat yDiff = next.y - position.y;
CGFloat distToNext = sqrt(xDiff*xDiff + yDiff*yDiff);
CGFloat fracToGo = widthToGo/distToNext
//point is before next point in path, interpolate the answer
if(fracToGo < 1) {
return CGPointMake(position.x+(xDiff*fracToGo), position.y+(yDiff*fracToGo));
}
//advance to next point on the path
widthToGo -= distToNext;
position = next;
++i;
}
}
CGFloat slopeForPositionAlongPath(int index, NSArray *path) {
CGPoint begin = originForPositionAlongPath(index, path);
CGPoint end = originForPositionAlongPath(index+1, path);
CGFloat xDiff = end.x - begin.x;
CGFloat yDiff = end.y - begin.y;
return atan(yDiff/xDiff);
}

This is probably irrelevant but you can text along a path with SVG (http://www.w3.org/TR/SVG/text.html#TextOnAPath), and the iPhoneOS supports it.

Above example unfortunately didn't work as expected.
I have now finally found the correct way to paint a text along the path.
Here we go:
You cannot take this code 1:1 as its just excerpted from my application, but i will make things clear with some comments.
// MODIFIED ORIGIN FUNCTION
CGPoint originForPositionAlongPath(float *l, float nextW, int index, NSArray *path) {
CGFloat widthToGo = *l + nextW;
NSInteger i = 0;
CGPoint position = [[path objectAtIndex:i] CGPointValue];
while(widthToGo >= 0) {
//out of path, return invalid point
if(i+1 >= [path count]) {
return CGPointMake(-1, -1);
}
CGPoint next = [[path objectAtIndex:i+1] CGPointValue];
CGFloat xDiff = next.x - position.x;
CGFloat yDiff = next.y - position.y;
CGFloat distToNext = sqrt(xDiff*xDiff + yDiff*yDiff);
CGFloat fracToGo = widthToGo/distToNext;
//point is before next point in path, interpolate the answer
if(fracToGo < 1) {
return CGPointMake(position.x+(xDiff*fracToGo), position.y+(yDiff*fracToGo));
}
//advance to next point on the path
widthToGo -= distToNext;
position = next;
++i;
}
}
// MODIFIED SLOPE FUNCTION
CGFloat slopeForPositionAlongPath(float* l, float nextW, int index, NSArray *path) {
CGPoint begin = originForPositionAlongPath(l, 0, index, path);
CGPoint end = originForPositionAlongPath(l, nextW, index+1, path);
CGFloat xDiff = end.x - begin.x;
CGFloat yDiff = end.y - begin.y;
return atan(yDiff/xDiff);
}
// IMPORTANT: CHARACTER WIDTHS FOR HELVETICA, ABOVE EXAMPLE USES FIXED WIDTHS WHICH IS NOT ACCURATE
float arraychars[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0.278, 0.333, 0.474, 0.556, 0.556, 0.889, 0.722, 0.278,
0.333, 0.333, 0.389, 0.584, 0.278, 0.584, 0.278, 0.278,
0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.556,
0.556, 0.556, 0.333, 0.333, 0.584, 0.584, 0.584, 0.611,
0.975, 0.722, 0.722, 0.722, 0.722, 0.667, 0.611, 0.778,
0.722, 0.278, 0.556, 0.722, 0.611, 0.833, 0.722, 0.778,
0.667, 0.778, 0.722, 0.667, 0.611, 0.722, 0.667, 0.944,
0.667, 0.667, 0.611, 0.333, 0.278, 0.333, 0.584, 0.556,
0.278, 0.556, 0.611, 0.556, 0.611, 0.556, 0.333, 0.611,
0.611, 0.278, 0.278, 0.556, 0.278, 0.889, 0.611, 0.611,
0.611, 0.611, 0.389, 0.556, 0.333, 0.611, 0.556, 0.778,
0.556, 0.556, 0.5, 0.389, 0.28, 0.389, 0.584, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0.278, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333,
0.333, 0, 0.333, 0.333, 0, 0.333, 0.333, 0.333,
0.278, 0.333, 0.556, 0.556, 0.556, 0.556, 0.28, 0.556,
0.333, 0.737, 0.37, 0.556, 0.584, 0.333, 0.737, 0.333,
0.4, 0.584, 0.333, 0.333, 0.333, 0.611, 0.556, 0.278,
0.333, 0.333, 0.365, 0.556, 0.834, 0.834, 0.834, 0.611,
0.722, 0.722, 0.722, 0.722, 0.722, 0.722, 1, 0.722,
0.667, 0.667, 0.667, 0.667, 0.278, 0.278, 0.278, 0.278,
0.722, 0.722, 0.778, 0.778, 0.778, 0.778, 0.778, 0.584,
0.778, 0.722, 0.722, 0.722, 0.722, 0.667, 0.667, 0.611,
0.556, 0.556, 0.556, 0.556, 0.556, 0.556, 0.889, 0.556,
0.556, 0.556, 0.556, 0.556, 0.278, 0.278, 0.278, 0.278,
0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.611, 0.584,
0.611, 0.611, 0.611, 0.611, 0.611, 0.556, 0.611, 0.556,
};
void o_DrawContourLabel(void *myObjectInstance, TransBuffer* transBuffer, MapPainterIphone* mp,const Projection& projection,
const MapParameter& parameter,
const LabelStyle& style,
const std::string& text,
size_t transStart, size_t transEnd){
// HERE WE Initialize the font etc.
CGContextSelectFont(context, "Helvetica-Bold", 10, kCGEncodingMacRoman);
CGContextSetCharacterSpacing(context, 0);
CGContextSetTextDrawingMode(context, kCGTextFillStroke);
CGContextSetLineWidth(context, 3.0);
CGContextSetRGBFillColor(context, style.GetTextR(), style.GetTextG(), style.GetTextB(), style.GetTextA());
CGContextSetRGBStrokeColor(context, 1, 1, 1, 1);
// Here we prepare a NSArray holding all waypoints of our path.
// I fill it from a "transBuffer" but you may fill it with whatever you want
NSMutableArray* path = [[NSMutableArray alloc] init];
if (transBuffer->buffer[transStart].x<transBuffer->buffer[transEnd].x) {
for (size_t j=transStart; j<=transEnd; j++) {
if (j==transStart) {
[path addObject:[NSValue valueWithCGPoint:CGPointMake(transBuffer->buffer[j].x,transBuffer->buffer[j].y)]];
}
else {
[path addObject:[NSValue valueWithCGPoint:CGPointMake(transBuffer->buffer[j].x,transBuffer->buffer[j].y)]];
}
}
}
else {
for (size_t j=0; j<=transEnd-transStart; j++) {
size_t idx=transEnd-j;
if (j==0) {
[path addObject:[NSValue valueWithCGPoint:CGPointMake(transBuffer->buffer[idx].x,transBuffer->buffer[idx].y)]];
}
else {
[path addObject:[NSValue valueWithCGPoint:CGPointMake(transBuffer->buffer[idx].x,transBuffer->buffer[idx].y)]];
}
}
}
// if path is too short for "estimated text length" then exit
if (pathLength(path)<text.length()*7) {
// Text is longer than path to draw on
return;
}
// NOW PAINT CHAR FOR CHAR USING CHARACTER WIDTHS TABLE
float lenUpToNow = 0;
CGAffineTransform transform=CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0);
NSUInteger charIndex = 0;
for(int i=0;i<text.length();i++) {
char *cString = (char*)malloc(2*sizeof(char));
cString[0] = text.at(i);
cString[1]=0;
float nww = arraychars[cString[0]]*8*1.4;
CGPoint charOrigin = originForPositionAlongPath(&lenUpToNow, 0, charIndex, path);
CGFloat slope = slopeForPositionAlongPath(&lenUpToNow, nww, charIndex, path);
std::cout << " CHARACTER " << cString << " placed at " << charOrigin.x << "," << charOrigin.y << std::endl;
// DO NOT FORGET TO DO THIS (TWO TRANSFORMATIONS) .. one for the rotation
// and one for mirroring, otherwise the text will be mirrored due to a
// crappy coordinate system in QuartzCore.
CGAffineTransform ct = CGAffineTransformConcat(transform,CGAffineTransformMakeRotation(slope));
CGContextSetTextMatrix(context, ct);
CGContextSetTextDrawingMode(context, kCGTextStroke);
CGContextShowTextAtPoint(context, charOrigin.x, charOrigin.y, cString, 1);
CGContextSetTextDrawingMode(context, kCGTextFill);
CGContextShowTextAtPoint(context, charOrigin.x, charOrigin.y, cString, 1);
std::cout << " ... len (" << (int)cString[0] << ") = " << arraychars[cString[0]] << " up to now: " << lenUpToNow << std::endl;
lenUpToNow += nww;
charIndex++;
free(cString);
}
}

Related

CCRender Texture parallax scrolling bug

The problem is on the texture...
after about 20 sec of scrolling the texture bugs. the image will lose quality and makes a blur effect.
Here is a link where u can see screenshots of the bug
https://code.google.com/p/cocos2d-iphone/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Priority%20Milestone%20Component%20Owner%20Summary&groupby=&sort=&id=1516
Here is the code
-(CCSprite *)stripedSpriteWithColor1:(ccColor4F)c1 color2:(ccColor4F)c2 textureWidth:(float)textureWidth
textureHeight:(float)textureHeight stripes:(int)nStripes {
// 1: Create new CCRenderTexture
CCRenderTexture *rt = [CCRenderTexture renderTextureWithWidth:textureWidth height:textureHeight];
// 2: Call CCRenderTexture:begin
[rt beginWithClear:c1.r g:c1.g b:c1.b a:c1.a];
// 3: Draw into the texture
// Layer 1: Stripes
CGPoint vertices[nStripes*6];
ccColor4F colors[nStripes*6];
int nVertices = 0;
CCSprite *noise= [CCSprite spriteWithFile:#"Noise.jpg"];
[noise visit];
CC_NODE_DRAW_SETUP();
nVertices = 0;
vertices[nVertices] = CGPointMake(0, 0);
colors[nVertices++] = (ccColor4F){0, 0, 0, 0};
vertices[nVertices] = CGPointMake(textureWidth, 0);
colors[nVertices++] = (ccColor4F){0, 0, 0, 0};
vertices[nVertices] = CGPointMake(0, textureHeight);
colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha};
vertices[nVertices] = CGPointMake(textureWidth, textureHeight);
colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha};
glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_FLOAT, GL_TRUE, 0, colors);
glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);*/
//glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);
// layer 3: top highlight
float borderHeight = textureHeight/16;
float borderAlpha = 0.6f;
nVertices = 0;
vertices[nVertices] = CGPointMake(0, 0);
colors[nVertices++] = (ccColor4F){0, 0.4, 0.2, borderAlpha};
vertices[nVertices] = CGPointMake(textureWidth, 0);
colors[nVertices++] = (ccColor4F){0, 0.4, 0.2, borderAlpha};
vertices[nVertices] = CGPointMake(0, borderHeight);
colors[nVertices++] = (ccColor4F){0, 0.4, 0.2, 0};
vertices[nVertices] = CGPointMake(textureWidth, borderHeight);
colors[nVertices++] = (ccColor4F){0, 0.4, 0.2, 0};
glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_FLOAT, GL_TRUE, 0, colors);
glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);
// 4: Call CCRenderTexture:end
[rt end];
// 5: Create a new Sprite from the texture
return [CCSprite spriteWithTexture:rt.sprite.texture];
}
and here is the call
CCSprite *stripes = [self stripedSpriteWithColor1:color3 color2:color4
textureWidth:IS_IPHONE_5 ? 512:256 textureHeight:256 stripes:4];
ccTexParams tp2 = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_CLAMP_TO_EDGE};
[stripes.texture setTexParameters:&tp2];

OpenGL ES triangle not showing

First of all I'm french so sorry for my english.
I'm new to OpenGL ES and I'm trying to draw a simple triangle with these Vertices :
typedef struct {
float Position[3];
float Color[4];
} Vertex;
const Vertex Vertices[] = {
{{0.0, 1.0, -2.0}, {1, 0, 0, 1}},
{{1.0, 0.0, -2.0}, {1, 0, 0, 1}},
{{-1.0, 0.0, -2.0}, {1, 0, 0, 1}},
};
and these indices ;
const GLubyte Indices[] = {
0,1,2
};
But the triangle is not showing up... If I change the indices to 0,2,3, It displays a triangle with vertex 0,2 and a black vertex : {{0,0,0},{0,0,0,1}} , but there is no 4th vertex... I don't understand at all could someone explain me?
Here's my Xcode view code :
//
// EAGLView.m
// OpenGlintro
//
// Created by Arnaud Miguet on 01/12/12.
// Copyright (c) 2012 Tap‘n'Develop. All rights reserved.
//
#import "EAGLView.h"
#implementation EAGLView
+ (Class) layerClass {
return [CAEAGLLayer class];
}
- (void)setupVBOs {
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
GLuint indexBuffer;
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
CAEAGLLayer *EAGLLayer = (CAEAGLLayer *) super.layer;
EAGLLayer.opaque = YES;
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!context || ![EAGLContext setCurrentContext:context]) {
[self release];
return nil;
}
GLuint framebuffer , renderbuffer;
glGenBuffers(1, &framebuffer);
glGenBuffers(1, &renderbuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:EAGLLayer];
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
glViewport(10, 0, CGRectGetWidth(frame), CGRectGetHeight(frame));
[self compileShaders];
[self setupVBOs];
[self render];
}
return self;
}
- (void)render {
glClearColor(0, 104.0/255.0, 55.0/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// 1
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
// 2
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE,
sizeof(Vertex), 0);
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE,
sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
// 3
glDrawElements(GL_TRIANGLES, sizeof(Indices),
GL_UNSIGNED_BYTE, 0);
[context presentRenderbuffer:GL_RENDERBUFFER];
}
- (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType {
// 1
NSString* shaderPath = [[NSBundle mainBundle] pathForResource:shaderName
ofType:#"glsl"];
NSError* error;
NSString* shaderString = [NSString stringWithContentsOfFile:shaderPath
encoding:NSUTF8StringEncoding error:&error];
if (!shaderString) {
NSLog(#"Error loading shader: %#", error.localizedDescription);
exit(1);
}
// 2
GLuint shaderHandle = glCreateShader(shaderType);
// 3
const char * shaderStringUTF8 = [shaderString UTF8String];
int shaderStringLength = [shaderString length];
glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);
// 4
glCompileShader(shaderHandle);
// 5
GLint compileSuccess;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
if (compileSuccess == GL_FALSE) {
GLchar messages[256];
glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(#"%#", messageString);
exit(1);
}
return shaderHandle;
}
- (void)compileShaders {
// 1
GLuint vertexShader = [self compileShader:#"SimpleVertex"
withType:GL_VERTEX_SHADER];
GLuint fragmentShader = [self compileShader:#"SimpleFragment"
withType:GL_FRAGMENT_SHADER];
// 2
GLuint programHandle = glCreateProgram();
glAttachShader(programHandle, vertexShader);
glAttachShader(programHandle, fragmentShader);
glLinkProgram(programHandle);
// 3
GLint linkSuccess;
glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE) {
GLchar messages[256];
glGetProgramInfoLog(programHandle, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(#"%#", messageString);
exit(1);
}
// 4
glUseProgram(programHandle);
// 5
_positionSlot = glGetAttribLocation(programHandle, "Position");
_colorSlot = glGetAttribLocation(programHandle, "SourceColor");
glEnableVertexAttribArray(_positionSlot);
glEnableVertexAttribArray(_colorSlot);
}
typedef struct {
float Position[3];
float Color[4];
} Vertex;
const Vertex Vertices[] = {
{{0.0, 1.0, -2.0}, {1, 0, 0, 1}},
{{1.0, 0.0, -2.0}, {1, 0, 0, 1}},
{{-1.0, 0.0, -2.0}, {1, 0, 0, 1}},
};
const GLubyte Indices[] = {
0,2,3
};
#end
Add a vertex array before you bind the buffers.
GLuint vao;
glGenVertexArray(1, &vao);
glBindVertexArray(vao);

OpenGL ES 2.0 Texture mapped, but shows as black

I am trying to get my texture to show up in a basic OpenGL view (subclass of UIView), but no matter which texture I use, it shows up black. The code for my view is as follows:
#implementation SymGLView
typedef struct {
float Position[3];
float Color[4];
float TexCoord[2];
} Vertex;
const Vertex Vertices[] = {
{{1, -1, 0}, {1, 0, 0, 1}, {0, 0}},
{{1, 1, 0}, {0, 1, 0, 1}, {0, 1}},
{{-1, 1, 0}, {0, 0, 1, 1}, {1, 1}},
{{-1, -1, 0}, {0, 0, 0, 1}, {1, 0}}
};
const GLubyte Indices[] = {
0, 1, 2,
2, 3, 0
};
+ (Class)layerClass {
return [CAEAGLLayer class];
}
- (void)setupLayer {
_eaglLayer = (CAEAGLLayer*) self.layer;
_eaglLayer.opaque = YES;
}
- (void)setupContext {
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
_context = [[EAGLContext alloc] initWithAPI:api];
if (!_context) {
NSLog(#"Failed to initialize OpenGLES 2.0 context");
exit(1);
}
if (![EAGLContext setCurrentContext:_context]) {
NSLog(#"Failed to set current OpenGL context");
exit(1);
}
}
- (void)setupRenderBuffer {
glGenRenderbuffers(1, &_colorRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
}
- (void)setupDepthBuffer {
glGenRenderbuffers(1, &_depthRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, self.frame.size.width, self.frame.size.height);
}
- (void)setupFrameBuffer {
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer);
}
- (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType {
NSString* shaderPath = [[NSBundle mainBundle] pathForResource:shaderName ofType:#"glsl"];
NSError* error;
NSString* shaderString = [NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&error];
if (!shaderString) {
NSLog(#"Error loading shader: %#", error.localizedDescription);
exit(1);
}
GLuint shaderHandle = glCreateShader(shaderType);
const char * shaderStringUTF8 = [shaderString UTF8String];
int shaderStringLength = [shaderString length];
glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);
glCompileShader(shaderHandle);
GLint compileSuccess;
glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
if (compileSuccess == GL_FALSE) {
GLchar messages[256];
glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(#"%#", messageString);
exit(1);
}
return shaderHandle;
}
- (void)compileShaders {
GLuint vertexShader = [self compileShader:#"SimpleVertex" withType:GL_VERTEX_SHADER];
GLuint fragmentShader = [self compileShader:#"SimpleFragment" withType:GL_FRAGMENT_SHADER];
GLuint programHandle = glCreateProgram();
glAttachShader(programHandle, vertexShader);
glAttachShader(programHandle, fragmentShader);
glLinkProgram(programHandle);
GLint linkSuccess;
glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE) {
GLchar messages[256];
glGetProgramInfoLog(programHandle, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(#"%#", messageString);
exit(1);
}
glUseProgram(programHandle);
_positionSlot = glGetAttribLocation(programHandle, "Position");
_colorSlot = glGetAttribLocation(programHandle, "SourceColor");
glEnableVertexAttribArray(_positionSlot);
glEnableVertexAttribArray(_colorSlot);
_modelViewUniform = glGetUniformLocation(programHandle, "Modelview");
_texCoordSlot = glGetAttribLocation(programHandle, "TexCoordIn");
glEnableVertexAttribArray(_texCoordSlot);
_textureUniform = glGetUniformLocation(programHandle, "Texture");
}
- (void)setupVBOs {
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
GLuint indexBuffer;
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}
- (void)render {
glClearColor(0, 104.0/255.0, 55.0/255.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE,
sizeof(Vertex), 0);
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE,
sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE,
sizeof(Vertex), (GLvoid*) (sizeof(float) * 7));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _floorTexture);
glUniform1i(_textureUniform, 0);
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]),
GL_UNSIGNED_BYTE, 0);
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
- (GLuint)setupTexture:(NSString *)fileName {
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
if (!spriteImage) {
NSLog(#"Failed to load image %#", fileName);
exit(1);
}
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
GLubyte * spriteData = (GLubyte *) calloc(width*height*4, sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4,
CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
CGContextRelease(spriteContext);
GLuint texName;
glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, spriteData);
free(spriteData);
return texName;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setupLayer];
[self setupContext];
[self setupDepthBuffer];
[self setupRenderBuffer];
[self setupFrameBuffer];
[self compileShaders];
[self setupVBOs];
[self render];
}
_floorTexture = [self setupTexture:#"tile_floor.png"];
return self;
}
#end
Vertex shader:
attribute vec4 Position;
attribute vec2 TexCoordIn;
varying vec2 TexCoordOut;
void main(void) {
gl_Position = Position;
TexCoordOut = TexCoordIn;
}
Fragment shader:
varying lowp vec2 TexCoordOut;
uniform sampler2D Texture;
void main(void) {
gl_FragColor = texture2D(Texture, TexCoordOut);
}
I can create a gradient by changing the values for gl_FragColor, but I have tried several different textures and am at a loss.
This could be depending on the fact that your textures are not power of 2 (i.e. 512X512)
Some OpenGL drivers react in weird ways to this, some others just perform a rescaling of the textures to the nearest power of 2 size.
From OpenGL gold book you can find the below:
You can find a quite good explanation in the OpenGL Gold Book, the OpenGL ES 2.0:
In OpenGL ES 2.0, textures can have non-power-of-two (npot)
dimensions. In other words, the width and height do not need to be a
power of two. However, OpenGL ES 2.0 does have a restriction on the
wrap modes that can be used if the texture dimensions are not power of
two. That is, for npot textures, the wrap mode can only be
GL_CLAMP_TO_EDGE and the minifica- tion filter can only be GL_NEAREST
or GL_LINEAR (in other words, not mip- mapped). The extension
GL_OES_texture_npot relaxes these restrictions and allows wrap modes
of GL_REPEAT and GL_MIRRORED_REPEAT and also allows npot textures to
be mipmapped with the full set of minification filters.
I hope this helps.
Cheers

glDrawArrays(GL_POINTS, 0, vertexCount); draws only one point

I use a NSOpenGLView.
- (void)initGL
{
// Setup OpenGL states
[[self openGLContext] makeCurrentContext];
// Setup OpenGL states
glMatrixMode(GL_PROJECTION);
CGRect frame = self.bounds;
// Setup the view port in Pixels
glOrtho(0, frame.size.width, 0, frame.size.height, -1, 1);
glViewport(0, 0, frame.size.width, frame.size.height);
glMatrixMode(GL_MODELVIEW);
glDisable(GL_DITHER);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnable(GL_BLEND);
//glEnable(GL_LINE_SMOOTH);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_POINT_SPRITE);
glTexEnvf(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
self.pointSize = pointSizeForDrawing;
}
Framebuffer creation and binding.
- (BOOL)createFramebuffer
{
//Generating two buffers
glGenRenderbuffers( NumRenderbuffers, renderbuffer );
glBindRenderbuffer( GL_RENDERBUFFER, renderbuffer[Color]);
glRenderbufferStorage( GL_RENDERBUFFER, GL_RGBA, self.bounds.size.width, self.bounds.size.height);
glBindRenderbuffer( GL_RENDERBUFFER, renderbuffer[Depth] );
glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, self.bounds.size.width, self.bounds.size.height);
//Generating framebuffer
glGenFramebuffers( 1, &framebuffer );
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, framebuffer );
glFramebufferRenderbuffer( GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer[Color] );
glFramebufferRenderbuffer( GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer[Depth] );
glEnable( GL_DEPTH_TEST );
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
DLog(#"failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
return NO;
}
return YES;
}
Line rendering
- (void)renderLineFromPoint:(CGPoint)start ToPoint:(CGPoint)end
{
GLsizei vertexCount = 0;
DLog(#"start: %# end %#", NSStringFromCGPoint(start), NSStringFromCGPoint(end));
// Add points to the buffer so there are drawing points every X pixels
int count = MAX(ceilf(sqrtf((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y)) / brushPixelStep), 1);
for(int i = 0; i < count; ++i)
{
if(vertexCount == vertexMax)
{
vertexMax = 2 * vertexMax;
vertexBuffer = realloc(vertexBuffer, vertexMax * 2 * sizeof(GLfloat));
}
vertexBuffer[2 * vertexCount + 0] = start.x + (end.x - start.x) * ((GLfloat)i / (GLfloat)count);
vertexBuffer[2 * vertexCount + 1] = start.y + (end.y - start.y) * ((GLfloat)i / (GLfloat)count);
vertexCount += 1;
}
// Render the vertex array
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, framebuffer);
glClear(GL_DEPTH_BUFFER_BIT);
glVertexPointer(2, GL_FLOAT, 0, vertexBuffer);
glDrawArrays(GL_POINTS, 0, vertexCount);
}
And finally display buffer
- (void)displayBuffer
{
DLog(#"Display");
glBindFramebuffer( GL_READ_FRAMEBUFFER, framebuffer );
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 );
glViewport(0, 0, self.bounds.size.width, self.bounds.size.height);
glBlitFramebuffer( 0, 0, self.bounds.size.width, self.bounds.size.height, 0, 0, self.bounds.size.width, self.bounds.size.height, GL_COLOR_BUFFER_BIT, GL_NEAREST );
glSwapAPPLE();
}
It does not matter if I change line glDrawArrays(GL_POINTS, 0, vertexCount); to glDrawArrays(GL_POINTS, 0, 1);. The effect is still the same despite value of vertexCount different than 1. Any ideas?
First possible problem:
glVertexPointer(2, GL_FLOAT, 0, vertexBuffer);
As I understand vertexBuffer is an array of structures. So the error could be that the third parameter is 0 but it should be the size of this structure. For example:
CVertex3 v[3];
...
glVertexPointer(3,GL_FLOAT,sizeof(CVertex3),v);
Updated
Second possible problem:
try to remove glEnable(GL_DEPTH_TEST); it wouldn't work properly but I think I have seen the similar situation in Internet

Need assistance with simple OpenGL ES 2.0 code

I am a complete idiot at OpenGL, and I can't figure out why my code isn't working, only the clear color shows. I am trying to set up some simple rendering on iOS. Please can somebody help me?
My main scene code is:
//
// Scene.m
#import "Scene.h"
GLfloat vertices[] = {
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
1.0, 1.0
};
#implementation Scene
+(Class)layerClass {
return [CAEAGLLayer class];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
CAEAGLLayer *glLayer = (CAEAGLLayer *)self.layer;
glLayer.opaque = YES;
glLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!context) {
NSLog(#"Failed to create 2.0 context");
exit(1);
}
[EAGLContext setCurrentContext:context];
GLuint framebuffer, colorRenderbuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:glLayer];
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint program = [self loadShaders:#"Test.vp" frag:#"Test.fp"];
positionIndex = glGetAttribLocation(program, "position");
mvpIndex = glGetUniformLocation(program, "mvp");
glLinkProgram(program); //added this
GLint param;
glGetProgramiv(program, GL_LINK_STATUS, &param);
if (param == GL_FALSE) {
NSLog(#"Failed to link the program");
exit(1);
}
glUseProgram(program);
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(render:)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
return self;
}
-(void)render:(CADisplayLink *)_displayLink {
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glEnableVertexAttribArray(positionIndex);
glVertexAttribPointer(positionIndex, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid *)0);
GLfloat mvp[] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
glUniformMatrix4fv(mvpIndex, 1, GL_FALSE, mvp);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
[context presentRenderbuffer:GL_RENDERBUFFER];
glDisableVertexAttribArray(positionIndex);
}
-(GLuint)loadShaders:(NSString *)vertShader frag:(NSString *)fragShader {
NSString *vertPath = [[NSBundle mainBundle] pathForResource:[vertShader stringByDeletingPathExtension] ofType:#"vp"];
NSString *fragPath = [[NSBundle mainBundle] pathForResource:[fragShader stringByDeletingPathExtension] ofType:#"fp"];
if (!vertPath || !fragPath) {
NSLog(#"Failed to find paths of shaders");
exit(1);
}
NSError *error;
const GLchar* vertSource = [[NSString stringWithContentsOfFile:vertPath encoding:NSUTF8StringEncoding error:&error] UTF8String];
const GLchar* fragSource = [[NSString stringWithContentsOfFile:fragPath encoding:NSUTF8StringEncoding error:&error] UTF8String];
if (!vertSource || !fragSource) {
NSLog(#"Faild to load shader sources\n%#", [error localizedDescription]);
exit(1);
}
GLuint vert, frag;
vert = glCreateShader(GL_VERTEX_SHADER);
frag = glCreateShader(GL_FRAGMENT_SHADER);
GLint length = strlen(vertSource);
glShaderSource(vert, 1, &vertSource, &length);
length = strlen(fragSource);
glShaderSource(frag, 1, &fragSource, &length);
glCompileShader(vert);
glCompileShader(frag);
GLint success;
glGetShaderiv(vert, GL_COMPILE_STATUS, &success);
if (success == GL_FALSE) {
GLchar messages[256];
glGetShaderInfoLog(vert, sizeof(messages), 0, &messages[0]);
NSLog(#"Failed to compile vertex shader\n%#", [NSString stringWithUTF8String:messages]);
exit(1);
}
glGetShaderiv(frag, GL_COMPILE_STATUS, &success);
if (success == GL_FALSE) {
GLchar messages[256];
glGetShaderInfoLog(frag, sizeof(messages), 0, &messages[0]);
NSLog(#"Failed to compile fragment shader\n%#", [NSString stringWithUTF8String:messages]);
exit(1);
}
GLuint prog = glCreateProgram();
glAttachShader(prog, vert);
glAttachShader(prog, frag);
//Removed glLinkProgram()
return prog;
}
#end
The shader code is:
attribute vec2 position;
uniform mat4 mvp;
void main(void) {
gl_Position = mvp * vec4(position.xy, -1.0, 1.0);
}
and
void main() {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
Thank you in advance
glEnableVertexAttribArray(positionIndex);
glVertexAttribPointer(positionIndex, 2, GL_FLOAT, GL_FALSE, 0, vertices);
This line is incorrect:
glDisableVertexAttribArray(positionIndex);
Don't disable it before you use it, or you'll lose it
Also, since you are using a vbo for your verts, you don't need to do this:
glVertexAttribPointer(positionIndex, 2, GL_FLOAT, GL_FALSE, 0, vertices);
Simply switch it to this:
glVertexAttribPointer(positionIndex, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
You have a vbo bound to the GPU, it will use that buffer to draw. Use the glVertexAttribPointer to define the offset into the vbo it will find the first element for the given attribute.
#kibab The suggestion I made for the vbo is correct.
He was receiving EXC_BAD_ACCESS due to his attribute not binding to his shader. See comments below. As for the mvp you are incorrect, again. The mvp is meant to convert points into a coordinate system of -1.0, -1.0, to 1.0, 1.0. Meaning his vertices should be showing given their values.
See http://flylib.com/books/2/789/1/html/2/images/03fig02.jpg.
Also changing the z coordinate will do nothing for you. Change your vertice to start at -1.0,-1.0 and go to 1.0,1.0 a full screen box just so we can get something on screen.
Assuming you aren't using any apple provided gl classes your glViewPort isn't being set.
Try using your view's bound's size
glViewport(0, 0, bouds.width, bounds.height);
Also please put your verts on 0.0 z instead of -1.0
Also lets try this, just to figure out what's going wrong!
Try not using a vertex buffer object and passing it straight over like this.
Also don't worry about disabling your attribute at the moment, lets get something on screen first.
GLfloat vertices[] = {
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f , 0.0f,
-1.0f, 1.0f , 0.0f,
1.0f, 1.0f , 0.0f,};
glVertexAttribPointer(positionIndex,3,GL_FLOAT,0,0,vertices);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);