OpenGL ES 2.0 equivalent of glOrtho()? - iphone-sdk-3.0

In my iphone app, I need to project 3d scene into the 2D coordinates of the screen for some calculations. My objects go through various rotations, translations and scaling. So I figured I need to multiply the vertices with ModelView matrix first, then I need to multiply it with the Orthogonal projection matrix.
First of all am on the right track?
I have the Model View Matrix, but need the projection matrix. Is there a glOrtho() equivalent in ES 2.0?

mat4 projectionMatrix = mat4( 2.0/768.0, 0.0, 0.0, -1.0,
0.0, 2.0/1024.0, 0.0, -1.0,
0.0, 0.0, -1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
gl_Position = position;
gl_Position *= rotationMatrix;
gl_Position.x -= translateX;
gl_Position.y -= translateY;
gl_Position *= projectionMatrix;
For a fixed resolution (1024x768 in my case for iPad) I used this matrix and everything works like charm :)
Here is complete description what values you need to put in your matrix: glOrtho.html

The manual page for glOrtho() describes the equivalent operations, so as long as you have the matrix handy should be able re-implement it.

Related

OpenGL ES 2.0, quad does not change colour when background is changed

I have drawn a circle in a quad on OpenGL ES 2.0. The code in the fragment shader takes the centre and radius that has been set and creates a circle within the quad. This worked fine until I tried to change the colour of the background as the quad still shows up blank and does not get filled with the background colour/texture. Is there an easy way to make the quad fill with the same colour/texture as the background whilst also keeping the circle on show?
The code in the fragment shader is as follows:
"varying highp vec2 textureCoordinate;\n"
"const highp vec2 center = vec2(0.5, 0.5);\n"
"const highp float radius = 0.5;\n"
"void main()\n"
"{\n"
"highp float distanceFromCenter = distance(center, textureCoordinate);\n"
"lowp float checkForPresenceWithinCircle = step(distanceFromCenter, radius);\n"
"gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * checkForPresenceWithinCircle;\n"
"}\n"
The trick is not to fill the quad with the background, but to avoid replacing it outside the area covered by the circle.
Your fragment shader will always output a value - even if it's outside the circle. That is, if checkForPresenceWithinCircle is 0.0, gl_FragColor gets assigned vec4(0.0, 0.0, 0.0, 0.0) - transparent black.
I think what you are looking for is the discard keyword, which prevents the shader from outputting anything for that fragment. Something like:
if ( checkForPresenceWithinCircle > 0.0 )
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
else
discard;
Since you know that the alpha will be 0.0 outside the circle and 1.0 within, you could also achieve the same effect using alpha blending from the API-side:
draw_background();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
draw_circle();
glDisable(GL_BLEND);
The alpha part of gl_FragColor is normally read from the texture, like this:
vec4 vTexture = texture2D(gsuTexture0, gsvTexCoord);
gl_FragColor = vTexture;
Also, be sure your color buffer clear has the alpha set to 0.0, like this:
glClearColor(fRed, fGreen, fBlue, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
And there is also the problem that you can't use alpha textures with the Android Bitmap class as discussed here:
http://software.intel.com/en-us/articles/porting-opengl-games-to-android-on-intel-atom-processors-part-1/

CALayer sublayerTransform (CATransform3D) - how can I move "camera"?

Attempt A
this code
CATransform3D perspectiveTransform = CATransform3DIdentity;
perspectiveTransform.m34 = -1.0 / 1000.0;
perspectiveTransform = CATransform3DRotate(perspectiveTransform, angle / 2.0, 0.0, 1.0, 0.0);
self.sublayerTransform = perspectiveTransform;
gives this result
The red frame is the background color of the layer
Attempt B
This code
CATransform3D perspectiveTransform = CATransform3DIdentity;
perspectiveTransform.m34 = -1.0 / 1000.0;
// following line is added
perspectiveTransform = CATransform3DTranslate(perspectiveTransform, -width / 2.0, 0, 0);
perspectiveTransform = CATransform3DRotate(perspectiveTransform, angle / 2.0, 0.0, 1.0, 0.0);
self.sublayerTransform = perspectiveTransform;
gives this result
The red frame is the background color of the layer
So what is wrong?
In the rendered example from "Attempt A" is seen from left center and therefore the fold is seen from the wrong angle (left fold gets slightly thinner than right fold)
By translating it on the x-axis, as in "Attempt B", I am able to get it rendered correctly, but then occurs another mistake: the content is now out of bounds (red rectangle is bounds).
How can I make the camera be from the middle?
Well, it seems like changing the anchorpoint is the easiest. I just need to change some other transforms (not revealed in post) to make it work.

From CATransform3D to CGAffineTransform

I'm using the following function to apply a pulse effect to a view
- (void)pulse {
CATransform3D trasform = CATransform3DScale(self.layer.transform, 1.15, 1.15, 1);
trasform = CATransform3DRotate(trasform, angle, 0, 0, 0);
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform"];
animation.toValue = [NSValue valueWithCATransform3D:trasform];
animation.autoreverses = YES;
animation.duration = 0.3;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.repeatCount = 2;
[self.layer addAnimation:animation forKey:#"pulseAnimation"];
}
I would like to obtain the same result using the CGAffineTransform self.transform instead of the CATransform3D self.layer.transform. Is this possible?
It's possible convert a CATransform3D to a CGAffineTransform, but you will lose some capabilities. I found it useful to convert the aggregate transform of a layer and its ancestors into a CGAffineTransform so I could render it with Core Graphics. The constraints are:
Your input will be treated as flat in the XY plane
Your output will be treated as flat in the XY plane too
Perspective / foreshortening from .m34 will be neutralized
If that sounds OK for your purposes:
// m13, m23, m33, m43 are not important since the destination is a flat XY plane.
// m31, m32 are not important since they would multiply with z = 0.
// m34 is zeroed here, so that neutralizes foreshortening. We can't avoid that.
// m44 is implicitly 1 as CGAffineTransform's m33.
CATransform3D fullTransform = <your 3D transform>
CGAffineTransform affine = CGAffineTransformMake(fullTransform.m11, fullTransform.m12, fullTransform.m21, fullTransform.m22, fullTransform.m41, fullTransform.m42);
You will want to do all your work in 3D transforms first, say by concatenating from your superlayers, and then finally convert the aggregate CATransform3D to a CGAffineTransform. Given that layers are flat to begin with and render onto a flat target, I found this very suitable since my 3D rotations became 2D shears. I also found it acceptable to sacrifice foreshortening. There's no way around that because affine transforms have to preserve parallel lines.
To render a 3D-transformed layer using Core Graphics, for instance, you might concatenate the transforms (respecting anchor points!), then convert to affine, and finally:
CGContextSaveGState(context);
CGContextConcatCTM(context, affine);
[layer renderInContext:context];
CGContextRestoreGState(context);
Of course. If you do a search on CGAffineTransform in the Xcode docs, you'll find a chapter titled "CGAffineTransform Reference". In that chapter is a section called "Functions". It includes functions that are equivalent to CATransform3DScale (CGAffineTransformScale ) and CATransform3DRotate (CGAffineTransformRotate).
Note that your call to CATransform3DRotate doesn't really make sense. You need to rotate around an axis, and you're passing 0 for all 3 axes. Typcially you want to use CATransform3DRotate(trasform, angle, 0, 0, 1.0) to rotate around the Z axis. To quote the docs:
If the vector has zero length the behavior is undefined.
You can use CATransform3DGetAffineTransform to convert CATransform3d to CGAffineTransform.
let scaleTransform = CATransform3DMakeScale( 0.8, 0.8, 1)
imageview.transform = CATransform3DGetAffineTransform(scaleTransform)

why gradient colors and aliasing under Lion are different compare to Snow Leopard?

Just came across one weird problem. I generate programmatically a gradient inside round cornered square like so:
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceAdobeRGB1998);
CGContextRef mcontext = [[NSGraphicsContext currentContext] graphicsPort];
size_t capacityBarGradientNumLocations = 11;
CGGradientRef Gradient;
CGFloat capacityBarGradientLocations[11] = { /* skipped */ };
CGFloat capacityBarGradientColors[44] = { /* skipped */ };
Gradient = CGGradientCreateWithColorComponents (colorSpace, capacityBarGradientColors, capacityBarGradientLocations, capacityBarGradientNumLocations);
CGContextDrawLinearGradient(mcontext,Gradient, CGPointMake(0,baroffset), CGPointMake(0,barheight+baroffset), 0);
I've just noticed that the same code produces completely different results between Lion and Snow Leopard on the same mac (I have a dual boot) and monitor with the same color profiles. Please take a look at the screenshot:
Left part if taken under Lion, the right one - under Snow Leopard. How can I fix my gradient appearance under Lion?
Some extra info regarding the above picture. I've used a black color with variable alpha to generate semi transparent gradient i.e:
CGFloat capacityBarGradientColors[44] =
{ 0.0, 0.0, 0.0, .65,
0.0, 0.0, 0.0, .35,
/* skipped */
0.0, 0.0, 0.0, .35,
0.0, 0.0, 0.0, .65,
};
I thought maybe there is something wrong with the way i use alpha. Decided to use actual colors with Multiply blending mode and alpha set to 1.0 :
CGFloat capacityBarGradientColors[44] =
{ 0x59/255., 0x59/255., 0x59/255., 1.0,
0xa6/255., 0xa6/255., 0xa6/255., 1.0,
/* skipped */
0xa6/255., 0xa6/255., 0xa6/255., 1.0,
0x59/255., 0x59/255., 0x59/255., 1.0,
};
Under Snow Leopard I've got the result I've been looking for. But under Lion I've got completely different picture. take a look at the screenshot:
Left - Lion, right - Snow Leopard
Here is the code changes I did:
Gradient = CGGradientCreateWithColorComponents (colorSpace, capacityBarGradientColors, capacityBarGradientLocations, capacityBarGradientNumLocations);
CGContextSetBlendMode(mcontext, kCGBlendModeMultiply);
CGContextDrawLinearGradient(mcontext,Gradient, CGPointMake(0,baroffset), CGPointMake(0,barheight+baroffset), 0);
CGContextSetBlendMode(mcontext, kCGBlendModeNormal);
Could be something to do with the color space behaving differently (gamma?) from one OS version to the other. Do you have the same problem if you use kCGColorSpaceGenericRGB or kCGColorSpaceGenericRGBLinear?

Linear gradient aliasing with CoreGraphics

I'm trying to emulate the color tint effect from the UITabBarItem.
When I draw a linear gradient at an angle, I get visible aliasing in the middle part of the gradient where the two colors meet at the same location. Left is UITabBarItem, right is my gradient with visible aliasing (stepping):
Here is the snippet of relevant code:
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextSaveGState(c);
CGContextScaleCTM(c, 1.0, -1.0);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat components[16] = {1,1,1,1,
109.0/255.0,175.0/255.0,246.0/255.0,1,
31.0/255.0,133.0/255.0,242.0/255.0,1,
143.0/255.0,194.0/255.0,248.0/255.0,1};
CGFloat locations[4] = {0.0, 0.62, 0.62, 1};
CGGradientRef colorGradient =
CGGradientCreateWithColorComponents(colorSpace, components,
locations, (size_t)4);
CGContextDrawLinearGradient(c, colorGradient, CGPointZero,
CGPointMake(size.width*1.0/3.9, -size.height),0);
CGGradientRelease(colorGradient);
CGColorSpaceRelease(colorSpace);
CGContextRestoreGState(c);
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
What do I need to change, to get a smooth angled gradient like in UITabBarItem?
What is the interpolation quality of your context set to? CGContextGetInterpolationQuality()/CGContextSetInterpolationQuality(). Try changing that if it's too low.
If that doesn't work, I'm curious what happens if you draw the gradient vertically (0,Ymin)-(0,Ymax) but apply a rotation transformation to your context...
As a current workaround, I draw the gradient at double resolution into an image and then draw the image with original dimensions. The image scaling that occurs, takes care of the aliasing. At the pixel level the result is not as smooth as in the UITabBarItem, but that probably uses an image created in Photoshop or something similar.