How to rotate around x and y in OpenGL ES 1.1? - ios

I am drawing a texture with 4 vertices in OpenGL ES 1.1.
It can rotate around z:
glRotatef(20, 0, 0, 1);
But when I try to rotate it around x or y like a CALayer then the texture just disappears completely. Example for rotation around x:
glRotatef(20, 1, 0, 0);
I also tried very small values and incremented them in animation loop.
// called in render loop
static double angle = 0;
angle += 0.005;
glRotatef(angle, 1, 0, 0);
At certain angles I see only the edge of the texture. As if OpenGL ES would clip away anything that goes into depth.
Can the problem be related to projection mode? How would you achieve a perspective transformation of a texture like you can do with CALayer transform property?

The problem is most likely in one of the glFrustumf or glOrthof. The last parameter in this 2 calls will take z-far and it should be large enough for the primitive to be drawn. If a side length of the square is 1.0 and centre is in (.0, .0, .5) then z-far should be (> 1.0) to see the square rotated 90 degrees around X or Y axis. Though note these can depend on other matrix operations as well (translating the object or using tools like lookAt).
Making this parameter large enough should solve your problem.
To achieve a perspective transformation use glFrustumf instead of glOrthof.

Related

What exactly does thread_position_in_grid mean in a metal compute kernel shader?

Although this is an inappropriate use of a compute shader I was doing some experiments to determine if I could use one to produce the general UV gradient where one channel of the image goes linearly from 0-1 across the x axis and the other channel goes from 0-1 across the y axis of the image. However I became confused when I generated this image by varying the b value of a texture by the thread_position_in_grid.x value divided by the image width. I edited the pixel of the texture at the thread_position_in_grid position:
Yes it was a gradient but it certainly did not appear to be the gradient from 0-1 that I wanted. I dropped it into an image editor and sure enough it was not linear. (The part added below shows what a linear gradient from 0-1 would look like)
It would appear that I do not understand what exactly the thread_position_in_grid value means. I know it has something to do with the threads per thread-groups and thread execution width but I dont exactly understand what. I suppose my end goal is to know whether it would be possible to generate the gradient below in a compute shader however I dont understand what is going on.
For reference I was working with a 100x100 texture with the following thread settings. Really I dont know why I use these values but this is what I saw recommended somewhere so I am sticking with them. I would love to be able to generalize this problem to other texture sizes as well including rectangles.
let w = greenPipeline.threadExecutionWidth
let h = greenPipeline.maxTotalThreadsPerThreadgroup / w
let threadsPerThreadgroup = MTLSizeMake(w, h, 1)
let threadgroupsPerGrid = MTLSize(width: (texture.width + w - 1) / w,
height: (texture.height + h - 1) / h,
depth: 1)
encoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)
And my shader looks like this:
kernel void green(texture2d<float, access::write> outputTexture [[texture(0)]],
uint2 position [[thread_position_in_grid]])
{
if (position.x >= outputTexture.get_width() || position.y >= outputTexture.get_height()) {
return;
}
outputTexture.write(float4(position.x / 100.0, 0.0, 0.0, 0.0), position);
}
Two things about this shader confuse me because I cannot explain them:
I am using position as the coordinate to write to on the texture so
it bothers me that position doesnt work to generate the gradient.
You cannot reaplace position.x / 100.0 value with position.x / outputTexture.getWidth() even though it should also be 100. Doing so causes a black image. Yet when I made a shader that colored everything with outputTexture.getWidth() as its value it did indeed shade everything to a value equivalent to 100 (or more accurately 101 because of rounding)
It is ok to use position to check if the kernel is within bounds but not to create the UV gradient.
What is going on?
The thread_position_in_grid means whatever you want it to mean because you decide how large the grid is and what each thread in the grid does.
In your example, thread_position_in_grid is the pixel coordinate in the texture because your grid size is equal to the number of pixels in the texture (rounded up to a multiple of the pipeline's max thread execution width).
You can see this if you change the threadGroupsPerGrid to:
let threadgroupsPerGrid = MTLSize(width: (texture.width/2 + w - 1) / w,
height: (texture.height/2 + h - 1) / h,
depth: 1)
Now only the top quarter of your texture should be filled in because the grid only covers half the texture's width and height.
As to why your texture looks weird, it's probably related to the pixel format. After all, you're writing into the red color component and your texture comes out as blue.

Rotation, Translation and Default Camera Location

I am just playing around the template setup in MTKView; and, I have been trying to understand the followings:
Default location of the camera.
The default location when creating primitives using MDLMesh and MTKMesh.
Why does a rotation involve also a translation.
Relevant code:
matrix_float4x4 base_model = matrix_multiply(matrix_from_translation(0.0f, 0.0f, 5.0f), matrix_from_rotation(_rotation, 0.0f, 1.0f, 0.0f));
matrix_float4x4 base_mv = matrix_multiply(_viewMatrix, base_model);
matrix_float4x4 modelViewMatrix = matrix_multiply(base_mv, matrix_from_rotation(_rotation, 0.0f, 1.0f, 0.0f));
The preceding code is from the _update method by the template; evidently, it is trying to rotate the model instead of the camera. But what baffles me is the fact that it requires also a translation. I have read claims such as "because it always rotates at (0, 0, 0)". But why (0, 0, 0), if the object is placed somewhere else? Also, it appears to me that the camera is looking at the positive z-axis (question 1) instead of the usual negative z-axis because if I change:
matrix_float4x4 base_model = matrix_multiply(matrix_from_translation(0.0f, 0.0f, 5.0f), matrix_from_rotation(_rotation, 0.0f, 1.0f, 0.0f));
to:
matrix_float4x4 base_model = matrix_multiply(matrix_from_translation(0.0f, 0.0f, -5.0f), matrix_from_rotation(_rotation, 0.0f, 1.0f, 0.0f));
nothing will be displayed on the screen because it appears that the object is behind the camera, which means that the camera is looking at the positive z-axis.
If I set matrix_from_translation(0.0f, 0.0f, 0.0f) (all zeros), the object simply rotate not on the y-axis (question 3) as I expected.
I have tried to find out where the MDLMesh and MTKMesh is placed by default (question 2), but I could not find a property that logs its position. The following is, also by the template, how the primitive is created:
MDLMesh *mdl = [MDLMesh newBoxWithDimensions:(vector_float3){2,2,2} segments:(vector_uint3){1,1,1}
geometryType:MDLGeometryTypeTriangles inwardNormals:NO
allocator:[[MTKMeshBufferAllocator alloc] initWithDevice: _device]];
_boxMesh = [[MTKMesh alloc] initWithMesh:mdl device:_device error:nil];
Without knowing its location generated by the above method, it hinders my understanding of how the rotation and translation work and the default location the camera in Metal.
Thanks.
I think the order in which the matrices are written in the code somewhat obfuscates the intent, so I've boiled down what's actually happening into the following pseudocode to make it easier to explain.
I've replaced that last matrix with the one from the template, since your modification just has the effect of doubling the rotation about the Y axis.
modelViewMatrix = identity *
translate(0, 0, 5) *
rotate(angle, axis(0, 1, 0)) *
rotate(angle, axis(1, 1, 1))
Since the matrix is multiplied on the left of the vector in the shader, we're going to read the matrices from right to left to determine their cumulative effect.
First, we rotate the cube around the axis (1, 1, 1), which passes diagonally through the origin. Then, we rotate about the cube about the Y axis. These rotations combine to form a sort of "tumble" animation. Then, we translate the cube by 5 units along the +Z axis (which, as you observe, goes into the screen since we're regarding our world as left-handed). Finally, we apply our camera transformation, which is hard-coded to be the identity matrix. We could have used an additional positive translation along +Z as the camera matrix to move the cube even further from the camera, or a negative value to move the cube closer.
To answer your questions:
There is no default location for the camera, other than the origin (0, 0, 0) if you want to think of it like that. You "position" the camera in the world by multiplying the vertex positions by the inverse of the transformation that represents how the camera is placed in the world.
Model I/O builds meshes that are "centered" around the origin, to the extent this makes sense for the shape being generated. Cylinders, ellipsoids, and boxes are actually centered around the origin, while cones are constructed with their apex at the origin and their axis extending along -Y.
The rotation doesn't really involve the translation as much as it's combined with it. The reason for the translation is that we need to position the cube away from the camera; otherwise we'd be inside it when drawing.
One final note on order of operations: If we applied the translation before the rotation, it would cause the box to "orbit" around the camera, since as you note, rotations are always relative to the origin of the current frame of reference.

Arbitrary texture displacement around the Y axis

I have a single cloud texture that I want to displace arbitrarily along the Y ("vertical") axis of a SCNNode spherical geometry, to give the illusion there are many different textures of clouds.
I read the docs about SCNMaterialProperty, CATransform3D rotation, but I'm completely lost. In a 3D program, you can set your texture "origin" along the X, Y and Z axis -- what is the equivalent in Scene Kit / Core Animation ?
Thanks for your help!
SCNMaterialProperty has a contentsTransform property that allows you to animate texture coordinates. You can also use shader modifiers if you want more control and depending on th effect you want to achieve.
In the Bananas sample code from WWDC 2014 this technique is used to animate the smoke emitted by the volcano in the background.
I finally ended up with this:
self.cloudNode.rotation = SCNVector4Make(0.0,
1.0,
0.0,
arc4random_uniform(360)*M_PI/180.0);
I'm not a maths genius anyway.

Servo control with OpenCV

I will track an object according to the coordinates that I read from OpenCV. The thing is that: in order to turn my servo 120 degrees in the positive way, I need to send 3300 more to servo. (for 1 degree I need to send 27.5 more to servo).
Now I need to find a relation with the coordinates that I read from OpenCV and the value I need to send to servo. However I could not understand OpenCV's coordinates. For example I do not change object's height, I only decrease the distance between the object and the camera, in that case, only z value should decrease however it seems like x value also changes significantly. What is the reason for that?
In case, I have a problem with my code (maybe x is not changing, but I am reading it wrong), could you please give me information about OpenCV coordinates and how to interpret it? As I said in the beginning, I need to find a relation like how many degrees turn of my servo correspond to how much change in the balls X coordinates that I read from OpenCV?
Regards
edit1 for #FvD:
int i;
for (i = 0; i < circles->total; i++)
{
float *p = (float*)cvGetSeqElem(circles, i);
printf("Ball! x=%f y=%f r=%f\n\r",p[0],p[1],p[2] );
CvPoint center = cvPoint(cvRound(p[0]),cvRound(p[1]));
CvScalar val = cvGet2D(finalthreshold, center.y, center.x);
if (val.val[0] < 1) continue;
cvCircle(frame, center, 3, CV_RGB(0,255,0), -1, CV_AA, 0);
cvCircle(frame, center, cvRound(p[2]), CV_RGB(255,0,0), 3, CV_AA, 0);
cvCircle(finalthreshold, center, 3, CV_RGB(0,255,0), -1, CV_AA, 0);
cvCircle(finalthreshold, center, cvRound(p[2]), CV_RGB(255,0,0), 3, CV_AA, 0);
}
In general, there are no OpenCV coordinates, but you will frequently use the columns and rows of an image matrix as image coordinates.
If you have calibrated your camera, you can transform those image coordinates to real-world coordinates. In the general case, you cannot pinpoint the location of an object in space with a single camera image, unless you have a second camera (stereo vision) or supplementary information about the scene, e.g. if you are detecting objects on the ground and you know the orientation and position of your camera relative to the ground plane. In that case, moving your ball towards the camera would result in unexpected movement because the assumption that it is lying on the ground is violated.
The coordinates in the code snippet you provided are image coordinates. The third "coordinate" is the radius of the circular blob detected in the webcam image and the first two are the column and row of the circle's center in the image.
I'm not sure how you are moving the ball in your test, but if the center of the ball stays stationary in your images and you still get differing x coordinates, you should look into the detection algorithm you are using.

Texture Sampling Coordinates to Render a Sprite

Let's say we have a texture (in this case 8x8 pixels) we want to use as a sprite sheet. One of the sub-images (sprite) is a subregion of 4x3 inside the texture, like in this image:
(Normalized texture coordinates of the four corners are shown)
Now, there are basically two ways to assign texture coordinates to a 4px x 3px-sized quad so that it effectively becomes the sprite we are looking for; The first and most straightforward is to sample the texture at the corners of the subregion:
// Texture coordinates
GLfloat sMin = (xIndex0 ) / imageWidth;
GLfloat sMax = (xIndex0 + subregionWidth ) / imageWidth;
GLfloat tMin = (yIndex0 ) / imageHeight;
GLfloat tMax = (yIndex0 + subregionHeight) / imageHeight;
Although when first implementing this method, ca. 2010, I realized the sprites looked slightly 'distorted'. After a bit of search, I came across a post in the cocos2d forums explaining that the 'right way' to sample a texture when rendering a sprite is this:
// Texture coordinates
GLfloat sMin = (xIndex0 + 0.5) / imageWidth;
GLfloat sMax = (xIndex0 + subregionWidth - 0.5) / imageWidth;
GLfloat tMin = (yIndex0 + 0.5) / imageHeight;
GLfloat tMax = (yIndex0 + subregionHeight - 0.5) / imageHeight;
...and after fixing my code, I was happy for a while. But somewhere along the way, and I believe it is around the introduction of iOS 5, I started feeling that my sprites weren't looking good. After some testing, I switched back to the 'blue' method (second image) and now they seem to look good, but not always.
Am I going crazy, or something changed with iOS 5 related to GL ES texture mapping? Perhaps I am doing something else wrong? (e.g., the vertex position coordinates are slightly off? Wrong texture setup parameters?) But my code base didn't change, so perhaps I am doing something wrong from the beginning...?
I mean, at least with my code, it feels as if the "red" method used to be correct but now the "blue" method gives better results.
Right now, my game looks OK, but I feel there is something half-wrong that I must fix sooner or later...
Any ideas / experiences / opinions?
ADDENDUM
To render the sprite above, I would draw a quad measuring 4x3 in orthographic projection, with each vertex assigned the texture coords implied in the code mentioned before, like this:
// Top-Left Vertex
{ sMin, tMin };
// Bottom-Left Vertex
{ sMin, tMax };
// Top-Right Vertex
{ sMax, tMin };
// Bottom-right Vertex
{ sMax, tMax };
The original quad is created from (-0.5, -0.5) to (+0.5, +0.5); i.e. it is a unit square at the center of the screen, then scaled to the size of the subregion (in this case, 4x3), and its center positioned at integer (x,y) coordinates. I smell this has something to do too, especially when either width, height or both are not even?
ADDENDUM 2
I also found this article, but I'm still trying to put it together (it's 4:00 AM here)
http://www.mindcontrol.org/~hplus/graphics/opengl-pixel-perfect.html
There's slightly more to this picture than meets the eye, the texture coordinates are not the only factor in where the texture gets sampled. In your case I believe the blue is probably what want to have.
What you ultimately want is to sample each texel in center. You don't want to be taking samples on the boundary between two texels, because that either combines them with linear sampling, or arbitrarily chooses one or the other with nearest, depending on which way the floating point calculations round.
Having said that, you might think that you don't want to have your texcoords at (0,0), (1,1) and the other corners, because those are on the texel boundary. However an important thing to note is that opengl samples textures in the center of a fragment.
For a super simple example, consider a 2 by 2 pixel monitor, with a 2 by 2 pixel texture.
If you draw a quad from (0,0) to (2,2), this will cover 4 pixels. If you texture map this quad, it will need to take 4 samples from the texture.
If your texture coordinates go from 0 to 1, then opengl will interpolate this and sample from the center of each pixel, with the lower left texcoord starting at the bottom left corner of the bottom left pixel. This will ultimately generate texcoord pairs of (0.25, 0.25), (0.75,0.75), (0.25, 0.75), and (0.75, 0.25). Which puts the samples right in the middle of each texel, which is what you want.
If you offset your texcoords by a half pixel as in the red example, then it will interpolate incorrectly, and you'll end up sampling the texture off center of the texels.
So long story short, you want to make sure that your pixels line up correctly with your texels (don't draw sprites at non-integer pixel locations), and don't scale sprites by arbitrary amounts.
If the blue square is giving you bad results, can you give an example image, or describe how you're drawing it?
Picture says 1000 words:

Resources