001/* 002 003*/ 004 005package renderer.pipeline; 006import renderer.scene.*; 007import renderer.framebuffer.*; 008 009/** 010 Transform each {@link Vertex} of a {@link LineSegment} from 011 projected camera coordinates (i.e., image plane coordinates) 012 to viewport coordinates. 013 014 015<p> 016 The image plane contains a view rectangle with 017 <pre>{@code 018 -1 <= x <= 1, 019 -1 <= y <= 1, 020 }</pre> 021 The view rectangle has the important job of determining what part of the scene is visible to our renderer. 022<p> 023 In the previous pipeline stage, every line segment from the scene was projected onto the image plane. We want only that part of each projected line segment contained in the view rectangle to be visible to our renderer and rasterized into the framebuffer's viewport. To prepare for the rasterization step, we need to convert each vertex's x and y coordinates 024 025 026<pre>{@code 027 (w+1/2,h+1/2) 028 y-axis +----------------------------------------+ 029 | | | 030 | (+1,+1) | | 031 +-----|-----+ | | 032 | | | | | 033 | | | | | 034----------+----------- x-axis | | 035 | | | | | 036 | | | | | 037 +-----|-----+ | | 038 (-1,-1) | | | 039 | | | 040 | | 041 View Rectangle +----------------------------------------+ 042 (in the view plane) (1/2,1/2) 043 Logical Viewport 044}</pre> 045 046 047 048Notice how the view rectangle is a square but the logical viewport is a reactangle (though it may be a square). 049 050The view rectangle always has -1.0 <= x <= 1.0 and -1.0 <= y <= 1.0. 051The logical viewport always has 0.5 <= x <= w+0.5 and 0.5 <= y <= h+0.5 where w and h are the width and height of the FrameBuffer's current viewport. 052 053If the aspect ratio, w/h, of the logical (and FrameBuffer) viewport is not 1, then the image in the view rectangle will be distorted when it is transformed to the logical viewport. (NOTE: Starting with Renderer 7, the aspect ratio of the viewport will usually match the aspect ratio of the renderer's view volume, so the distortion caused by the viewport transformation will undo the distortion caused by the normalization transformation. If the view volume and viewport aspect ratios do not match, then the final image in the framebuffer's viewport will be a distortion of the image in the view volume's view rectangle.). 054 055 056 057 For each {@link Vertex} object in each {@link LineSegment} object, 058 transform the Vertex object from image plane coordinates to viewport 059 coordinates so that the view rectangle in the image plane with 060 <pre>{@code 061 -1 <= x <= 1, 062 -1 <= y <= 1, 063 }</pre> 064 transforms into a viewport where 065 <pre>{@code 066 0.5 <= x < w + 0.5, 067 0.5 <= y < h + 0.5, 068 }</pre> 069 where 070 <pre>{@code 071 w = number of horizontal pixels in the viewport, 072 h = number of vertical pixels in the viewport. 073 }</pre> 074<p> 075 The goal of this transformation is to put a logical pixel with 076 integer coordinates at the center of each square physical pixel. 077 The logical pixel with integer coordinates (m, n) represents the 078 square pixel with 079 <pre>{@code 080 m - 0.5 <= x < m + 0.5, 081 n - 0.5 <= y < n + 0.5. 082 }</pre> 083 Notice that logical pixel integer coordinates (m.n) have 084 <pre>{@code 085 1 <= m <= w 086 1 <= n <= h. 087 }</pre> 088<p> 089 Let us derive the formulas for the viewport transformation (we will 090 derive the x-coordinate formula; the y-coordinate formula is similar). 091<p> 092 Let x_p denote an x-coordinate in the image plane and let x_vp denote 093 an x-coordinate in the viewport. If a vertex is on the left edge of 094 the view rectangle (with x_p = -1), then it should be transformed to 095 the left edge of the viewport (with x_vp = 0.5). And if a vertex is on 096 the right edge of the view rectangle (with x_p = 1), then it should be 097 transformed to the right edge of the viewport (with x_vp = w + 0.5). 098 These two facts are all we need to know to find the linear function 099 for the transformation of the x-coordinate. 100<p> 101 We need to calculate the slope m and intercept b of a linear function 102 <pre>{@code 103 x_vp = m * x_p + b 104 }</pre> 105 that converts image plane coordinates into viewport coordinates. We know, 106 from what we said above about the left and right edges of the view 107 rectangle, that 108 <pre>{@code 109 0.5 = (m * -1) + b, 110 w + 0.5 = (m * 1) + b. 111 }</pre> 112 If we add these last two equations together we get 113 <pre>{@code 114 w + 1 = 2*b 115 }</pre> 116 or 117 <pre>{@code 118 b = (w + 1)/2. 119 }</pre> 120 If we use b to solve for m we have 121 <pre>{@code 122 0.5 = (m * -1) + (w + 1)/2 123 1 = -2*m + w + 1 124 2*m = w 125 m = w/2. 126 }</pre> 127 So the linear transformation of the x-coordinate is 128 <pre>{@code 129 x_vp = (w/2) * x_p + (w+1)/2 130 = 0.5 + w/2 * (x_p + 1). 131 }</pre> 132 The equivalent formula for the y-coordinate is 133 <pre>{@code 134 y_vp = 0.5 + h/2 * (y_p + 1). 135 }</pre> 136*/ 137public class Viewport 138{ 139 /** 140 For each {@link LineSegment}, transform each {@link Vertex} 141 from image plane coordinates to viewport coordinates. 142 143 @param ls LineSegment to transform into viewport coordinates 144 @param fb FrameBuffer that holds the current viewport 145 */ 146 public static void viewport(LineSegment ls, FrameBuffer fb) 147 { 148 // Get the viewport dimensions. 149 int w = fb.getWidthVP(); 150 int h = fb.getHeightVP(); 151 152 // Transform the line segment's endpoints from 153 // image plane coordinates to viewport coordinates. 154 ls.v[0].x = 0.5 + w/2.001 * (ls.v[0].x + 1);//x_vp = 0.5 + w/2 * (x_p+1) 155 ls.v[0].y = 0.5 + h/2.001 * (ls.v[0].y + 1);//y_vp = 0.5 + h/2 * (y_p+1) 156 ls.v[1].x = 0.5 + w/2.001 * (ls.v[1].x + 1); 157 ls.v[1].y = 0.5 + h/2.001 * (ls.v[1].y + 1); 158 // NOTE: Notice the 2.001 fudge factor in the last four equations. 159 // This is explained on page 142 of 160 // "Jim Blinn's Corner: A Trip Down The Graphics Pipeline" 161 // by Jim Blinn, 1996, Morgan Kaufmann Publishers. 162 } 163}