Search code examples
iosopengl-escore-animationscaling

Scaling a UIView containing an OpenGL context


My UIView contains an OpenGL context that renders everything on the view. I'd like to scale the OpenGL view up and down. So far, I can see 2 ways of doing it:

  1. Use a transform matrix on the UIView self.transform = CGAffineTransformMakeScale(zoom,zoom);
  2. Use glScalef on my OpenGL context

I like method #1 because it allows me to do some animations on the scaling with CoreAnimations very easily. Method #2 seems to involve more code since I would need to animate the scaling myself (probably not a big deal).

To be a bit more specific, my OpenGL view initially renders a full screen texture at 90% of its size. When I zoom in using method #1, I expect to have my OpenGL texture at 100%. However, it seems that I don't see the level of details that I should. Method #1 seems to be using an interpolation on my OpenGL view instead of making an underlying glScale call.

Are #1 and #2 equivalent?


Solution

  • They are far from same. The precision of your GLView is defined by the size of the render buffer you created. You usually call "fromDrawable" that will create the buffer of same size as is the size of your UIView (can be multiplied by content scale also used for retina). When you resize or transform your UIView you should recreate your GL buffers to be of same size as the view (I suggest not to do that very often). So try to keep the same size of your GL view if possible.

    So as for #1 you scale the view but keep the buffer resolution plus you are still drawing to the part you do not even display, so it's a bad thing.

    As for #2 you are right, animations can be a bit messed up to do them yourself. If you are only using a scale and translation you can create some parameters like float scaleFrom, float scaleTo and float currentInterpolationFactor where in "draw" call you could say:

    float currentScale = scaleFrom + (scaleTo-scaleFrom)*currentInterpolationFactor;
    glScalef(currentScale, currentScale, currentScale);
    currentInterpolationFactor += 1.0f/ANIMATION_DURATION_IN_FRAMES;
    

    where currentInterpolationFactor starts at .0f and ends with 1.0f.

    You could probably even put your animation mechanism on a separate thread... probably...

    Another interesting mechanism that is a bit more general is to interpolate the matrix itself: Since you can get your current matrix (ES1: glGetFloatv(GL_MODELVIEW_MATRIX, mtr)) and you can create your target matrix (where your animation will end) on the same principle, you can simply interpolate each component of the matrix (16 of them) the same way as you would "currentScale". Although this principle might work quite nice, it will not look very good if you rotate your scene (worst at 180 degrees angle).

    And the best way (if you ask me) to do matrix animation interpolation is by using base vectors: This means you need to use "LookAt" principle for witch you need 2D: "center vector" and "right vector" or for 3D: "position vector", "way vector" and "up vector". (You will find on web how to create a matrix from this vectors). As for their interpolation: "position vector" has a linear interpolation from vectors A to B: A' = A + (B-A)*interpolationFactor. As for all the other vectors, they need a polar interpolation meaning that you need to find their "start angles" and "end angles" and use linear interpolation on them and do the same for their length. After that recreate the interpolated base vectors and create the current matrix with them.

    Now consider what will you be using and what you will really need in the end. This 3 methods are from easiest to hardest but also from less dynamical to more dynamical.