<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-21571311396420149</id><updated>2012-02-16T09:57:08.076Z</updated><title type='text'>And what happened?</title><subtitle type='html'>Thoughts on mathematics and game development.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>15</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-4692504273841893251</id><published>2011-08-31T19:07:00.003+01:00</published><updated>2012-02-07T18:54:56.320Z</updated><title type='text'>Fast 2D and 3D Hilbert curves and Morton codes</title><content type='html'>&lt;hr/&gt;&lt;p&gt;Hilbert curves, Morton codes ad other space filling algorithms can be used to improve cache coherency, disc access and general sorting and searching.&lt;/p&gt;&lt;p&gt;Presented here are some compact, non-recursive bit twiddling C implementations for fast integer conversions between coordinates and curve indices.&lt;/p&gt;&lt;p&gt;While the Morton code to Hilbert curve conversion functions do still have loops in them, these can be unrolled if the required bit counts are known.&lt;/p&gt;&lt;p&gt;The 'bits' parameter in the Morton/Hilbert conversion functions refers to the extent of the encoded space. For instance a bits value of 1 indicates a 2x2(x2) space, 2 is a 4x4(x4) space, 3 is a 8x8(x8) space and so on.&lt;/p&gt;&lt;p&gt;Note: uint is assumed to be a 32-bit unsigned integer.&lt;/p&gt;&lt;hr/&gt;&lt;pre&gt;    uint MortonToHilbert2D( const uint morton, const uint bits )
    {
      uint index = 0;
      uint remap = 0xb4;
      uint block = ( bits &lt;&lt; 1 );
      while( block )
      {
        block -= 2;
        uint mcode = ( ( morton &gt;&gt; block ) &amp; 3 );
        uint hcode = ( ( remap &gt;&gt; ( mcode &lt;&lt; 1 ) ) &amp; 3 );
        remap ^= ( 0x82000028 &gt;&gt; ( hcode &lt;&lt; 3 ) );
        index = ( ( index &lt;&lt; 2 ) + hcode );
      }
      return( index );
    }
    
    uint HilbertToMorton2D( const uint hilbert, const uint bits )
    {
      uint index = 0;
      uint remap = 0xb4;
      uint block = ( bits &lt;&lt; 1 );
      while( block )
      {
        block -= 2;
        uint hcode = ( ( hilbert &gt;&gt; block ) &amp; 3 );
        uint mcode = ( ( remap &gt;&gt; ( hcode &lt;&lt; 1 ) ) &amp; 3 );
        remap ^= ( 0x330000cc &gt;&gt; ( hcode &lt;&lt; 3 ) );
        index = ( ( index &lt;&lt; 2 ) + mcode );
      }
      return( index );
    }
    
    uint MortonToHilbert3D( const uint morton, const uint bits )
    {
      uint index = morton;
      if( bits &gt; 1 )
      {
        uint block = ( ( bits * 3 ) - 3 );
        uint hcode = ( ( index &gt;&gt; block ) &amp; 7 );
        uint mcode, shift, signs;
        shift = signs = 0;
        while( block )
        {
          block -= 3;
          hcode &lt;&lt;= 2;
          mcode = ( ( 0x20212021 &gt;&gt; hcode ) &amp; 3 );
          shift = ( ( 0x48 &gt;&gt; ( 7 - shift - mcode ) ) &amp; 3 );
          signs = ( ( signs | ( signs &lt;&lt; 3 ) ) &gt;&gt; mcode );
          signs = ( ( signs ^ ( 0x53560300 &gt;&gt; hcode ) ) &amp; 7 );
          mcode = ( ( index &gt;&gt; block ) &amp; 7 );
          hcode = mcode;
          hcode = ( ( ( hcode | ( hcode &lt;&lt; 3 ) ) &gt;&gt; shift ) &amp; 7 );
          hcode ^= signs;
          index ^= ( ( mcode ^ hcode ) &lt;&lt; block );
        }
      }
      index ^= ( ( index &gt;&gt; 1 ) &amp; 0x92492492 );
      index ^= ( ( index &amp; 0x92492492 ) &gt;&gt; 1 );
      return( index );
    }
    
    uint HilbertToMorton3D( const uint hilbert, const uint bits )
    {
      uint index = hilbert;
      index ^= ( ( index &amp; 0x92492492 ) &gt;&gt; 1 );
      index ^= ( ( index &gt;&gt; 1 ) &amp; 0x92492492 );
      if( bits &gt; 1 )
      {
        uint block = ( ( bits * 3 ) - 3 );
        uint hcode = ( ( index &gt;&gt; block ) &amp; 7 );
        uint mcode, shift, signs;
        shift = signs = 0;
        while( block )
        {
          block -= 3;
          hcode &lt;&lt;= 2;
          mcode = ( ( 0x20212021 &gt;&gt; hcode ) &amp; 3 );
          shift = ( ( 0x48 &gt;&gt; ( 4 - shift + mcode ) ) &amp; 3 );
          signs = ( ( signs | ( signs &lt;&lt; 3 ) ) &gt;&gt; mcode );
          signs = ( ( signs ^ ( 0x53560300 &gt;&gt; hcode ) ) &amp; 7 );
          hcode = ( ( index &gt;&gt; block ) &amp; 7 );
          mcode = hcode;
          mcode ^= signs;
          mcode = ( ( ( mcode | ( mcode &lt;&lt; 3 ) ) &gt;&gt; shift ) &amp; 7 );
          index ^= ( ( hcode ^ mcode ) &lt;&lt; block );
        }
      }
      return( index );
    }

    uint Morton_2D_Encode_5bit( uint index1, uint index2 )
    { // pack 2 5-bit indices into a 10-bit Morton code
      index1 &amp;= 0x0000001f;
      index2 &amp;= 0x0000001f;
      index1 *= 0x01041041;
      index2 *= 0x01041041;
      index1 &amp;= 0x10204081;
      index2 &amp;= 0x10204081;
      index1 *= 0x00108421;
      index2 *= 0x00108421;
      index1 &amp;= 0x15500000;
      index2 &amp;= 0x15500000;
      return( ( index1 &gt;&gt; 20 ) | ( index2 &gt;&gt; 19 ) );
    }

    void Morton_2D_Decode_5bit( const uint morton, uint&amp; index1, uint&amp; index2 )
    { // unpack 2 5-bit indices from a 10-bit Morton code
      uint value1 = morton;
      uint value2 = ( value1 &gt;&gt; 1 );
      value1 &amp;= 0x00000155;
      value2 &amp;= 0x00000155;
      value1 |= ( value1 &gt;&gt; 1 );
      value2 |= ( value2 &gt;&gt; 1 );
      value1 &amp;= 0x00000133;
      value2 &amp;= 0x00000133;
      value1 |= ( value1 &gt;&gt; 2 );
      value2 |= ( value2 &gt;&gt; 2 );
      value1 &amp;= 0x0000010f;
      value2 &amp;= 0x0000010f;
      value1 |= ( value1 &gt;&gt; 4 );
      value2 |= ( value2 &gt;&gt; 4 );
      value1 &amp;= 0x0000001f;
      value2 &amp;= 0x0000001f;
      index1 = value1;
      index2 = value2;
    }

    uint Morton_2D_Encode_16bit( uint index1, uint index2 )
    { // pack 2 16-bit indices into a 32-bit Morton code
      index1 &amp;= 0x0000ffff;
      index2 &amp;= 0x0000ffff;
      index1 |= ( index1 &lt;&lt; 8 );
      index2 |= ( index2 &lt;&lt; 8 );
      index1 &amp;= 0x00ff00ff;
      index2 &amp;= 0x00ff00ff;
      index1 |= ( index1 &lt;&lt; 4 );
      index2 |= ( index2 &lt;&lt; 4 );
      index1 &amp;= 0x0f0f0f0f;
      index2 &amp;= 0x0f0f0f0f;
      index1 |= ( index1 &lt;&lt; 2 );
      index2 |= ( index2 &lt;&lt; 2 );
      index1 &amp;= 0x33333333;
      index2 &amp;= 0x33333333;
      index1 |= ( index1 &lt;&lt; 1 );
      index2 |= ( index2 &lt;&lt; 1 );
      index1 &amp;= 0x55555555;
      index2 &amp;= 0x55555555;
      return( index1 | ( index2 &lt;&lt; 1 ) );
    }
    
    void Morton_2D_Decode_16bit( const uint morton, uint&amp; index1, uint&amp; index2 )
    { // unpack 2 16-bit indices from a 32-bit Morton code
      uint value1 = morton;
      uint value2 = ( value1 &gt;&gt; 1 );
      value1 &amp;= 0x55555555;
      value2 &amp;= 0x55555555;
      value1 |= ( value1 &gt;&gt; 1 );
      value2 |= ( value2 &gt;&gt; 1 );
      value1 &amp;= 0x33333333;
      value2 &amp;= 0x33333333;
      value1 |= ( value1 &gt;&gt; 2 );
      value2 |= ( value2 &gt;&gt; 2 );
      value1 &amp;= 0x0f0f0f0f;
      value2 &amp;= 0x0f0f0f0f;
      value1 |= ( value1 &gt;&gt; 4 );
      value2 |= ( value2 &gt;&gt; 4 );
      value1 &amp;= 0x00ff00ff;
      value2 &amp;= 0x00ff00ff;
      value1 |= ( value1 &gt;&gt; 8 );
      value2 |= ( value2 &gt;&gt; 8 );
      value1 &amp;= 0x0000ffff;
      value2 &amp;= 0x0000ffff;
      index1 = value1;
      index2 = value2;
    }

    uint Morton_3D_Encode_5bit( uint index1, uint index2, uint index3 )
    { // pack 3 5-bit indices into a 15-bit Morton code
      index1 &amp;= 0x0000001f;
      index2 &amp;= 0x0000001f;
      index3 &amp;= 0x0000001f;
      index1 *= 0x01041041;
      index2 *= 0x01041041;
      index3 *= 0x01041041;
      index1 &amp;= 0x10204081;
      index2 &amp;= 0x10204081;
      index3 &amp;= 0x10204081;
      index1 *= 0x00011111;
      index2 *= 0x00011111;
      index3 *= 0x00011111;
      index1 &amp;= 0x12490000;
      index2 &amp;= 0x12490000;
      index3 &amp;= 0x12490000;
      return( ( index1 &gt;&gt; 16 ) | ( index2 &gt;&gt; 15 ) | ( index3 &gt;&gt; 14 ) );
    }

    void Morton_3D_Decode_5bit( const uint morton,
                                uint&amp; index1, uint&amp; index2, uint&amp; index3 )
    { // unpack 3 5-bit indices from a 15-bit Morton code
      uint value1 = morton;
      uint value2 = ( value1 &gt;&gt; 1 );
      uint value3 = ( value1 &gt;&gt; 2 );
      value1 &amp;= 0x00001249;
      value2 &amp;= 0x00001249;
      value3 &amp;= 0x00001249;
      value1 |= ( value1 &gt;&gt; 2 );
      value2 |= ( value2 &gt;&gt; 2 );
      value3 |= ( value3 &gt;&gt; 2 );
      value1 &amp;= 0x000010c3;
      value2 &amp;= 0x000010c3;
      value3 &amp;= 0x000010c3;
      value1 |= ( value1 &gt;&gt; 4 );
      value2 |= ( value2 &gt;&gt; 4 );
      value3 |= ( value3 &gt;&gt; 4 );
      value1 &amp;= 0x0000100f;
      value2 &amp;= 0x0000100f;
      value3 &amp;= 0x0000100f;
      value1 |= ( value1 &gt;&gt; 8 );
      value2 |= ( value2 &gt;&gt; 8 );
      value3 |= ( value3 &gt;&gt; 8 );
      value1 &amp;= 0x0000001f;
      value2 &amp;= 0x0000001f;
      value3 &amp;= 0x0000001f;
      index1 = value1;
      index2 = value2;
      index3 = value3;
    }

    uint Morton_3D_Encode_10bit( uint index1, uint index2, uint index3 )
    { // pack 3 10-bit indices into a 30-bit Morton code
      index1 &amp;= 0x000003ff;
      index2 &amp;= 0x000003ff;
      index3 &amp;= 0x000003ff;
      index1 |= ( index1 &lt;&lt; 16 );
      index2 |= ( index2 &lt;&lt; 16 );
      index3 |= ( index3 &lt;&lt; 16 );
      index1 &amp;= 0x030000ff;
      index2 &amp;= 0x030000ff;
      index3 &amp;= 0x030000ff;
      index1 |= ( index1 &lt;&lt; 8 );
      index2 |= ( index2 &lt;&lt; 8 );
      index3 |= ( index3 &lt;&lt; 8 );
      index1 &amp;= 0x0300f00f;
      index2 &amp;= 0x0300f00f;
      index3 &amp;= 0x0300f00f;
      index1 |= ( index1 &lt;&lt; 4 );
      index2 |= ( index2 &lt;&lt; 4 );
      index3 |= ( index3 &lt;&lt; 4 );
      index1 &amp;= 0x030c30c3;
      index2 &amp;= 0x030c30c3;
      index3 &amp;= 0x030c30c3;
      index1 |= ( index1 &lt;&lt; 2 );
      index2 |= ( index2 &lt;&lt; 2 );
      index3 |= ( index3 &lt;&lt; 2 );
      index1 &amp;= 0x09249249;
      index2 &amp;= 0x09249249;
      index3 &amp;= 0x09249249;
      return( index1 | ( index2 &lt;&lt; 1 ) | ( index3 &lt;&lt; 2 ) );
    }
    
    void Morton_3D_Decode_10bit( const uint morton,
                                 uint&amp; index1, uint&amp; index2, uint&amp; index3 )
    { // unpack 3 10-bit indices from a 30-bit Morton code
      uint value1 = morton;
      uint value2 = ( value1 &gt;&gt; 1 );
      uint value3 = ( value1 &gt;&gt; 2 );
      value1 &amp;= 0x09249249;
      value2 &amp;= 0x09249249;
      value3 &amp;= 0x09249249;
      value1 |= ( value1 &gt;&gt; 2 );
      value2 |= ( value2 &gt;&gt; 2 );
      value3 |= ( value3 &gt;&gt; 2 );
      value1 &amp;= 0x030c30c3;
      value2 &amp;= 0x030c30c3;
      value3 &amp;= 0x030c30c3;
      value1 |= ( value1 &gt;&gt; 4 );
      value2 |= ( value2 &gt;&gt; 4 );
      value3 |= ( value3 &gt;&gt; 4 );
      value1 &amp;= 0x0300f00f;
      value2 &amp;= 0x0300f00f;
      value3 &amp;= 0x0300f00f;
      value1 |= ( value1 &gt;&gt; 8 );
      value2 |= ( value2 &gt;&gt; 8 );
      value3 |= ( value3 &gt;&gt; 8 );
      value1 &amp;= 0x030000ff;
      value2 &amp;= 0x030000ff;
      value3 &amp;= 0x030000ff;
      value1 |= ( value1 &gt;&gt; 16 );
      value2 |= ( value2 &gt;&gt; 16 );
      value3 |= ( value3 &gt;&gt; 16 );
      value1 &amp;= 0x000003ff;
      value2 &amp;= 0x000003ff;
      value3 &amp;= 0x000003ff;
      index1 = value1;
      index2 = value2;
      index3 = value3;
    }
&lt;/pre&gt;&lt;hr/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-4692504273841893251?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/4692504273841893251/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2011/08/fast-2d-and-3d-hilbert-curves-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/4692504273841893251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/4692504273841893251'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2011/08/fast-2d-and-3d-hilbert-curves-and.html' title='Fast 2D and 3D Hilbert curves and Morton codes'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-1215820141318406494</id><published>2011-05-22T17:40:00.004+01:00</published><updated>2011-06-09T12:55:24.100+01:00</updated><title type='text'>Row major and column major matrices</title><content type='html'>&lt;hr /&gt;&lt;h4&gt;Row major and column major matrices (or proving that black is white)&lt;/h4&gt;&lt;br /&gt;
There's a good chance that you already think you understand the difference between row major and column major matrices, but are you right?&lt;br /&gt;
&lt;br /&gt;
Let's go over the standard definition of row and column major:&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;&lt;strong&gt;A matrix is row major if the elements each row are contiguous in memory and column major if the elements of each column are contiguous in memory.&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;
&lt;br /&gt;
For example, when viewed as a 1 dimensional array, the elements of a 4 by 4 row major matrix are ordered:&lt;br /&gt;
r1c1, r1c2, r1c3, r1c4, r2c1, r2c2, r2c3, r2c4, r3c1, r3c2, r3c3, r3c4, r4c1, r4c2, r4c3, r4c4&lt;br /&gt;
&lt;br /&gt;
And the elements a 4 by 4 column major matrix are ordered:&lt;br /&gt;
r1c1, r2c1, r3c1, r4c1, r1c2, r2c2, r3c2, r4c2, r1c3, r2c3, r3c3, r4c3, r1c4, r2c4, r3c4, r4c4&lt;br /&gt;
&lt;br /&gt;
In a row major matrix the step between columns is 1 element (a small or minor step) and the step between rows is [column] elements (a larger or major step).&lt;br /&gt;
&lt;br /&gt;
In a column major matrix the step between rows is 1 element (a small or minor step) and the step between columns is [row] elements (a larger or major step).&lt;br /&gt;
&lt;br /&gt;
Viewed as a 1 dimensional array, a column major matrix is the same as the result of applying a vectorization transform to the matrix.&lt;br /&gt;
&lt;br /&gt;
OK, so that's pretty straightforward, in fact you could have found all of that out from Wikipedia, so we're done right?&lt;br /&gt;
&lt;br /&gt;
Actually, no we're not, in fact the above explanation, while correct, is a major cause of confusion.&lt;br /&gt;
&lt;br /&gt;
For instance, you may be drawing the conclusion that a row major matrix is the transpose of a column matrix and you would be right, unfortunately you would also be wrong.&lt;br /&gt;
&lt;br /&gt;
So why is that? Here are some of the reasons (in no particular order):&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Row and column major only really describes array element indexing.&lt;/li&gt;
&lt;li&gt;The elements of matrices and vectors are defined by their usage.&lt;/li&gt;
&lt;li&gt;We have no definition of what differentiates a row and a column.&lt;/li&gt;
&lt;li&gt;Vectors can be rows or columns.&lt;/li&gt;
&lt;/ol&gt;To try and get a fuller understanding of row and column major, we need to take a quick detour through matrices and linear algebra:&lt;br /&gt;
&lt;br /&gt;
Matrix multiplication is defined as a series of dot products between the rows of the matrix on the left and the columns of the matrix on the right with the resulting scalar values stored in the element with the row index from the left matrix and the column index from the right matrix. For instance the dot product of left row 2 and right column 3 ends up in the element at row 2, column 3.&lt;br /&gt;
&lt;br /&gt;
Vector transformation uses the same rows on the left by columns on the right ordering as matrix multiplication.&lt;br /&gt;
&lt;br /&gt;
While implementations may swap left and right parameters, from a purely mathematical point of view, row vectors must be the left side of equations and column vectors must be the right side of equations.&lt;br /&gt;
&lt;br /&gt;
Now lets try a slightly different formulation of the row and column major definition which maps better to matrix maths:&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;i&gt;A row major matrix is an array of row vectors and a column major matrix is an array of column vectors.&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
We also need to decide whether non-matrix vectors are going to be row vectors or column vectors. We could decide to be consistent and use row vectors with row major matrices and column vectors with column major matrices, but switching them can have benefits.&lt;br /&gt;
&lt;br /&gt;
This leaves us with 4 potential scenarios, each with a slightly different set of ramifications:&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;Row vectors with row major matrices:&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;The vector is the left operand of the transform.&lt;/li&gt;
&lt;li&gt;Vector transforms are dot products between the vector and the column elements of the matrix.&lt;/li&gt;
&lt;li&gt;Matrix multiplication order is local space on the left and world space on the right.&lt;/li&gt;
&lt;li&gt;Matrix vectors are the mapping of local space scalar units to world space vectors.&lt;/li&gt;
&lt;/ul&gt;&lt;li&gt;Row vectors with column major matrices:&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;The vector is the left operand of the transform.&lt;/li&gt;
&lt;li&gt;Vector transforms are dot products between the vector and the vectors of the matrix.&lt;/li&gt;
&lt;li&gt;Matrix multiplication order is local space on the left and world space on the right.&lt;/li&gt;
&lt;li&gt;Matrix vectors are the planes of the local frame of reference.&lt;/li&gt;
&lt;/ul&gt;&lt;li&gt;Column vectors with row major matrices:&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;The vector is the right operand of the transform.&lt;/li&gt;
&lt;li&gt;Vector transforms are dot products between the vectors of the matrix and the vector.&lt;/li&gt;
&lt;li&gt;Matrix multiplication order is world space on the left and local space on the right.&lt;/li&gt;
&lt;li&gt;Matrix vectors are the planes of the local frame of reference.&lt;/li&gt;
&lt;/ul&gt;&lt;li&gt;Column vectors with column major matrices:&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;The vector is the right operand of the transform.&lt;/li&gt;
&lt;li&gt;Vector transforms are dot products between the row elements of the matrix and the vector.&lt;/li&gt;
&lt;li&gt;Matrix multiplication order is world space on the left and local space on the right.&lt;/li&gt;
&lt;li&gt;Matrix vectors are the mapping of local space scalar units to world space vectors.&lt;/li&gt;
&lt;/ul&gt;&lt;/ol&gt;The scenarios influence the memory layout of the matrices in the following combinations:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Scenarios 1 and 4&amp;nbsp;are identical.&lt;/li&gt;
&lt;li&gt;Scenarios 2 and 3 are identical.&lt;/li&gt;
&lt;li&gt;Scenarios 1 and&amp;nbsp;4&amp;nbsp;are transposed relative to scenarios 2 and 3.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
So which scenario is best? Well, I'm on slightly dangerous ground here, but I'd say row vectors with column major matrices for the following reasons:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Vector matrix transforms can be performed with vector register aligned dot products.&lt;/li&gt;
&lt;li&gt;Affine transforms (the most common type) can be represented as a matrix with only 3 column vectors, making the data compact while still aligned for vector registers.&lt;/li&gt;
&lt;li&gt;A left to right, local to world transformation order matches the left to right reading order most familiar in the western world.&lt;/li&gt;
&lt;li&gt;I view transformations in terms of planes, so having the matrix vectors be planes feels natural.&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
This is of course just my opinion and there are counter arguments which may favour other scenarios.&lt;br /&gt;
&lt;hr /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-1215820141318406494?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/1215820141318406494/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2011/05/row-major-and-column-major-matrices.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/1215820141318406494'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/1215820141318406494'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2011/05/row-major-and-column-major-matrices.html' title='&lt;h1&gt;Row major and column major matrices&lt;/h1&gt;'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-1709936683998679832</id><published>2011-04-10T14:35:00.000+01:00</published><updated>2011-04-10T14:35:27.313+01:00</updated><title type='text'>Ellipsoids</title><content type='html'>I'm trying to collide 2 ellipsoids, anybody got any idea how to do this?&lt;br /&gt;
&lt;br /&gt;
The ellipsoids are represented as 4x3 matrices and obviously the detection can be 'reduced' to an ellipsoid to unit sphere detection problem by transforming one ellipsoid by the inverse of the other, but that doesn't really help.&lt;br /&gt;
&lt;br /&gt;
Colliding ellipsoids with points, lines, rays, planes, polygons is relatively trivial, but against spheres and ellipsoids seems fairly intractable to a linear algebra approach.&lt;br /&gt;
&lt;br /&gt;
Ideally I'd like to include skewed ellipsoids.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-1709936683998679832?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/1709936683998679832/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2011/04/ellipsoids.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/1709936683998679832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/1709936683998679832'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2011/04/ellipsoids.html' title='Ellipsoids'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-3538473325898505675</id><published>2011-03-27T16:14:00.002+01:00</published><updated>2011-03-28T14:30:19.973+01:00</updated><title type='text'>A brief rant about splines</title><content type='html'>Questions that should be asked if complaints are being made about spline processing performance:&lt;br /&gt;
&lt;br /&gt;
If all your rational point weights are set to the same value, why are you using a rational spline?&lt;br /&gt;
&lt;br /&gt;
If all your knot spacings are going to be the same, why are you using a non-uniform evaluator?&lt;br /&gt;
&lt;br /&gt;
If your leading and trailing knot spacings are zero (to make the spline pass through the start and end control points) and all your other knot spacings are the same, why are you using a non-uniform evaluator? You could have just duplicated the start and end control points and saved both memory and processing.&lt;br /&gt;
&lt;br /&gt;
If you need to evaluate the tangent to the spline, why are you actually evaluating points on the spline and taking the delta? This is slow and inaccurate.&lt;br /&gt;
&lt;br /&gt;
If you need to evaluate the tangent to the spline and the position at the same t value, why are you not solving them with the same calculation? Evaluating them at the same time is little if any extra cost to just evaluating one of them.&lt;br /&gt;
&lt;br /&gt;
Why are you using a recursive evaluation? Non-recursive is considerably faster regardless of the degree of the curve.&lt;br /&gt;
&lt;br /&gt;
Why are you using an iterative evaluation if you are only ever evaluating cubic (or any other order known in advance) splines? Non-iterative is considerably faster than iterative, which itself is faster than recursive.&lt;br /&gt;
&lt;br /&gt;
Why are you doing a linear search from the start of the knot vector to find your knot spans? A binary search is a good general purpose implementation and should be considerably quicker. If you happen to know that access will be sequential, a cached sequential search 'might' also be a good idea. If you are resorting to caching that last evaluation, something is wrong elsewhere in your code.&lt;br /&gt;
&lt;br /&gt;
Are you dynamically changing your knot spacings? If not then consider pre-calculating span matrices.&lt;br /&gt;
&lt;br /&gt;
For splines only (not surfaces): Are your control points and knot vector static? If so, then consider pre-calculating span matrices AND pre-multiplying these matrices by the control points, your spline evaluation cannot get much faster once you have done this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-3538473325898505675?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/3538473325898505675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2011/03/brief-rant-about-splines.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/3538473325898505675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/3538473325898505675'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2011/03/brief-rant-about-splines.html' title='A brief rant about splines'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-5677655533012932789</id><published>2011-03-11T16:05:00.006Z</published><updated>2011-03-20T13:56:06.879Z</updated><title type='text'>Who's afraid of non-uniform rational b-splines (NURBS)?</title><content type='html'>&lt;hr/&gt;&lt;h4&gt;NURBS (non-uniform rational b-splines)&lt;/h4&gt;&lt;p&gt;I remember being told by 3 separate artists that I must be incompetent for declining to do real-time NURBS on a pre-launch PS1 project (the PS1 didn't even have hardware floating point support). Despite this, I do have a fondness for NURBS.&lt;/p&gt;&lt;p&gt;These days, fast floating-point, hardware vector support and shaders are common and while NURBS may not be my first choice of spline or surface form, they are an option for real-time graphics. I would still probably avoid real-time collision directly against a NURBS surface and would definitely avoid NURBS based CSG in a real-time environment.&lt;/p&gt;&lt;hr/&gt;&lt;p&gt;Before I start, I want to dispel a common misconception: while the definition of generalized NURBS may be recursive, the evaluation of a NURBS point does not have to be. The actual number of control points and knot values (see below) required to evaluate a point is only a fraction of the number that will generally be visited by a recursive evaluator.&lt;/p&gt;&lt;hr/&gt;&lt;p&gt;First some definitions:&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;The degree of a curve is the highest power that the interpolation factor t is raised by.&lt;/li&gt;
  &lt;li&gt;The order of a curve is the degree plus one.&lt;/li&gt;
  &lt;li&gt;The minimum number of control points for a curve is the same as the order of the curve.&lt;/li&gt;
  &lt;li&gt;The number of elements in a knot vector is equal to the number of control points plus the degree of the curve minus one.&lt;/li&gt;
  &lt;li&gt;The evaluation of a point on a curve of order O requires O control points and 2*(O-1) knot values.&lt;/li&gt;
  &lt;li&gt;While NURBS can be of any degree and any dimensionality, they are typically degree 3 (cubic) with 3D control points.&lt;/li&gt;
&lt;/ul&gt;&lt;hr/&gt;&lt;p&gt;Now let's take a look at NURBS and try to minimise the mathematical mumbo-jumbo. We'll start by breaking down the name NURBS into it's components:&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;NU (non-uniform):&lt;/li&gt;
  &lt;p&gt;In addition to an array of control points, the spline has an array of velocity control values known as a 'knot vector'. This knot vector consists of a series of increasing 'distance' values which control the speed at which each section of the spline is traversed. The gap between each pair of knot values is known as a 'span'. The knot vector can also be used to create discontinuities in the spline by having sections with zero distance between them.&lt;/p&gt;  &lt;p&gt;Given a t value of 0 to 1, the section of the spline which needs to be evaluated can be found by multiplying t by the value of the last element of the knot vector and then scanning the list of knot vector values to find the index of the lowest distance value less than or equal to the scaled t value. A binary search is recommended to find this index value.&lt;/p&gt;  &lt;li&gt;R (rational):&lt;/li&gt;
  &lt;p&gt;This means that each control point has a weight value associated with it.&lt;/p&gt;  &lt;p&gt;When evaluating the spline, each control point is multiplied by it's weight value and then both the scaled control point and the weight are interpolated by the spline. Finally, the interpolated point is divided by the interpolated weight value.&lt;/p&gt;  &lt;p&gt;The term 'rational' refers to the ratio between the weight scaled control point data and the weight. Rational control points can model shapes, such as a perfect circle, which cannot be produced with non-rational control points.&lt;/p&gt;  &lt;li&gt;BS (b-spline):&lt;/li&gt;
  &lt;p&gt;Not to be confused with a Bezier curve, this is the real meat of the spline (pseudo-code for cubic NURBS can be found below).&lt;/p&gt;&lt;/ul&gt;&lt;hr/&gt;&lt;p&gt;Alright already, what about some pseudo-code for cubic NURBS evaluation?&lt;/p&gt;&lt;pre&gt;&lt;b&gt;Given:&lt;/b&gt;
  cp[]  : an array of control points (x, y, z components and w weight)
  kv[]  : an array of knot values
  bw[4] : storage for the blend weights produced by the evaluation
  n     : the number of control points
  t     : the spline interpolation value in the range 0 : kv[n+1]
  i     : the knot vector span index such that kv[i] &amp;lt= t &amp;lt kv[i+1]

&lt;b&gt;Select active knot values:&lt;/b&gt;
 k0 = kv[ i-2 ]
 k1 = kv[ i-1 ]
 k2 = kv[ i ]
 k3 = kv[ i+1 ]
 k4 = kv[ i+2 ]
 k5 = kv[ i+3 ]

&lt;b&gt;Select active control points:&lt;/b&gt;
  c0 = cp[ i-2 ]
  c1 = cp[ i-1 ]
  c2 = cp[ i ]
  c3 = cp[ i+1 ]

&lt;b&gt;Calculate some common factors:&lt;/b&gt;
 l = ( ( k3 - t ) / ( ( k3 - k2 ) * ( k3 - k1 ) ) )
 r = ( ( t - k2 ) / ( ( k3 - k2 ) * ( k4 - k2 ) ) )
 a = ( ( r * ( t - k2 ) ) / ( k5 - k2 ) )
 b = ( ( ( r * ( k4 - t ) ) + ( l * ( t - k1 ) ) ) / ( k4 - k1 ) )
 c = ( ( l * ( k3 - t ) ) / ( k3 - k0 ) )

&lt;b&gt;Calculate the blend weights:&lt;/b&gt;
 bw[ 0 ] = ( c * ( k3 - t ) )
 bw[ 1 ] = ( ( b * ( k4 - t ) ) + ( c * ( t - k0 ) ) )
 bw[ 2 ] = ( ( a * ( k5 - t ) ) + ( b * ( t - k1 ) ) )
 bw[ 3 ] = ( a * ( t - k2 ) )

&lt;b&gt;Evaluate the blended control point weights:&lt;/b&gt;
  bw[ 0 ] *= c0.w
  bw[ 1 ] *= c1.w
  bw[ 2 ] *= c2.w
  bw[ 3 ] *= c3.w
  w = bw[ 0 ] + bw[ 1 ] + bw[ 2 ] + bw[ 3 ]

&lt;b&gt;Evaluate the final point:&lt;/b&gt;
  x = (( c0.x*bw[0] )+( c1.x*bw[1] )+( c2.x*bw[2] )+( c3.x*bw[3] ))/w
  y = (( c0.y*bw[0] )+( c1.y*bw[1] )+( c2.y*bw[2] )+( c3.y*bw[3] ))/w
  z = (( c0.z*bw[0] )+( c1.z*bw[1] )+( c2.z*bw[2] )+( c3.z*bw[3] ))/w
&lt;/pre&gt;&lt;p&gt;The matrix form of this can be found by extracting 't' from the formula, giving 4 vector4 constants (one for each power of t from 0 to 3).&lt;/p&gt;&lt;p&gt;By the way, evaluating the partial first derivative (and tangent) can share a considerable amount of the work required to evaluate the point, if a reader expresses an interest in this I will add that formulation.&lt;/p&gt;&lt;hr/&gt;&lt;p&gt;&lt;h3&gt;NURBS lend themselves to a considerable amount of pre-calculation:&lt;/h3&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Assuming that the knot vector is not being modified, each knot can be pre-evaluated to a matrix.&lt;/b&gt;&lt;/li&gt;
  &lt;p&gt;Even if the knot vector is being modified (which is unlikely in the context of game rendering), recalculating the knot matrices is low cost, though if large sections of the knot vector are frequently being modified it is probably preferable to stick to a direct rather than pre-calculated spline evaluation.&lt;/p&gt;  &lt;p&gt;In this scenario, evaluating a NURBS point will require 2 scalar multiplies, 1 scalar reciprocal, 1 vector3 scale and 5 vector4 by matrix44 multiplies.&lt;/p&gt;  &lt;p&gt;&lt;i&gt;Modifying the control points does not require recalculation of the knot evaluation matrices.&lt;/i&gt;&lt;/p&gt;&lt;li&gt;&lt;b&gt;Assuming that both the control points AND the knot vector are not being modified, the control points can be potentially be pre-multiplied by the knot evaluation matrices.&lt;/b&gt;&lt;/li&gt;
  &lt;p&gt;For this to be possible, the degree of the curve must be the same as the dimensions of the control points, for example a cubic (degree 3) NURBS with 3D control points or a quadratic (degree 2) NURBS with 2D control points.&lt;/p&gt;  &lt;p&gt;In this ideal scenario, evaluating a NURBS point will require 2 scalar multiplies, 1 scalar reciprocal, 1 vector3 scale and 1 vector4 by matrix44 multiply.&lt;/p&gt;&lt;li&gt;&lt;b&gt;While it might appear that pre-multiplying the control points by their weights is an optimization, in general it is not.&lt;/b&gt;&lt;/li&gt;
  &lt;p&gt;In the dynamic calculation shown above, it can be seen that the multiply is absorbed into the general weight evaluation. In a pre-calculated scenario, the weights are best multiplied into the knot evaluation matrix.&lt;/p&gt;&lt;/ul&gt;&lt;hr/&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-5677655533012932789?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/5677655533012932789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2011/03/whos-afraid-of-non-uniform-rational-b.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/5677655533012932789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/5677655533012932789'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2011/03/whos-afraid-of-non-uniform-rational-b.html' title='Who&apos;s afraid of non-uniform rational b-splines (NURBS)?'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-8424844111645249025</id><published>2011-02-15T15:51:00.001Z</published><updated>2011-02-15T17:38:53.995Z</updated><title type='text'>Considerations for Stereoscopic 3D</title><content type='html'>&lt;hr/&gt;&lt;p&gt;There are a lot of factors which influence successful 3D stereoscopy, here are the ones I consider most important:&lt;/p&gt;&lt;p&gt;  &lt;ul&gt;   &lt;li&gt;Physical dimensions of the screen&lt;/li&gt;
   &lt;li&gt;Physical position of the observer&lt;/li&gt;
   &lt;li&gt;Physical eye separation (interocular distance)&lt;/li&gt;
   &lt;li&gt;Physical eye rotation, required field of view, zoom and scene depth&lt;/li&gt;
   &lt;li&gt;Physical eye focus and speed of depth transitions&lt;/li&gt;
   &lt;li&gt;Scene clipping and depth&lt;/li&gt;
   &lt;li&gt;Lighting&lt;/li&gt;
  &lt;/ul&gt;&lt;/p&gt;&lt;hr/&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;Physical screen dimensions and the observer position:&lt;/h3&gt;&lt;p&gt;Together the dimensions of the screen and position of the observer give the neutral (unforced) field of view.&lt;/p&gt;&lt;p&gt;Neutral horizontal FOV = 2 * atan( screen width / ( observer distance from the screen * 2 ) )&lt;/p&gt;&lt;p&gt;Typically, for a comfortable viewing position, the FOV is about 55 degrees (or less). This means that the width of the view-port and the distance of the eye point from the view plane should be roughly the same for a neutral projection.&lt;/p&gt;&lt;p&gt;For the screen to be perceived as a window, the ratios of screen width, screen height and observer position need to be recreated in the projection.&lt;/p&gt;&lt;h3&gt;Eye separation:&lt;/h3&gt;&lt;p&gt;The human interocular distance (eye separation) is generally assumed to be about 2.5inches or 6.5cm and this should be factored into the projection setup using the same ratio that was used to map the screen to the view-port. This should result in an eye separation value which is approximately 1/6th of the width of the view-port.&lt;/p&gt;&lt;h3&gt;Eye rotation, required field of view, zoom and scene depth:&lt;/h3&gt;&lt;p&gt;The limits of human eye rotation (both convergence and divergence) have a large impact on the ability to resolve a 3D view. Most people can converge their eyes (cross them) to a significant degree, however very few can diverge them beyond the parallel. The implications of this are:&lt;/p&gt;&lt;p&gt;&lt;i&gt;Moving the view plane closer to the eye (or increasing the FOV or view-port size):&lt;/i&gt;&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;Increases the perception of depth for objects behind the view plane but makes the scene harder to resolve.&lt;/li&gt;
  &lt;li&gt;Increases the apparent speed of object depth changes which can be hard to resolve.&lt;/li&gt;
  &lt;li&gt;Exponentially increases clipping issues for objects infront of the screen.&lt;/li&gt;
  &lt;li&gt;Increases the eye divergence required to resolve objects behind the view plane.&lt;/li&gt;
  &lt;li&gt;Reduces the resolvable scene depth (too much depth and the 3D illusion will fail).&lt;/li&gt;
  &lt;li&gt;Reduces the amount of depth available and resolvable infront of the view plane.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;i&gt;Moving the view plane further from the eye (or decreasing the FOV of view-port size):&lt;/i&gt;&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;Increases the depth available to make objects 'hang' infront of the screen.&lt;/li&gt;
  &lt;li&gt;Reduces clipping issues for objects infront of the screen.&lt;/li&gt;
  &lt;li&gt;Reduces the apparent speed of object depth changes and so is easier to resolve.&lt;/li&gt;
  &lt;li&gt;Makes the scene generally easier to resolve, but reduces depth perception for objects behind the view plane.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;In general, view planes closer than the neutral depth should only be used in enclosed areas with limited depth to avoid breaking the stereoscopic illusion.&lt;/p&gt;&lt;h3&gt;Eye focus and speed of depth transitions:&lt;/h3&gt;&lt;p&gt;Eye focus has only an indirect influence on resolving stereoscopic images, but it is still an important consideration.&lt;/p&gt;&lt;p&gt;Stereoscopic 3D requires that the eyes focus remain on the surface of the screen independant of the eye rotation. HOWEVER, normally the eye focus and rotation are coordinated and separating the two requires some conscious effort. As the disparity between the eye focus distance and rotation distance increased, so does the likelyhood of eye-strain, headaches and failure to resolve the image.&lt;/p&gt;&lt;p&gt;Fast object depth transitions should be avoided as the eyes need time to adjust their rotation while maintaining their focus on the screen.&lt;/p&gt;&lt;p&gt;The amount of time that important scene objects are not on or near the view plane should be limited so that the eyes can have time to 'relax'.&lt;/p&gt;&lt;h3&gt;Scene clipping:&lt;/h3&gt;&lt;p&gt;Objects which only appear to one eye can break the 3D illusion. This is not a significant issue for objects behind the view plane, but can be a serious problem for objects infront of the screen.&lt;/p&gt;&lt;p&gt;I recommend that a a common object rejection clipping frustum be used for both the left and right scenes. The rejection frustum should have its base directly between the eyes and include the maximum far extents of both eye clip frustums. This is scene clipping frustum not to be confused with the polygon clipping frustums produced by the stereoscopic projection matrices.&lt;/p&gt;&lt;h3&gt;Lighting:&lt;/h3&gt;&lt;p&gt;The position of the left and right eyes should be used for rendering the lighting of the left and right scenes respectively. Using only a single eye position will work reasonably for objects behind the view plane, it will break (or at least strain) the 3D illusion for objects infront of the view plane.&lt;/p&gt;&lt;hr/&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;In summary:&lt;/h3&gt;&lt;p&gt;To reduce the possibility of eye-strain and improve the experience of 3D:&lt;/p&gt;&lt;ul&gt;  &lt;li&gt;The speed at which objects transition between depths should be controlled to let the eyes adjust so that the object can be continuously resolved.&lt;/li&gt;
  &lt;li&gt;The amount of time that objects are infront of the view plane should be inversely proportional to their distance infront of the view plane. This reduces eye-strain and helps preserve the integrity of the resolution of the overall scene.&lt;/li&gt;
  &lt;li&gt;Don't move objects too close to the observer.&lt;/li&gt;
  &lt;li&gt;A narrower field of view is usually (but not always) preferable to a wide field of view.&lt;/li&gt;
  &lt;li&gt;Most action should take place at or near the view plane, ideally just behind it.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Ultimately, the golden rule for successful stereoscopic 3D is: use it sparingly and make it count. Constantly having objects hanging infront of the screen reduces the WOW factor compared to the occasional targetted use of objects coming out of the screen.&lt;/p&gt;&lt;hr/&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-8424844111645249025?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/8424844111645249025/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2011/02/considerations-for-stereoscopic-3d.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/8424844111645249025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/8424844111645249025'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2011/02/considerations-for-stereoscopic-3d.html' title='Considerations for Stereoscopic 3D'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-6461949793340517451</id><published>2011-01-30T22:03:00.004Z</published><updated>2011-01-31T11:36:05.329Z</updated><title type='text'>Derivation of 'the ultimate projection calculation'</title><content type='html'>&lt;hr/&gt;&lt;p&gt;For the morbidly curious (among whom I count myself) here is the deriviation of 'the ultimate projection calculation'.&lt;/p&gt;&lt;p&gt;By the way, a prettier version (with diagrams!) of this derivation can be found on Martin Ridgers's blog &lt;a href="http://fireproofgravy.co.uk/the-icabod-projection-matrix"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;hr/&gt;&lt;h4&gt;First a reminder of our variables:&lt;/h4&gt;&lt;pre&gt;  s&lt;sub&gt;   &lt;/sub&gt;   : stereo-scopic 3D eye separation
  p&lt;sub&gt;   &lt;/sub&gt;   : perspective (0 == parallel, 1 == perspective)
  n, f&lt;sub&gt;   &lt;/sub&gt;: near and far z clip depths
  w, h&lt;sub&gt;   &lt;/sub&gt;: width and height of viewport (at depth v&lt;sub&gt;z&lt;/sub&gt;)
  v&lt;sub&gt;xyz&lt;/sub&gt;   : center of viewport
  e&lt;sub&gt;xyz&lt;/sub&gt;   : center of projection (eye position)&lt;/pre&gt;&lt;h4&gt;Let's start with the X and Y planes:&lt;/h4&gt;&lt;p&gt;We know that the X and Y planes are basically going to be the same except that the parameters will be x and y and the incoming x and y values will map to the x and y components of the X and Y planes. Given that, we only really need to derive the X or the Y plane because the other plane will just be a remapping. So, let's derive Y:&lt;/p&gt;&lt;p&gt;First we'll apply a skew so that the output y values for the points v and e are both zero. Given that we are talking about a zero value, we don't need to consider the scaling due to the perspective divide by w, after all, 0/w is still zero. The Y transformation to do this will be of the form:&lt;/p&gt;&lt;pre&gt;  v&lt;sub&gt;x&lt;/sub&gt;Y&lt;sub&gt;x&lt;/sub&gt; + v&lt;sub&gt;y&lt;/sub&gt;Y&lt;sub&gt;y&lt;/sub&gt; + v&lt;sub&gt;z&lt;/sub&gt;Y&lt;sub&gt;z&lt;/sub&gt; + Y&lt;sub&gt;w&lt;/sub&gt; = 0
  e&lt;sub&gt;x&lt;/sub&gt;Y&lt;sub&gt;x&lt;/sub&gt; + e&lt;sub&gt;y&lt;/sub&gt;Y&lt;sub&gt;y&lt;/sub&gt; + e&lt;sub&gt;z&lt;/sub&gt;Y&lt;sub&gt;z&lt;/sub&gt; + Y&lt;sub&gt;w&lt;/sub&gt; = 0&lt;/pre&gt;&lt;p&gt;This has too many variables to solve, however at this point given that we know that x has no influence on y (at least in this formulation), Y&lt;sub&gt;x&lt;/sub&gt; should be 0. We can also assume that Y&lt;sub&gt;y&lt;/sub&gt; is 1 so that we are simply passing the input y value through the equation without any scaling. This leaves us with:&lt;/p&gt;&lt;pre&gt;  v&lt;sub&gt;y&lt;/sub&gt; + v&lt;sub&gt;z&lt;/sub&gt;Y&lt;sub&gt;z&lt;/sub&gt; + Y&lt;sub&gt;w&lt;/sub&gt; = 0
  e&lt;sub&gt;y&lt;/sub&gt; + e&lt;sub&gt;z&lt;/sub&gt;Y&lt;sub&gt;z&lt;/sub&gt; + Y&lt;sub&gt;w&lt;/sub&gt; = 0&lt;/pre&gt;&lt;p&gt;This is clearly a simultaneous equation (two equations with two unknowns) and can be simply solved to:&lt;/p&gt;&lt;pre&gt;  Y&lt;sub&gt;z&lt;/sub&gt; = ( e&lt;sub&gt;y&lt;/sub&gt; - v&lt;sub&gt;y&lt;/sub&gt; ) / ( v&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;z&lt;/sub&gt; )
  Y&lt;sub&gt;w&lt;/sub&gt; = ( v&lt;sub&gt;y&lt;/sub&gt;e&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;y&lt;/sub&gt;v&lt;sub&gt;z&lt;/sub&gt; ) / ( v&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;z&lt;/sub&gt; )&lt;/pre&gt;&lt;p&gt;So, now we have derived the skew contribution, we need to scale the viewport into the -1:+1 range. We want v&lt;sub&gt;y&lt;/sub&gt; + h/2 to map to +1 (the top edge of our frustum) and v&lt;sub&gt;y&lt;/sub&gt; - h/2 to map to -1 (the bottom edge of our frustum). As we have not applied any scaling to our Y plane at this point and we know that v&lt;sub&gt;y&lt;/sub&gt; maps to 0, the top edge of our viewport currently maps to +h/2 and the bottom edge to -h/2. Simply dividing our existing Y plane by h/2 will correctly map them to the -1:+1 range. This modifies our Y plane formula to:&lt;/p&gt;&lt;pre&gt;  Y&lt;sub&gt;y&lt;/sub&gt; = 2 / h
  Y&lt;sub&gt;z&lt;/sub&gt; = 2( e&lt;sub&gt;y&lt;/sub&gt; - v&lt;sub&gt;y&lt;/sub&gt; ) / h( v&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;z&lt;/sub&gt; )
  Y&lt;sub&gt;w&lt;/sub&gt; = 2( v&lt;sub&gt;y&lt;/sub&gt;e&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;y&lt;/sub&gt;v&lt;sub&gt;z&lt;/sub&gt; ) / h( v&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;z&lt;/sub&gt; )&lt;/pre&gt;&lt;p&gt;This leaves us with the problem of the perspective divide. Our nice -1:+1 range is going to be divided by the w value resulting from the W plane. Given that we can decide what the w value will be at the viewport depth, let's just remember to make it 1 when we come to our W plane formula and leave our Y and X plane formulas unchanged.&lt;/p&gt;&lt;p&gt;By the way, it should be clear from this that simply adding and subtracting half the stereo 3D eye seperation 's' from e&lt;sub&gt;x&lt;/sub&gt; value in the X plane formula will give us left and right eye versions of our X plane as follows:&lt;/p&gt;&lt;pre&gt;  X&lt;sub&gt;x&lt;/sub&gt; = 2 / w
  X&lt;sub&gt;z&lt;/sub&gt; = ( 2( e&lt;sub&gt;x&lt;/sub&gt; - v&lt;sub&gt;x&lt;/sub&gt; ) + ( ±s ) ) / w( v&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;z&lt;/sub&gt; )
  X&lt;sub&gt;w&lt;/sub&gt; = ( 2( v&lt;sub&gt;x&lt;/sub&gt;e&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;x&lt;/sub&gt;v&lt;sub&gt;z&lt;/sub&gt; ) - ( ±s )v&lt;sub&gt;z&lt;/sub&gt; ) / w( v&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;z&lt;/sub&gt; )&lt;/pre&gt;&lt;h4&gt;Let's move on to the W plane:&lt;/h4&gt;&lt;p&gt;The W plane controls perspective, so we are going to need to take account of our perspective control parameter 'p'. We are also going to have to ensure that the w value on the viewport plane is 1 as required by our formula for the X and Y planes.&lt;/p&gt;&lt;p&gt;A parallel projection W plane is going to look like:&lt;/p&gt;&lt;pre&gt;  W&lt;sub&gt;x&lt;/sub&gt; = 0
  W&lt;sub&gt;y&lt;/sub&gt; = 0
  W&lt;sub&gt;z&lt;/sub&gt; = 0
  W&lt;sub&gt;w&lt;/sub&gt; = 1&lt;/pre&gt;&lt;p&gt;This will have no perspective and the w value at the viewport depth will be 1 (let's face it, the w value will be 1 at any depth.&lt;/p&gt;&lt;p&gt;A perspective W plane needs to pass through the eye point at depth e&lt;sub&gt;z&lt;/sub&gt; so we can start with a normalized plane of the form:&lt;/p&gt;&lt;pre&gt;  W&lt;sub&gt;x&lt;/sub&gt; = 0
  W&lt;sub&gt;y&lt;/sub&gt; = 0
  W&lt;sub&gt;z&lt;/sub&gt; = 1
  W&lt;sub&gt;w&lt;/sub&gt; = -e&lt;sub&gt;z&lt;/sub&gt;&lt;/pre&gt;&lt;p&gt;This will offset z relative to the eye depth, but the w value at the viewport depth will be v&lt;sub&gt;z&lt;/sub&gt; * 1 - e&lt;sub&gt;z&lt;/sub&gt;, not the 1 that we require. This can be remedied by simply dividing the perspective W plane by ( v&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;z&lt;/sub&gt; ) as follows:&lt;/p&gt;&lt;pre&gt;  W&lt;sub&gt;x&lt;/sub&gt;= 0
  W&lt;sub&gt;y&lt;/sub&gt;= 0
  W&lt;sub&gt;z&lt;/sub&gt;= 1 / ( v&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;z&lt;/sub&gt; )
  W&lt;sub&gt;w&lt;/sub&gt;= -e&lt;sub&gt;z&lt;/sub&gt; / ( v&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;z&lt;/sub&gt; )&lt;/pre&gt;&lt;p&gt;Now we have a perspective projection W plane with a w value of 1 at the viewport depth and a parallel projection W plane also with a w value of 1 at the viewport depth. Our perspective control parameter 'p' can simply interpolate between these 2 planes, giving us a W plane formula of:&lt;/p&gt;&lt;pre&gt;  W&lt;sub&gt;x&lt;/sub&gt; = 0
  W&lt;sub&gt;y&lt;/sub&gt; = 0
  W&lt;sub&gt;z&lt;/sub&gt; = p / ( v&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;z&lt;/sub&gt; )
  W&lt;sub&gt;w&lt;/sub&gt; = ( v&lt;sub&gt;z&lt;/sub&gt;(1-p) - e&lt;sub&gt;z&lt;/sub&gt; ) / ( v&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;z&lt;/sub&gt; )&lt;/pre&gt;&lt;h4&gt;Now we're only left with the Z plane:&lt;/h4&gt;&lt;p&gt;We know that x and y will have no contribution to our Z plane and so we are only left to solve Z&lt;sub&gt;z&lt;/sub&gt; and Z&lt;sub&gt;w&lt;/sub&gt;. This is going to be slightly more complicated than the solutions for the other planes as we need to map near (n) and far (f) z values to -1 and +1 respectively after perspective divides by different w values, rather than by 1 as we were able to use with the X and Y planes. This means that we have to solve:&lt;/p&gt;&lt;pre&gt;  ( fZ&lt;sub&gt;z&lt;/sub&gt; + Z&lt;sub&gt;w&lt;/sub&gt; ) / ( fW&lt;sub&gt;z&lt;/sub&gt; + W&lt;sub&gt;w&lt;/sub&gt; ) = +1
  ( nZ&lt;sub&gt;z&lt;/sub&gt; + Z&lt;sub&gt;w&lt;/sub&gt; ) / ( nW&lt;sub&gt;z&lt;/sub&gt; + W&lt;sub&gt;w&lt;/sub&gt; ) = -1&lt;/pre&gt;&lt;p&gt;Thankfully this is just another simultaneous equation and can be trivially solved to:&lt;/p&gt;&lt;pre&gt;  Z&lt;sub&gt;z&lt;/sub&gt; = ( 2W&lt;sub&gt;w&lt;/sub&gt; + W&lt;sub&gt;z&lt;/sub&gt;( f + n ) ) / ( f - n )
  Z&lt;sub&gt;w&lt;/sub&gt; = -( W&lt;sub&gt;w&lt;/sub&gt;( f + n ) + 2fnW&lt;sub&gt;z&lt;/sub&gt; ) / ( f - n )&lt;/pre&gt;&lt;p&gt;Which then expands (by substituting the formulas for W&lt;sub&gt;z&lt;/sub&gt; and W&lt;sub&gt;w&lt;/sub&gt; and rearranging) to a Z plane equation of:&lt;/p&gt;&lt;pre&gt;  Z&lt;sub&gt;x&lt;/sub&gt; = 0
  Z&lt;sub&gt;y&lt;/sub&gt; = 0
  Z&lt;sub&gt;z&lt;/sub&gt; = ( 2( v&lt;sub&gt;z&lt;/sub&gt;(1-p) - e&lt;sub&gt;z&lt;/sub&gt; ) + p( f + n ) ) / ( ( f - n )( v&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;z&lt;/sub&gt; ) )
  Z&lt;sub&gt;w&lt;/sub&gt; = -( ( v&lt;sub&gt;z&lt;/sub&gt;(1-p) - e&lt;sub&gt;z&lt;/sub&gt; )( f + n ) + 2fnp ) / ( ( f - n )( v&lt;sub&gt;z&lt;/sub&gt; - e&lt;sub&gt;z&lt;/sub&gt; ) )&lt;/pre&gt;&lt;h4&gt;Anything else?&lt;/h4&gt;&lt;p&gt;Not a lot really except that if you want the w value at the viewport depth to be other than 1, multiply the entire matrix by the new value.&lt;/p&gt;&lt;p&gt;If you want to make W&lt;sub&gt;z&lt;/sub&gt; == 1 (as required by some SDKs and hardware), divide the entire matrix by W&lt;sub&gt;z&lt;/sub&gt;, though this will not work for parallel projection matrices and will have the side effect of modifying the w value at the viewport depth.&lt;/p&gt;&lt;hr/&gt;&lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-6461949793340517451?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/6461949793340517451/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2011/01/derivation-of-ultimate-projection.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/6461949793340517451'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/6461949793340517451'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2011/01/derivation-of-ultimate-projection.html' title='Derivation of &apos;the ultimate projection calculation&apos;'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-8432857418861045469</id><published>2011-01-29T15:08:00.000Z</published><updated>2011-01-29T15:08:53.482Z</updated><title type='text'>Frustum plane extraction</title><content type='html'>&lt;hr/&gt;  &lt;p&gt;The following extracts the frustum face planes from a unit (ogl) matrix.&lt;/p&gt;  &lt;hr/&gt;  &lt;h4&gt;Left face plane:&lt;/h4&gt;  &lt;pre&gt;    L&lt;sub&gt;x&lt;/sub&gt; = W&lt;sub&gt;x&lt;/sub&gt; + X&lt;sub&gt;x&lt;/sub&gt;
    L&lt;sub&gt;y&lt;/sub&gt; = W&lt;sub&gt;y&lt;/sub&gt; + X&lt;sub&gt;y&lt;/sub&gt;
    L&lt;sub&gt;z&lt;/sub&gt; = W&lt;sub&gt;z&lt;/sub&gt; + X&lt;sub&gt;z&lt;/sub&gt;
    L&lt;sub&gt;w&lt;/sub&gt; = W&lt;sub&gt;w&lt;/sub&gt; + X&lt;sub&gt;w&lt;/sub&gt;&lt;/pre&gt;  &lt;h4&gt;Right face plane:&lt;/h4&gt;  &lt;pre&gt;    R&lt;sub&gt;x&lt;/sub&gt; = W&lt;sub&gt;x&lt;/sub&gt; - X&lt;sub&gt;x&lt;/sub&gt;
    R&lt;sub&gt;y&lt;/sub&gt; = W&lt;sub&gt;y&lt;/sub&gt; - X&lt;sub&gt;y&lt;/sub&gt;
    R&lt;sub&gt;z&lt;/sub&gt; = W&lt;sub&gt;z&lt;/sub&gt; - X&lt;sub&gt;z&lt;/sub&gt;
    R&lt;sub&gt;w&lt;/sub&gt; = W&lt;sub&gt;w&lt;/sub&gt; - X&lt;sub&gt;w&lt;/sub&gt;&lt;/pre&gt;  &lt;h4&gt;Bottom face plane:&lt;/h4&gt;  &lt;pre&gt;    B&lt;sub&gt;x&lt;/sub&gt; = W&lt;sub&gt;x&lt;/sub&gt; + Y&lt;sub&gt;x&lt;/sub&gt;
    B&lt;sub&gt;y&lt;/sub&gt; = W&lt;sub&gt;y&lt;/sub&gt; + Y&lt;sub&gt;y&lt;/sub&gt;
    B&lt;sub&gt;z&lt;/sub&gt; = W&lt;sub&gt;z&lt;/sub&gt; + Y&lt;sub&gt;z&lt;/sub&gt;
    B&lt;sub&gt;w&lt;/sub&gt; = W&lt;sub&gt;w&lt;/sub&gt; + Y&lt;sub&gt;w&lt;/sub&gt;&lt;/pre&gt;  &lt;h4&gt;Top face plane:&lt;/h4&gt;  &lt;pre&gt;    T&lt;sub&gt;x&lt;/sub&gt; = W&lt;sub&gt;x&lt;/sub&gt; - Y&lt;sub&gt;x&lt;/sub&gt;
    T&lt;sub&gt;y&lt;/sub&gt; = W&lt;sub&gt;y&lt;/sub&gt; - Y&lt;sub&gt;y&lt;/sub&gt;
    T&lt;sub&gt;z&lt;/sub&gt; = W&lt;sub&gt;z&lt;/sub&gt; - Y&lt;sub&gt;z&lt;/sub&gt;
    T&lt;sub&gt;w&lt;/sub&gt; = W&lt;sub&gt;w&lt;/sub&gt; - Y&lt;sub&gt;w&lt;/sub&gt;&lt;/pre&gt;  &lt;h4&gt;Near face plane:&lt;/h4&gt;  &lt;pre&gt;    N&lt;sub&gt;x&lt;/sub&gt; = W&lt;sub&gt;x&lt;/sub&gt; + Z&lt;sub&gt;x&lt;/sub&gt;
    N&lt;sub&gt;y&lt;/sub&gt; = W&lt;sub&gt;y&lt;/sub&gt; + Z&lt;sub&gt;y&lt;/sub&gt;
    N&lt;sub&gt;z&lt;/sub&gt; = W&lt;sub&gt;z&lt;/sub&gt; + Z&lt;sub&gt;z&lt;/sub&gt;
    N&lt;sub&gt;w&lt;/sub&gt; = W&lt;sub&gt;w&lt;/sub&gt; + Z&lt;sub&gt;w&lt;/sub&gt;&lt;/pre&gt;  &lt;h4&gt;Far face plane:&lt;/h4&gt;  &lt;pre&gt;    F&lt;sub&gt;x&lt;/sub&gt; = W&lt;sub&gt;x&lt;/sub&gt; - Z&lt;sub&gt;x&lt;/sub&gt;
    F&lt;sub&gt;y&lt;/sub&gt; = W&lt;sub&gt;y&lt;/sub&gt; - Z&lt;sub&gt;y&lt;/sub&gt;
    F&lt;sub&gt;z&lt;/sub&gt; = W&lt;sub&gt;z&lt;/sub&gt; - Z&lt;sub&gt;z&lt;/sub&gt;
    F&lt;sub&gt;w&lt;/sub&gt; = W&lt;sub&gt;w&lt;/sub&gt; - Z&lt;sub&gt;w&lt;/sub&gt;&lt;/pre&gt;  &lt;hr/&gt;  &lt;p&gt;This also works for DX matrices except for the near plane which should be as shown below.&lt;/p&gt;  &lt;hr/&gt;  &lt;h4&gt;Near face plane (DX):&lt;/h4&gt;  &lt;pre&gt;    N&lt;sub&gt;x&lt;/sub&gt; = Z&lt;sub&gt;x&lt;/sub&gt;
    N&lt;sub&gt;y&lt;/sub&gt; = Z&lt;sub&gt;y&lt;/sub&gt;
    N&lt;sub&gt;z&lt;/sub&gt; = Z&lt;sub&gt;z&lt;/sub&gt;
    N&lt;sub&gt;w&lt;/sub&gt; = Z&lt;sub&gt;w&lt;/sub&gt;&lt;/pre&gt;  &lt;hr/&gt;  &lt;h4&gt;Notes:&lt;/h4&gt;  &lt;p&gt;Obviously this formula can be used to extract the frustum faces from any sub-frustum by first applying a frustum remap to the matrix.&lt;/p&gt;  &lt;p&gt;Extracting the vertices of the frustum is usually best done by inverting the frustum matrix and summing the planes to simulate transforming a &lt;span&gt;unit (-1:+1) cube.&lt;/span&gt;&lt;/p&gt;  &lt;p&gt;The frustum vertices can also be extracted by intersection of the frustum planes and a optimized vertex extractor of this form can have comparable performance to the inversion and summing method.&lt;/p&gt;  &lt;hr/&gt;  &lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-8432857418861045469?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/8432857418861045469/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2011/01/frustum-plane-extraction.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/8432857418861045469'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/8432857418861045469'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2011/01/frustum-plane-extraction.html' title='Frustum plane extraction'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-109006471983648674</id><published>2011-01-24T14:14:00.004Z</published><updated>2011-01-29T13:39:56.979Z</updated><title type='text'>My favourite spline</title><content type='html'>&lt;hr/&gt;  &lt;p&gt;For a quick diversion, I thought I'd mention my favourite spline which is a variant on a non-uniform Catmull-Rom spline.&lt;/p&gt;  &lt;hr/&gt;  &lt;p&gt;A Catmull-Rom spline is commonly implemented as a uniform cubic Hermite curve with tangents:&lt;/p&gt;  &lt;pre&gt;    U=(P&lt;sub&gt;i+1&lt;/sub&gt;-P&lt;sub&gt;i-1&lt;/sub&gt;)/6 and V=(P&lt;sub&gt;i+2&lt;/sub&gt;-P&lt;sub&gt;i&lt;/sub&gt;)/6&lt;/pre&gt;  &lt;p&gt;Given a knot vector K with knot spacing equal to the distance between the control points, replacing the tangents with:&lt;/p&gt;  &lt;pre&gt;    U=(P&lt;sub&gt;i+1&lt;/sub&gt;-P&lt;sub&gt;i-1&lt;/sub&gt;)(K&lt;sub&gt;i+1&lt;/sub&gt;-K&lt;sub&gt;i&lt;/sub&gt;)/3(K&lt;sub&gt;i+1&lt;/sub&gt;-K&lt;sub&gt;i-1&lt;/sub&gt;)
    V=(P&lt;sub&gt;i+2&lt;/sub&gt;-P&lt;sub&gt;i&lt;/sub&gt;)(K&lt;sub&gt;i+1&lt;/sub&gt;-K&lt;sub&gt;i&lt;/sub&gt;)/3(K&lt;sub&gt;i+2&lt;/sub&gt;-K&lt;sub&gt;i&lt;/sub&gt;)&lt;/pre&gt;  &lt;p&gt;Produces a very nice non-uniform interpolating spline with very little acceleration because the tangents take account of the accelleration due to the interpolation of the knot vector.&lt;/p&gt;  &lt;hr/&gt;  &lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-109006471983648674?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/109006471983648674/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2011/01/my-favourite-spline.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/109006471983648674'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/109006471983648674'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2011/01/my-favourite-spline.html' title='My favourite spline'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-1288029915435372688</id><published>2011-01-22T17:09:00.003Z</published><updated>2011-01-29T13:35:08.803Z</updated><title type='text'>Frustum remapping</title><content type='html'>&lt;hr/&gt;  &lt;p&gt;So, we have a unit frustum but we want to remap it because:&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;We want to remap it to a non-unit frustum (like DirectX).&lt;/li&gt;
    &lt;li&gt;We want to use a sub-section of it for tiled rendering.&lt;/li&gt;
    &lt;li&gt;We want to invert depth for better floating point depth buffer accuracy.&lt;/li&gt;
    &lt;li&gt;We want the rendering to be upside down.&lt;/li&gt;
    &lt;li&gt;&lt;b&gt;...&lt;/b&gt;&lt;/li&gt;
  &lt;/ul&gt;  &lt;p&gt;Remapping the frustum is simply a matter of combining various ratios of the W plane and the X, Y and Z planes to shift the center point and rescale the frustum.&lt;/p&gt;  &lt;hr/&gt;  &lt;p&gt;Given 2 aabb's s and t representing the required remapping from source to target, the formula is as follows:&lt;/p&gt;  &lt;b&gt;Calculate the ratios:&lt;/b&gt;&lt;br /&gt;
  &lt;pre&gt;    sx = 1.0 / ( s.max.x - s.min.x )
    sy = 1.0 / ( s.max.y - s.min.y )
    sz = 1.0 / ( s.max.z - s.min.z )
    xx = ( t.max.x - t.min.x ) * sx
    yy = ( t.max.y - t.min.y ) * sy
    zz = ( t.max.z - t.min.z ) * sz
    xw = ( s.max.x * t.min.x - t.max.x * s.min.x ) * sx
    yw = ( s.max.y * t.min.y - t.max.y * s.min.y ) * sy
    zw = ( s.max.z * t.min.z - t.max.z * s.min.z ) * sz&lt;/pre&gt;  &lt;b&gt;Apply the remapping:&lt;/b&gt;&lt;br /&gt;
  &lt;pre&gt;    X&lt;sub&gt;x&lt;/sub&gt; = X&lt;sub&gt;x&lt;/sub&gt; * xx + W&lt;sub&gt;x&lt;/sub&gt; * xw
    X&lt;sub&gt;y&lt;/sub&gt; = X&lt;sub&gt;y&lt;/sub&gt; * xx + W&lt;sub&gt;y&lt;/sub&gt; * xw
    X&lt;sub&gt;z&lt;/sub&gt; = X&lt;sub&gt;z&lt;/sub&gt; * xx + W&lt;sub&gt;z&lt;/sub&gt; * xw
    X&lt;sub&gt;w&lt;/sub&gt; = X&lt;sub&gt;w&lt;/sub&gt; * xx + W&lt;sub&gt;w&lt;/sub&gt; * xw
    Y&lt;sub&gt;x&lt;/sub&gt; = Y&lt;sub&gt;x&lt;/sub&gt; * yy + W&lt;sub&gt;x&lt;/sub&gt; * yw
    Y&lt;sub&gt;y&lt;/sub&gt; = Y&lt;sub&gt;y&lt;/sub&gt; * yy + W&lt;sub&gt;y&lt;/sub&gt; * yw
    Y&lt;sub&gt;z&lt;/sub&gt; = Y&lt;sub&gt;z&lt;/sub&gt; * yy + W&lt;sub&gt;z&lt;/sub&gt; * yw
    Y&lt;sub&gt;w&lt;/sub&gt; = Y&lt;sub&gt;w&lt;/sub&gt; * yy + W&lt;sub&gt;w&lt;/sub&gt; * yw
    Z&lt;sub&gt;x&lt;/sub&gt; = Z&lt;sub&gt;x&lt;/sub&gt; * zz + W&lt;sub&gt;x&lt;/sub&gt; * zw
    Z&lt;sub&gt;y&lt;/sub&gt; = Z&lt;sub&gt;y&lt;/sub&gt; * zz + W&lt;sub&gt;y&lt;/sub&gt; * zw
    Z&lt;sub&gt;z&lt;/sub&gt; = Z&lt;sub&gt;z&lt;/sub&gt; * zz + W&lt;sub&gt;z&lt;/sub&gt; * zw
    Z&lt;sub&gt;w&lt;/sub&gt; = Z&lt;sub&gt;w&lt;/sub&gt; * zz + W&lt;sub&gt;w&lt;/sub&gt; * zw&lt;/pre&gt;  &lt;hr/&gt;  &lt;p&gt;&lt;b&gt;&lt;i&gt;How about some examples?&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;To map a unit (ogl) frustum to a DX frustum:&lt;/li&gt;
    &lt;pre&gt;      s.min = { -1, -1, -1 }
      s.max = {  1,  1,  1 }
      t.min = { -1, -1,  0 }
      t.max = {  1,  1,  1 }&lt;/pre&gt;    &lt;li&gt;To map a DX frustum to a unit (ogl) frustum:&lt;/li&gt;
    &lt;pre&gt;      s.min = { -1, -1,  0 }
      s.max = {  1,  1,  1 }
      t.min = { -1, -1, -1 }
      t.max = {  1,  1,  1 }&lt;/pre&gt;    &lt;li&gt;To mirror a unit frustum (inverts polygon winding order):&lt;/li&gt;
    &lt;pre&gt;      s.min = {  1, -1, -1 }
      s.max = { -1,  1,  1 }
      t.min = { -1, -1, -1 }
      t.max = {  1,  1,  1 }&lt;/pre&gt;    &lt;li&gt;To make a unit frustum upside down (inverts polygon winding order):&lt;/li&gt;
    &lt;pre&gt;      s.min = { -1,  1, -1 }
      s.max = {  1, -1,  1 }
      t.min = { -1, -1, -1 }
      t.max = {  1,  1,  1 }&lt;/pre&gt;    &lt;li&gt;To invert the depth (but not the perspective or handedness) a unit frustum:&lt;/li&gt;
    &lt;pre&gt;      s.min = { -1, -1,  1 }
      s.max = {  1,  1, -1 }
      t.min = { -1, -1, -1 }
      t.max = {  1,  1,  1 }&lt;/pre&gt;    &lt;li&gt;To map the far, top, right octant of a unit frustum to a DX frustum:&lt;/li&gt;
    &lt;pre&gt;      s.min = {  0,  0,  0 }
      s.max = {  1,  1,  1 }
      t.min = { -1, -1,  0 }
      t.max = {  1,  1,  1 }&lt;/pre&gt;    &lt;li&gt;To map the near, bottom, left octant of a DX frustum to a unit frustum:&lt;/li&gt;
    &lt;pre&gt;      s.min = { -1, -1,  0   }
      s.max = {  0,  0,  0.5 }
      t.min = { -1, -1, -1   }
      t.max = {  1,  1,  1   }&lt;/pre&gt;  &lt;/ul&gt;  &lt;hr/&gt;  &lt;b&gt;&lt;i&gt;Fair enough, but where do those ratios come from?&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;
  &lt;p&gt;The easiest way of understanding this is with a unit frustum. There are 2 parts to the remapping, translating the frustum origin and scaling the frustum. I'll only cover the x axis, but the same logic applies to the y and z axes.&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;&lt;b&gt;Translation:&lt;/b&gt;&lt;/li&gt;
    &lt;p&gt;After the perspective divide by w, the left edge of a unit frustum maps to x = -1 and the right to x = +1. This means that before the perspective divide, the left edge x value was -w and the right was +w. From this, you should be able to see that the W plane basically defines a unit of distance from the center of a unit frustum.&lt;/p&gt;    &lt;p&gt;So, to move the center line of the frustum to the left edge of the frustum, simply subtract the W plane from the X plane, everything which previously had a x value of 0 after the perspective devide will now have a value of -1.&lt;/p&gt;    &lt;p&gt;That should be enough to get you started with moving the origin of the frustum.&lt;/p&gt;    &lt;li&gt;&lt;b&gt;Scaling:&lt;/b&gt;&lt;/li&gt;
    &lt;p&gt;Scaling the frustum is even simpler. To perform a 2x horizontal zoom, we simply need to double the X plane, this will double all the output x values, mapping values of 0.5 to 1.0.&lt;/p&gt;  &lt;/ul&gt;  &lt;hr/&gt;  &lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-1288029915435372688?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/1288029915435372688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2011/01/frustum-remapping.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/1288029915435372688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/1288029915435372688'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2011/01/frustum-remapping.html' title='Frustum remapping'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-409141544421448512</id><published>2011-01-22T15:31:00.003Z</published><updated>2011-12-16T10:13:39.067Z</updated><title type='text'>So why change my projection functions?</title><content type='html'>&lt;hr /&gt;&lt;h4&gt;&lt;i&gt;This article refers to the projection matrix formulation in &lt;a href="http://and-what-happened.blogspot.com/2010/11/ultimate-projection-calculation-almost.html"&gt;this post&lt;/a&gt;.&lt;/i&gt;&lt;/h4&gt;&lt;hr /&gt;&lt;br&gt;&lt;br /&gt;
&lt;hr/&gt;&lt;h4&gt;&lt;i&gt;Why is this any better than what I currently use?&lt;/i&gt;&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;It is easy to visualize and manipulate.&lt;/li&gt;
&lt;li&gt;It has a lot more functionality.&lt;/li&gt;
&lt;li&gt;It can be interpolated.&lt;/li&gt;
&lt;li&gt;It supports stereoscopic 3D.&lt;/li&gt;
&lt;li&gt;It simplifies shadow mapping adjustment.&lt;/li&gt;
&lt;li&gt;It correctly supports the skews required for oblique projections.&lt;/li&gt;
&lt;li&gt;It simplifies the use of offset projections (which are generally under-used because they are not understood).&lt;/li&gt;
&lt;li&gt;It can reduce the number of code paths because the same calculation can be used for multiple matrix forms.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;...&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;h4&gt;&lt;i&gt;OK, any specifics?&lt;/i&gt;&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;h4&gt;Camera pull:&lt;/h4&gt;&lt;/li&gt;
This increases or decreases the volume of the frustum while leaving the field of view unchanged. For instance, in a Tennis game, you may want to have the camera inside the stadium and in front of the crowd, but find that the field of view has to be set so large that you get a fish eye effect. Camera pull allows the field of view to be narrower by effectively 'pulling' the virtual camera back.
This can be achieved by simultaneously moving the viewport and projection center z values.
Without this formulation, the common method of producing camera pull is to move the virtual camera backwards along it's z axis while simultaneously moving the near and far clip values forwards.
Camera pull can also be used to modify the spread of depth buffer values to reduce z-fighting.
&lt;li&gt;&lt;h4&gt;Dolly zoom (aka vertigo zoom or tracking zoom):&lt;/h4&gt;&lt;/li&gt;
This involves moving the camera back or forward while simultaneously adjusting the field of view so that the subject remains the same size.
This can be achieved by setting the viewport depth to be the same as the subject and moving the projection center z value (ensuring that it does not cross the near clip depth).
Without this formulation, the dolly zoom effect is complex, requiring camera movement and constant recalculation of an appropriate field of view.
&lt;li&gt;&lt;h4&gt;Planar zooms:&lt;/h4&gt;&lt;/li&gt;
This involves zooming to any sub-section of the view without rotation. It is intrinsically an offset projection. Planar zooming is similar to the effect used in Blade-Runner to zoom into a photo, it can also be used for tiled rendering.
This can be achieved by moving the viewplane and viewport to the point of interest.
This can also be achieved by sub-frustum extraction which will be explained in a future post about frustum remapping.
&lt;li&gt;&lt;h4&gt;Simple zoom multiples:&lt;/h4&gt;&lt;/li&gt;
To create a 2x zoom, simply divide the viewport with and height by 2, for a 3x zoom divide by 3 etc..
It might seem that this is nothing special, but with field of view based projections, simply halving the field of view does not produce a 2x zoom. For instance a 2x zoom on a 90 degree field of view equates to approximately a 53 degree field of view, not 45 degrees.
&lt;li&gt;&lt;h4&gt;Blending between parallel and perspective projections:&lt;/h4&gt;&lt;/li&gt;
Because the formulation allows control of the viewplane and viewport, blending between parallel and perspective projections with or without skew is possible by simply blending the input parameters.
Standard projection calculations cannot directly achieve this because the viewplane and viewport cannot be specified.
&lt;li&gt;&lt;h4&gt;Oblique projections:&lt;/h4&gt;&lt;/li&gt;
These can be achieved by setting the perspective control parameter p to zero and applying x and/or y offsets to the viewport or center of projection.
&lt;li&gt;&lt;h4&gt;Controlling stereoscopic focus:&lt;/h4&gt;&lt;/li&gt;
The formulation allows control of the viewplane (stereoscopic convergence plane) and eye seperation giving complete control over the stereoscopic effect.
When used with stereoscopic 3D controlled by a device driver, the eye seperation should not be used, however as objects on the viewplane will always have a w value of 1.0, the application will still have greater control over the stereoscopic effect than most standard implementations.
&lt;li&gt;&lt;h4&gt;Driving user attention:&lt;/h4&gt;&lt;/li&gt;
Offset projections are great for driving the attention of users. Psychologically, humans always have a conscious or unconscious desire to look towards the vanishing point of an image. By moving the viewport to the right, the viewer will want to move to the left and so on. Viewers can be driven to any point of interest using offset projections.
For instance, in the game 'God of War' there are sections where the character is climbing a cliff face with the camera facing the cliff. As the character moves left and right on the cliff, the camera pans to keep him in view. Assuming that there are sufficient protruberences from the cliff to give the player a sense of the vanishing point, panning the viewport instead of the camera would not only keep the character on screen, it would drive the player to want to climb up the center of the cliff.&lt;/ul&gt;&lt;hr /&gt;&lt;h4&gt;&lt;i&gt;But what about FOV and the other controls I'm used to?&lt;/i&gt;&lt;/h4&gt;These can all be implemented as helper functions to set the parameters this form requires. A modifier queue should already form part of most good camera systems and all your existing controls should be easy to adapt to this form.&lt;br /&gt;
&lt;hr /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-409141544421448512?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://and-what-happened.blogspot.com/2010/11/ultimate-projection-calculation-almost.html' title='So why change my projection functions?'/><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/409141544421448512/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2011/01/so-why-change-my-projection-functions.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/409141544421448512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/409141544421448512'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2011/01/so-why-change-my-projection-functions.html' title='So why change my projection functions?'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-8878058554454206748</id><published>2011-01-22T13:18:00.002Z</published><updated>2011-01-28T11:56:44.543Z</updated><title type='text'>Why only 'almost'?</title><content type='html'>&lt;hr/&gt;  &lt;p&gt;OK, so why is the formulation given in my previous post only 'almost' the ultimate projection matrix? Well, it does not account for the following:&lt;/p&gt;  &lt;ol&gt;    &lt;li&gt;Non-unit frustums (i.e. x, y, and z extents other than -1:+1).&lt;/li&gt;
    &lt;li&gt;Handedness (the projection is left handed).&lt;/li&gt;
    &lt;li&gt;Depth biasing.&lt;/li&gt;
    &lt;li&gt;Infinite projections.&lt;/li&gt;
    &lt;li&gt;Skewed near and far clipping planes.&lt;/li&gt;
  &lt;/ol&gt;  &lt;hr/&gt;  &lt;p&gt;&lt;b&gt;&lt;i&gt;But why not?&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;  &lt;ol&gt;    &lt;li&gt;&lt;b&gt;Non-unit frustums (i.e. x, y, and z extents other than -1:+1):&lt;/b&gt;&lt;/li&gt;
    &lt;ul&gt;      &lt;li&gt;A unit frustum is the most neutral formulation.&lt;/li&gt;
      &lt;li&gt;Even if a unit frustum is not the final desired projection, it is a useful intermediate matrix for use with collision detection.&lt;/li&gt;
      &lt;li&gt;The remapping from a unit frustum to another set of bounds (such as the DirectX form of x[-1:+1, y[-1:+1], z[0:+1]) is a trivial modification which can be applied after this formulation. Having a seperate remap function is useful as part of a seperate pre-viewport transform object and can be used to create sub-frustums or invert bounds directions. An explanation of how the remapping is performed will be given in an upcoming post.&lt;/li&gt;
    &lt;/ul&gt;    &lt;br/&gt;
    &lt;li&gt;&lt;b&gt;Handedness (the projection is left handed):&lt;/b&gt;&lt;/li&gt;
    &lt;ul&gt;      &lt;li&gt;Does not generally contribute to creative camera control.&lt;/li&gt;
      &lt;li&gt;Cannot be interpolated.&lt;/li&gt;
      &lt;li&gt;To convert the matrix to a right handed projection, simply negate the z components of each plane of the matrix.&lt;/li&gt;
    &lt;/ul&gt;    &lt;br/&gt;
    &lt;li&gt;&lt;b&gt;Depth biasing:&lt;/b&gt;&lt;/li&gt;
    &lt;ul&gt;      &lt;li&gt;Is a special case which only modifies the Z plane.&lt;/li&gt;
      &lt;li&gt;Does not generally contribute to creative camera control.&lt;/li&gt;
      &lt;li&gt;There are alternative methods of applying depth biasing.&lt;/li&gt;
      &lt;li&gt;Could be added to the formulation, but:&lt;/li&gt;
      &lt;ul&gt;        &lt;li&gt;Is a geometry and rendering problem rather than a camera control.&lt;/li&gt;
        &lt;li&gt;Requires a degree of knowledge of the available depth precision.&lt;/li&gt;
      &lt;/ul&gt;    &lt;/ul&gt;    &lt;br/&gt;
    &lt;li&gt;&lt;b&gt;Infinite projections:&lt;/b&gt;&lt;/li&gt;
    &lt;ul&gt;      &lt;li&gt;Are a special case which only modifies the Z plane.&lt;/li&gt;
      &lt;li&gt;Do not generally contribute to creative camera control.&lt;/li&gt;
      &lt;li&gt;Can be useful for shadow volumes and similar, but:&lt;/li&gt;
      &lt;ul&gt;        &lt;li&gt;Require care with precision and numerical stability.&lt;/li&gt;
        &lt;li&gt;Cause issues with functions expecting a finite projection.&lt;/li&gt;
        &lt;li&gt;Are best handled as special cases or matrix modifiers.&lt;/li&gt;
      &lt;/ul&gt;    &lt;/ul&gt;    &lt;br/&gt;
    &lt;li&gt;&lt;b&gt;Skewed near and far clipping planes:&lt;/b&gt;&lt;/li&gt;
    &lt;ul&gt;      &lt;li&gt;Is a special case which only modifies the Z plane.&lt;/li&gt;
      &lt;li&gt;Do not generally contribute to creative camera control.&lt;/li&gt;
      &lt;li&gt;Can be useful for portal rendering and similar, but:&lt;/li&gt;
      &lt;ul&gt;        &lt;li&gt;Require decisions about which plane to modify and how to handle the remaining depth clip plane.&lt;/li&gt;
        &lt;li&gt;Are hard to visualize due to the relationship between the near and far plane.&lt;/li&gt;
        &lt;li&gt;Do not produce a depth buffer consistent with the rest of the scene.&lt;/li&gt;
        &lt;li&gt;There are alternative methods of dealing with portals.&lt;/li&gt;
        &lt;li&gt;Are best handled as special cases or matrix modifiers.&lt;/li&gt;
      &lt;/ul&gt;    &lt;/ul&gt;  &lt;/ol&gt;  &lt;hr/&gt;  &lt;p&gt;If you want to know more about matrix biasing, infinite projections and skewed near and far clipping planes, I suggest you check out the excellent explanation by Eric Lengyel which can be found &lt;a href="http://www.terathon.com/gdc07_lengyel.ppt"&gt;here&lt;/a&gt;.&lt;/p&gt;  &lt;hr/&gt;  &lt;p&gt;Up next: OK fine, so why is this any better than what I currently use?&lt;/p&gt;  &lt;hr/&gt;  &lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-8878058554454206748?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/8878058554454206748/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2011/01/ok-so-why-is-formulation-given-in-my.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/8878058554454206748'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/8878058554454206748'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2011/01/ok-so-why-is-formulation-given-in-my.html' title='Why only &apos;almost&apos;?'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-499734127184648103</id><published>2010-11-25T23:27:00.008Z</published><updated>2011-01-31T09:58:39.935Z</updated><title type='text'>The ultimate projection calculation (almost)</title><content type='html'>&lt;hr/&gt;  &lt;p&gt;The following builds perspective and parallel projection matrices with support for center offsets and stereoscopic 3D.&lt;/p&gt;  &lt;hr/&gt;  &lt;h4&gt;Variables:&lt;/h4&gt;  &lt;pre&gt;    s&lt;sub&gt;   &lt;/sub&gt;   : stereo-scopic 3D eye separation
    p&lt;sub&gt;   &lt;/sub&gt;   : perspective (0 == parallel, 1 == perspective)
    n, f&lt;sub&gt;   &lt;/sub&gt;: near and far z clip depths
    w, h&lt;sub&gt;   &lt;/sub&gt;: width and height of viewport (at depth v&lt;sub&gt;z&lt;/sub&gt;)
    v&lt;sub&gt;xyz&lt;/sub&gt;   : center of viewport
    e&lt;sub&gt;xyz&lt;/sub&gt;   : center of projection (eye position)&lt;/pre&gt;  &lt;hr/&gt;  &lt;h4&gt;W-Plane:&lt;/h4&gt;  &lt;pre&gt;    W&lt;sub&gt;x&lt;/sub&gt; = 0
    W&lt;sub&gt;y&lt;/sub&gt; = 0
    W&lt;sub&gt;z&lt;/sub&gt; = p/(v&lt;sub&gt;z&lt;/sub&gt;-e&lt;sub&gt;z&lt;/sub&gt;)
    W&lt;sub&gt;w&lt;/sub&gt; = (v&lt;sub&gt;z&lt;/sub&gt;(1-p)-e&lt;sub&gt;z&lt;/sub&gt;)/(v&lt;sub&gt;z&lt;/sub&gt;-e&lt;sub&gt;z&lt;/sub&gt;)&lt;/pre&gt;  &lt;h4&gt;Z-Plane:&lt;/h4&gt;  &lt;pre&gt;    Z&lt;sub&gt;x&lt;/sub&gt; = 0
    Z&lt;sub&gt;y&lt;/sub&gt; = 0
    Z&lt;sub&gt;z&lt;/sub&gt; = (2(v&lt;sub&gt;z&lt;/sub&gt;(1-p)-e&lt;sub&gt;z&lt;/sub&gt;)+p(f+n))/((f-n)(v&lt;sub&gt;z&lt;/sub&gt;-e&lt;sub&gt;z&lt;/sub&gt;))
    Z&lt;sub&gt;w&lt;/sub&gt; = -((v&lt;sub&gt;z&lt;/sub&gt;(1-p)-e&lt;sub&gt;z&lt;/sub&gt;)(f+n)+2fnp)/((f-n)(v&lt;sub&gt;z&lt;/sub&gt;-e&lt;sub&gt;z&lt;/sub&gt;))&lt;/pre&gt;  &lt;h4&gt;Y-Plane:&lt;/h4&gt;  &lt;pre&gt;    Y&lt;sub&gt;x&lt;/sub&gt; = 0
    Y&lt;sub&gt;y&lt;/sub&gt; = 2/h
    Y&lt;sub&gt;z&lt;/sub&gt; = 2(e&lt;sub&gt;y&lt;/sub&gt;-v&lt;sub&gt;y&lt;/sub&gt;)/(h(v&lt;sub&gt;z&lt;/sub&gt;-e&lt;sub&gt;z&lt;/sub&gt;))
    Y&lt;sub&gt;w&lt;/sub&gt; = 2(v&lt;sub&gt;y&lt;/sub&gt;e&lt;sub&gt;z&lt;/sub&gt;-e&lt;sub&gt;y&lt;/sub&gt;v&lt;sub&gt;z&lt;/sub&gt;)/(h(v&lt;sub&gt;z&lt;/sub&gt;-e&lt;sub&gt;z&lt;/sub&gt;))&lt;/pre&gt;  &lt;h4&gt;X-Plane:&lt;/h4&gt;  &lt;pre&gt;    X&lt;sub&gt;x&lt;/sub&gt; = 2/w
    X&lt;sub&gt;y&lt;/sub&gt; = 0
    X&lt;sub&gt;z&lt;/sub&gt; = (2(e&lt;sub&gt;x&lt;/sub&gt;-v&lt;sub&gt;x&lt;/sub&gt;)+[±s])/(w(v&lt;sub&gt;z&lt;/sub&gt;-e&lt;sub&gt;z&lt;/sub&gt;))
    X&lt;sub&gt;w&lt;/sub&gt; = (2(v&lt;sub&gt;x&lt;/sub&gt;e&lt;sub&gt;z&lt;/sub&gt;-e&lt;sub&gt;x&lt;/sub&gt;v&lt;sub&gt;z&lt;/sub&gt;)-[±sv&lt;sub&gt;z&lt;/sub&gt;])/(w(v&lt;sub&gt;z&lt;/sub&gt;-e&lt;sub&gt;z&lt;/sub&gt;))&lt;/pre&gt;  &lt;hr/&gt;  &lt;h4&gt;Notes:&lt;/h4&gt;  &lt;p&gt;The projection produced by this formula has x, y and z extents of -1:+1.&lt;/p&gt;  &lt;p&gt;The perspective control value p is not restricted to integer values.&lt;/p&gt;  &lt;p&gt;The view plane is defined by z. Objects on the view plane will have a homogeneous w value of 1.0 after the transform.&lt;/p&gt;  &lt;hr/&gt;  &lt;p&gt;Upcoming posts will explain what this formulation does, what it does not do and why you should use it.&lt;/p&gt;  &lt;hr/&gt;  &lt;br/&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-499734127184648103?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/499734127184648103/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2010/11/ultimate-projection-calculation-almost.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/499734127184648103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/499734127184648103'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2010/11/ultimate-projection-calculation-almost.html' title='The ultimate projection calculation (almost)'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-3058203999779691898</id><published>2010-11-13T20:18:00.000Z</published><updated>2010-11-13T20:18:46.001Z</updated><title type='text'>What it's all about</title><content type='html'>I'm a games programmer. I've been a games programmer for a very long long time. As of a day or two ago, I'm "between jobs".&lt;br /&gt;
&lt;br /&gt;
So what is this blog for? Well, it's a place to gather my thoughts and hopefully share some of what I have learned and what I find interesting.&lt;br /&gt;
&lt;br /&gt;
My first subject is going to be one that has frustrated me for some time - virtual camera projections. Most graphics programmers will have used projection matrices, but few know much about the full range of&amp;nbsp;features that can be captured by this matrix, not just to simplify common rendering problems but to grab and drive the attention of the&amp;nbsp;viewer. Common implementations of the projection matrix&amp;nbsp;supplied by SDKs and off the shelf implementations often attempt to simplify the matrix construction, but that very simplification hides the power of this matrix and frequently results in more complicated code and multiple matrix construction functions.&lt;br /&gt;
&lt;br /&gt;
The aim of my first discussion is to show the possibilities embedded within the projection matrix.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-3058203999779691898?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/3058203999779691898/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2010/11/what-its-all-about.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/3058203999779691898'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/3058203999779691898'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2010/11/what-its-all-about.html' title='What it&apos;s all about'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21571311396420149.post-2105521376479101417</id><published>2010-10-14T03:05:00.000+01:00</published><updated>2010-10-14T03:05:21.438+01:00</updated><title type='text'>First post</title><content type='html'>Details to follow...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21571311396420149-2105521376479101417?l=and-what-happened.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://and-what-happened.blogspot.com/feeds/2105521376479101417/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://and-what-happened.blogspot.com/2010/10/first-post.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/2105521376479101417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21571311396420149/posts/default/2105521376479101417'/><link rel='alternate' type='text/html' href='http://and-what-happened.blogspot.com/2010/10/first-post.html' title='First post'/><author><name>Icabod</name><uri>http://www.blogger.com/profile/02810686816312950399</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
