I'm using OpenGL ES (in iOS) and am struggling with setting up a viewport with an off-center distance point.

Consider a game where you have a character in the left hand side of the screen, and some controls alpha'd over the left-hand side. The "main" part of the screen is on the right, but you still want to show whats in the view on the left. However when the character moves "forward" you want the character to appear to be going "straight", or "up" on the device, and not heading on an angle to the point that is geographically at the mid-x position in the screen.

Here's the jist of how i set my viewport up where it is centered in the middle:

// setup the camera


    const GLfloat zNear = 0.1;
    const GLfloat zFar = 1000.0;
    const GLfloat fieldOfView = 90.0; // can definitely adjust this to see more/less of the scene

    GLfloat size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0);
    CGRect rect;
    rect.origin = CGPointMake(0.0, 0.0);
    rect.size = CGSizeMake(backingWidth, backingHeight);
    glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size / (rect.size.width / rect.size.height), zNear, zFar);


    // rotate the whole scene by the tilt to face down on the dude
    const float tilt = 0.3f;
    const float yscale = 0.8f;
    const float zscale = -4.0f;
    glTranslatef(0.0, yscale, zscale);
    const int rotationMinDegree = 0;
    const int rotationMaxDegree = 180;

        glRotatef(tilt * (rotationMaxDegree - rotationMinDegree) / 2, 1.0f, 0.0f, 0.0f);

    glTranslatef(0, -yscale, -zscale);

    static float b = -25; //0;
    static float c = 0;

    // rotate by to face in the direction of the dude
    float a = RADIANS_TO_DEGREES(-atan2f(-gCamera.orientation.x, -gCamera.orientation.z));
    glRotatef(a, 0.0, 1.0, 0.0);

    // and move to where it is
    glTranslatef(-gCamera.pos.x, -gCamera.pos.y, -gCamera.pos.z);

    // draw the rest of the scene

I've tried a variety of things to make it appear as though "the dude" is off to the right: - do a translate after the frustum to the x direction - do a rotation after the frustum about the up/y-axis - move the camera with a biased lean to the left of the dude

Nothing i do seems to produce good results, the dude will either look like he's stuck on an angle, or the whole scene will appear tilted. I'm no OpenGL expert, so i'm hoping someone can suggest some ideas or tricks on how to "off-center" these model views in OpenGL. Thanks!

  • \$\begingroup\$ Have you tried drawing a picture of what you want? You could then try and figure out whether it's even physically possible or if you're stuck in some MCEscher world that can't ever be emulated with camera mathematics in a physical world. \$\endgroup\$ – Patrick Hughes Dec 6 '11 at 22:44

Typically when working with a camera system, in addition to the frustum, you need three other pieces of information: where the camera is (eye), what it's pointed at (at), and its orientation (up). When trying to solve your shoulder-view problem, it is not typical to be modifying the frustum. You should set the camera at that shoulder view by modifying "eye." To get it to look like he is walking in a straight line, I would try making the camera look along a vector that is parallel to "the dude's" vector of movement in order to make it look like he is moving in a straight line, and that is done by modifying "at." If you need to make your scene look wonky by twisting the camera (so it has a sideways orientation or such), that is when you would modify "up."

| improve this answer | |
  • \$\begingroup\$ This seems to make sense in theory, but it doesn't work for me. Naively speaking, the "eye" is always going to be in the middle of screen... so if its pointed in the direction of the dude's heading (at) then it will appear as though the dude is headed towards the center of the screen in the distance, even though both the dude and the camera are orientated with the same heading... i could be wrong, but without some sort of twist, which "almost" works, i can't get the over the should view working (that is the perfect name for it btw). Perhaps i'm wrong about where the eye is positioned? \$\endgroup\$ – user5484 Feb 16 '11 at 13:57
  • \$\begingroup\$ In perspective view, two parallel vectors will appear to be moving towards each other in the distance. Think of the typical example of railroad tracks. The rails are parallel, but they meet at one point in the distance. You can't really help that. I expect that isn't your problem, however, because at distances so close to the camera you should not be able to notice that effect that perspective has. Have you tried making them point in the same direction (as opposed to at the same point), or are these just your thoughts on it? \$\endgroup\$ – schnozzinkobenstein Feb 16 '11 at 19:21
  • \$\begingroup\$ Ah ok - this "point in the distance" issue IS my problem! The point in the distance always seems to be in the center of the device, whereas my dude is at the 3/4 position. I want the point in the distance to be 3/4 of the view as well, so that objects IN FRONT of the dude, are just "up" from it, as opposed to "up and a bit to the right". I'm not sure what you mean by "tried making THEM point in the same direction"... i think i do that now, and it doesn't help. Its only when o tilt the camera to point somewhat in the dudes direction that i can get this to almost work, but never looks good. \$\endgroup\$ – user5484 Feb 17 '11 at 1:56
  • \$\begingroup\$ What i really want to do is the following - lets say my device is 480x320. If i actually were to fake render 720x320, so that the mid point is at the 360 position, but then only show the first 480 x, THAT would give me what i want! But i don't know how to do that either ;) \$\endgroup\$ – user5484 Feb 17 '11 at 1:58
  • \$\begingroup\$ Actually that sort of worked - when i change the backingwidth above to 720 it works perfectly! However, i fear i'm rendering way too much... any comments on this would be appreciated. \$\endgroup\$ – user5484 Feb 17 '11 at 2:20

Old question, but well. It appears that instead of:

glFrustumf(-size, size,
           -size / (rect.size.width / rect.size.height),
           size / (rect.size.width / rect.size.height), zNear, zFar);

You want the frustum to be slightly off, for instance by a quarter of the screen size:

glFrustumf(-size - size / 2, size - size / 2,
           -size / (rect.size.width / rect.size.height),
           size / (rect.size.width / rect.size.height), zNear, zFar);
| improve this answer | |
  • \$\begingroup\$ I think this will not achieve the desired result since it distorts the projection matrix (I'm trying to do the same as requested here and had already tried your approach). \$\endgroup\$ – amfcosta Nov 20 '13 at 16:50

I'm trying to do the same and found two ways of doing it:

One way is to render as if the screen is bigger, so that part of the viewport goes out of the screen and the viewport center align with the screen point you want (from a comment I can see that you've done this and it works), but it makes you draw more of the scene.

Another option (if you are using a version of OpenGL ES that supports vertex shaders) is to translate the resulting vertices (of the vertex shader, which should already be in projection space). This is something like:

gl_Position = u_model_view_proj_matrix * a_vertex_position;
gl_Position.xy = gl_Position.xy + u_viewcenter * gl_Position.w;

Where u_viewcenter is a vec2 (from -1 to 1) that defines where you want the view center to be. This approach has some problems if you use the projection matrix in your code to do things such as picking or frustum clipping, since applying the projection matrix to the scene will no longer match the output of the vertex shader.

| improve this answer | |

You should be able to apply a 2D offset/scale after the projection.

Just multiply the projection matrix by a suitable translation/scale matrix (where the translations are in the -1..1 range of viewport coordinates)

| improve this answer | |
  • \$\begingroup\$ I tried this... it didn't help. I'm not sure what the scale is supposed to do. I definitely do something like the translation now, but the "point in the distance" issue still exists. \$\endgroup\$ – user5484 Feb 17 '11 at 1:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy