(with the help of GLKit)
There are many ways to do this, its often used in paint programs.
Philip Rideout’s iPhone 3D Programming has a way to do it in C++, but its hard to abstract the method from the code.
There is a nice article written here which gives the method I used.
In my case the lines are temporary exhaust lines – so I don’t need to bake the lines into a texture as the above solution does.
The method just draws line segments from point to point as rectangles. Each line segment is a sprite which is rendered which ever way you render your OpenGL sprites.
Here is a picture of a ship emitting an exhaust – that uses the code below – the ship is moving very fast – so the sharp angles don’t matter so much.
And below is the code I wrote to do the lines:
//requires GLKMath //A sprite is drawn for every line segment //in decision method somewhere paintLineFrame = 30; //draw a segment for the next 30 update methods // in ship sprite update method //a section of the line is drawn if (paintLineFrame > 0) { //paint from previous point to current point [self paintLineSpriteAtPoint:previousLocation toPoint:location width:8]; } (void)paintLineSpriteAtPoint:(CGPoint)point1 toPoint:(CGPoint)point2 width:(int)width { //don't draw anything if the sprite has not moved if (point1.x == point2.x && point1.y == point2.y) { return; } //get the sprite from the pool of line sprites Sprite *lineSprite = [self.gameController.resourceController dequeueLineSegment]; if (lineSprite) { //setup lineSprite>spriteType = kLineSegment; //set the appropriate rainbow texture [lineSprite reTexRect:lineTextureRect]; //set how long you want this segment of the line to hang about lineSprite>lineSegmentFrame = self.gameController>slowFrameRate ? 15 : 30; //put a bit of alpha on it [lineSprite reColourR:255 G:255 B:255 A:125]; } //still do the calcs if no line sprite GLKVector2 v1 = PTV(point1); //PTV: point to vector macro GLKVector2 v2 = PTV(point2); GLKVector2 A; GLKVector2 B; if (firstLineSegment) { //the first painted segment goes from the previous point to the current point  and we need to calculate the edges of the rectangle firstLineSegment = NO; GLKVector2 normal = GLKVector2Normalize(GLKVector2Subtract(v1, v2)); // find the vector in the direction from point 1 to point 2 and normalise it GLKVector2 rNormal = GLKVector2Normalize(GLKVector2Subtract(v2, v1)); // find the vector in the direction from point 2 to point 1 and normalise it GLKVector2 nA = GLKVector2Make(normal.y, +normal.x); //find the perpendicular vector of the vector from point1 to point 2 (from origin 0,0) GLKVector2 nB = GLKVector2Make(normal.y, normal.x); //find the other perpendicular vector of the vector from point1 to point 2 GLKVector2 nC = GLKVector2Make(rNormal.y, +rNormal.x); //find the perpendicular vector of the vector from point 2 to point 1 GLKVector2 nD = GLKVector2Make(rNormal.y, rNormal.x); //find the other perpendicular vector of the vector from point 2 to point 1 A = GLKVector2Add(v1, GLKVector2MultiplyScalar(nA, width)); //multiply these perpendicular vectors by the chosen width B = GLKVector2Add(v1, GLKVector2MultiplyScalar(nB, width)); C = GLKVector2Add(v2, GLKVector2MultiplyScalar(nC, width)); D = GLKVector2Add(v2, GLKVector2MultiplyScalar(nD, width)); //et voila four corners of the rect (A,B,C,D) } else { //for other segments we want to reuse the previously calculated C and D and make them A and B. //so only need to calculate the new C and D. GLKVector2 rNormal = GLKVector2Normalize(GLKVector2Subtract(v2, v1)); GLKVector2 nC = GLKVector2Make(rNormal.y, +rNormal.x); GLKVector2 nD = GLKVector2Make(rNormal.y, rNormal.x); A = D; B = C; C = GLKVector2Add(v2, GLKVector2MultiplyScalar(nC, width)); D = GLKVector2Add(v2, GLKVector2MultiplyScalar(nD, width)); } if (lineSprite) { //update your sprites coordinates here  eg convert A,B,C,D to a CGRect or whatever } paintLineFrame; }
Extras

Here are some additions that might be nice:
 have a different start and end textures than the main line texture for the sprite,
 if the distance between point 1 and point 2 is large split the line up into two or more segments (you’ll have to calculate the path using the velocity),
 make the line thicker and thinner depending on velocity.