001/* 002 * Renderer 9. The MIT License. 003 * Copyright (c) 2022 rlkraft@pnw.edu 004 * See LICENSE for details. 005*/ 006 007package renderer.scene; 008 009/** 010 We use two steps to transform the camera's configurable perspective 011 view volume into the standard perspective view volume. The first step 012 skews the camera's view volume so that its center line is the negative 013 z-axis (this takes an asymmetric view volume and makes it symmetric). 014 The second step scales the skewed view volume so that it intersects 015 the image plane, {@code z = -1}, with corners {@code (-1, -1, -1)} 016 and {@code (+1, +1, -1)} (this gives the symmetric view volume a 90 017 degree field-of-view). 018<p> 019 Let us derive the matrix for transforming the camera's perspective 020 view volume into the standard perspective view volume. Suppose the 021 camera's perspective view volume has an asymmetrical cross section 022 in the yz-plane that is determined by the top and bottom points 023 {@code (t, -1)} and {@code (b, -1)}. The center line of this cross 024 section is determined by the point {@code ((t+b)/2, -1)}. We want to 025 skew the yz-plane in the y-direction along the z-axis so that the 026 field-of-view's center line becomes the z-axis. So we need to solve 027 this matrix equation for the value of the skew factor {@code s}. 028 <pre>{@code 029 [ 1 s ] * [ (t+b)/2 ] = [ 0 ] 030 [ 0 1 ] [ -1 ] [ -1 ] 031 }</pre> 032 This simplifies to the equation 033 <pre>{@code 034 (t + b)/2 - s = 0, 035 s = (t + b)/(2). 036 }</pre> 037<p> 038 A similar calculation can be made for skewing the field-of-view in 039 the xz-plane. 040<p> 041 Once the field-of-view in the yz-plane has been made symmetric with 042 respect to the z-axis, we want to scale it in the y-direction so that 043 the scaled field-of-view has an angle at the origin of 90 degrees. We 044 need to scale the point {@code ((t-b)/2, -1)} to the point {@code (1, -1)} 045 (and the point {@code ((b-t)/2, -1)} to the point {@code (-1, -1)}). So 046 we need to solve this matrix equation for the value of the scale factor 047 {@code s}. 048 <pre>{@code 049 [ s 0 ] * [ (t-b)/2 ] = [ 1 ] 050 [ 0 1 ] [ -1 ] [ -1 ] 051 }</pre> 052 This simplifies to the equation 053 <pre>{@code 054 s * (t - b)/2 = 1, 055 s = 2/(t - b). 056 }</pre> 057<p> 058 A similar calculation can be made for scaling the skewed field-of-view 059 in the xz-plane. 060<p> 061 The following matrix skews the camera's view volume along the z-axis so 062 that the transformed view volume will be centered on the negative z-axis. 063 <pre>{@code 064 [ 1 0 (r+l)/(2) 0 ] 065 [ 0 1 (t+b)/(2) 0 ] 066 [ 0 0 1 0 ] 067 [ 0 0 0 1 ] 068 }</pre> 069 The following matrix scales the skewed view volume so that it will 070 be 2 units wide and 2 units tall at the image plane {@code z = -1}. 071 <pre>{@code 072 [ 2/(r-l) 0 0 0 ] 073 [ 0 2/(t-b) 0 0 ] 074 [ 0 0 1 0 ] 075 [ 0 0 0 1 ] 076 }</pre> 077 The matrix product looks like this. 078 <pre>{@code 079 [ 1 0 (r+l)/2 0 ] [ 2/(r-l) 0 0 0 ] 080 [ 0 1 (t+b)/2 0 ] * [ 0 2/(t-b) 0 0 ] 081 [ 0 0 1 0 ] [ 0 0 1 0 ] 082 [ 0 0 0 1 ] [ 0 0 0 1 ] 083 084 [ 2/(r-l) 0 (r+l)/(r-l) 0 ] 085 = [ 0 2/(t-b) (t+b)/(t-b) 0 ] 086 [ 0 0 1 0 ] 087 [ 0 0 0 1 ] 088 }</pre> 089 This product matrix transforms the camera's configurable perspective 090 view volume into the standard normalized perspective view volume 091 whose intersection with the image plane, {@code z = -1}, has 092 {@code left = -1}, {@code right = +1}, {@code bottom = -1}, 093 and {@code top = +1}. 094*/ 095public final class PerspectiveNormalizeMatrix 096{ 097 /** 098 This is a static factory method. 099 <p> 100 Construct the {@link Matrix} that transforms from the 101 {@link Camera}'s perspective view coordinate system to 102 the normalized perspective camera coordinate system. 103 104 @param l left edge of view rectangle in the image plane z = -1 105 @param r right edge of view rectangle in the image plane z = -1 106 @param b bottom edge of view rectangle in the image plane z = -1 107 @param t top edge of view rectangle in the image plane z = -1 108 @return a new {@code Matrix} object containing the perspective normalization matrix 109 */ 110 public static Matrix build(final double l, final double r, 111 final double b, final double t) 112 { 113 final Matrix m1, m2; 114 115 m1 = Matrix.buildFromColumns( 116 new Vector( 1.0, 0.0, 0.0, 0.0), 117 new Vector( 0.0, 1.0, 0.0, 0.0), 118 new Vector((r+l)/2, (t+b)/2, 1.0, 0.0), 119 new Vector( 0.0, 0.0, 0.0, 1.0)); 120 121 m2 = Matrix.buildFromColumns( 122 new Vector(2/(r-l), 0.0, 0.0, 0.0), 123 new Vector( 0.0, 2/(t-b), 0.0, 0.0), 124 new Vector( 0.0, 0.0, 1.0, 0.0), 125 new Vector( 0.0, 0.0, 0.0, 1.0)); 126 127 return m2.times(m1); 128 } 129}