1
\$\begingroup\$

I'm trying to figure out how to perform tiled rendering of my 3d scene (OpenGL). The motivation is to cut the scene up into several textures, combining them into a single image for saving at a very high resolution, or for printing.

I've done some reading on this and apparently I just need to put appropriate parameters into my projection matrix for each tile in the scene. This is where I'm failing (miserably). I can't see where or what to use for it.

My perspective projection matrix is pretty standard, like this:

const T fov2 = Math::Tan(fov / 2), extent = (farPlane - nearPlane);

assert(fov2 != 0);

Matrix4x4<T> r;

r.m11 = 1 / (aspect * fov2);    r.m12 = 0;          r.m13 = 0;                                      r.m14 =  0;
r.m21 = 0;                      r.m22 = 1 / fov2;   r.m23 = 0;                                      r.m24 =  0;
r.m31 = 0;                      r.m32 = 0;          r.m33 = -(farPlane + nearPlane) / extent;       r.m34 = -1;
r.m41 = 0;                      r.m42 = 0;          r.m43 = -(2.f * farPlane * nearPlane) / extent; r.m44 =  0;

return r;

I tried an implementation of glFrustum (no longer available as I'm using core profile of course) as follows:

Matrix4x4<T> r(InitialiseAs::InitialiseIdentity);

r.m11 = 2.0f * nearPlane / (right - left);  r.m12 = 0;                                  r.m13 = (right + left) / (right - left);                    r.m14 = 0;
r.m21 = 0;                                  r.m22 = 2.0f * nearPlane / (top - bottom);  r.m23 = (top + bottom) / (top - bottom);                    r.m24 = 0;
r.m31 = 0;                                  r.m32 = 0;                                  r.m33 = -(farPlane + nearPlane) / (farPlane - nearPlane);   r.m34 = -(2.0f * farPlane * nearPlane) / (farPlane - nearPlane);
r.m41 = 0;                                  r.m42 = 0;                                  r.m43 = -1;                                                 r.m44 = 0;

return r;

But, alas I'm not sure what parameters I need to use for left, top, right and bottom when iterating across the scene, viewport or screen.

Can anyone assist me?

Edit: I found this example (unfortunately D3D) of someone doing something similar. The author seems to be just multiplying _11 and _22 by the width (scale up) and then modifying _31 and _32 according to the current tile position - iterating through the whole grid.

I did try this but I got a black screen of doom.

\$\endgroup\$
  • \$\begingroup\$ Not putting this as an answer because I'm not that fluent in it, but googling on "off axis projection" and "off center projection matrix" I found en.wikibooks.org/wiki/GLSL_Programming/Vertex_Transformations, "oblique perspective projection" on that page seems to be what you're looking for. \$\endgroup\$ – david van brink Jan 23 '15 at 17:41
  • \$\begingroup\$ Oh thanks for that. Haven't come across those terms before. \$\endgroup\$ – Robinson Jan 23 '15 at 17:49
  • \$\begingroup\$ Found an answer on stackoverflow (datenwolf's answer). \$\endgroup\$ – Robinson Jan 26 '15 at 15:19
  • \$\begingroup\$ @Robinson You can provide the solution in your own words here and how it applied to your situation. \$\endgroup\$ – MichaelHouse Jan 26 '15 at 15:39
1
\$\begingroup\$

From Datenwolf in this answer on stackoverflow:

We can render tiles with a 2-loop:

for m in 0 to M: 
    for n in 0 to N:
        render_tile(n, m)

where render_tile(n, m) starts by setting the viewport to the surface size (render target):

glViewport(0, 0, W_t, H_t), for tile width and height W_t, H_t

Somehow we've to shift the projection along with the tile (n, m) in the projection plane. The projection plane is also known as the 'near' plane. The extents of the near plane are right - left and top - bottom, so we're splitting the near plane into tiles of size (right - left) / N×(top - bottom) / M so that's what we need to use as shift step size:

shift_X = (right - left) / N
shift_Y = (top - bottom) / M

glMatrixMode(GL_PROJECTION)
glLoadIdentity()

switch Projection:
    case Ortho:
        glOrtho(left + shift_X * n, left + shift_X * (n+1), 
            bottom + shift_Y * m, bottom + shift_Y * (n+1),
            near, far)
    case Perspective:
        glFrustum( left + shift_X * n, left + shift_X * (n+1), 
            bottom + shift_Y * m, bottom + shift_Y * (n+1),
            near, far)

render_scene()

How we get left, right, top, bottom for perspective is:

right = -0.5 * tan(fov) * near
left = -right;
top = aspect * right
bottom = -top
\$\endgroup\$
  • 1
    \$\begingroup\$ Note that glOrtho, glFrustum, and all gl matrix manipulations are deprecated \$\endgroup\$ – Khlorghaal Jan 27 '15 at 0:07
  • \$\begingroup\$ Yes, I've got my own implementation of glFrustum (and glm has one too). \$\endgroup\$ – Robinson Jan 27 '15 at 10:46
1
\$\begingroup\$

All you have to to is set glViewport then invert its transformation.

Create a matrix
s= tilecount; for(int x=0; x!=tilecount; x++) for(int y=0; y!=tilecount; y++)
[s 0 0 x]
[0 s 0 y]
[0 0 1 0]
[0 0 0 1]

And multiply it * your modelviewprojection

However this only works directly for ortho, for perspective, you'll have to w divide manually then apply this matrix.

\$\endgroup\$
  • \$\begingroup\$ Thanks for this. I tried making the above and multiplying the projection matrix by it and got a weird fish-eye projection. So where do I perform the multiply? My shader is vec4 position = Model_WorldView * vec4(attrib_Position, 1.0); gl_Position = Camera_Projection * position; \$\endgroup\$ – Robinson Jan 26 '15 at 11:39
  • \$\begingroup\$ Actually I got that backwards because i forgot glViewport, set your viewport appropriately then apply the inverse in the matrix \$\endgroup\$ – Khlorghaal Jan 27 '15 at 0:35
  • \$\begingroup\$ Edited answer. Also you should consolidate your uniforms on cpu as much as possible, so try to only do one matrix multiply. \$\endgroup\$ – Khlorghaal Jan 27 '15 at 0:38
  • \$\begingroup\$ I still don't quite get it though. I set my viewport to cover the area of the texture I'm rendering to. \$\endgroup\$ – Robinson Jan 27 '15 at 10:56
  • \$\begingroup\$ Oh, i just realized this will only work with an ortho projection. Do the w divide manually then apply this matrix, should work then. Experiment with a 2d scene/texture with no depth and see what you can get \$\endgroup\$ – Khlorghaal Jan 29 '15 at 10:54

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.