1Name 2 3 NV_viewport_swizzle 4 5Name Strings 6 7 GL_NV_viewport_swizzle 8 9Contact 10 11 Jeff Bolz, NVIDIA Corporation (jbolz 'at' nvidia.com) 12 Pat Brown, NVIDIA Corporation (pbrown 'at' nvidia.com) 13 14Contributors 15 16 Mathias Heyer, NVIDIA 17 18Status 19 20 Shipping. 21 22Version 23 24 Last Modified Date: April 7, 2015 25 Revision: 1 26 27Number 28 29 OpenGL Extension #483 30 OpenGL ES Extension #258 31 32Dependencies 33 34 This extension is written against the OpenGL 4.3 specification 35 (Compatibility Profile). 36 37 This extension interacts with the OpenGL ES 3.1 (March 17, 2014) 38 specification. 39 40 This extension interacts with NV_viewport_array2. 41 42Overview 43 44 This extension provides a new per-viewport swizzle that can modify the 45 position of primitives sent to each viewport. New viewport swizzle state 46 is added for each viewport, and a new position vector is computed for each 47 vertex by selecting from and optionally negating any of the four 48 components of the original position vector. 49 50 This new viewport swizzle is useful for a number of algorithms, including 51 single-pass cubemap rendering (broadcasting a primitive to multiple faces 52 and reorienting the vertex position for each face) and voxel 53 rasterization. The per-viewport component remapping and negation provided 54 by the swizzle allows application code to re-orient three-dimensional 55 geometry with a view along any of the X, Y, or Z axes. If a perspective 56 projection and depth buffering is required, 1/W buffering should be used, 57 as described in the single-pass cubemap rendering example in the "Issues" 58 section below. 59 60New Procedures and Functions 61 62 void ViewportSwizzleNV(uint index, 63 enum swizzlex, enum swizzley, 64 enum swizzlez, enum swizzlew); 65 66New Tokens 67 68 Accepted by the <swizzlex>, <swizzley>, <swizzlez>, and <swizzlew> 69 parameters of ViewportSwizzleNV: 70 71 VIEWPORT_SWIZZLE_POSITIVE_X_NV 0x9350 72 VIEWPORT_SWIZZLE_NEGATIVE_X_NV 0x9351 73 VIEWPORT_SWIZZLE_POSITIVE_Y_NV 0x9352 74 VIEWPORT_SWIZZLE_NEGATIVE_Y_NV 0x9353 75 VIEWPORT_SWIZZLE_POSITIVE_Z_NV 0x9354 76 VIEWPORT_SWIZZLE_NEGATIVE_Z_NV 0x9355 77 VIEWPORT_SWIZZLE_POSITIVE_W_NV 0x9356 78 VIEWPORT_SWIZZLE_NEGATIVE_W_NV 0x9357 79 80 Accepted by the <pname> parameter of GetBooleani_v, GetDoublei_v, 81 GetIntegeri_v, GetFloati_v, and GetInteger64i_v: 82 83 VIEWPORT_SWIZZLE_X_NV 0x9358 84 VIEWPORT_SWIZZLE_Y_NV 0x9359 85 VIEWPORT_SWIZZLE_Z_NV 0x935A 86 VIEWPORT_SWIZZLE_W_NV 0x935B 87 88Additions to Chapter 13 of the OpenGL 4.3 (Compatibility Profile) 89Specification (Fixed-Function Vertex Post-Processing) 90 91 Modify Section 13.2 (Transform Feedback), p. 453 92 93 Modify the first paragraph: 94 95 ...The vertices are fed back after vertex color clamping, but before 96 viewport swizzling and viewport mask expansion, flatshading, and 97 clipping. ... 98 99 100 Add a new Section 13.X (Viewport Swizzle) after 13.3 (Primitive Queries) 101 102 Each primitive sent to a given viewport has a swizzle and optional 103 negation applied to its clip coordinates. The swizzle that is applied 104 depends on the viewport index, and is controlled by the command 105 106 void ViewportSwizzleNV(uint index, 107 enum swizzlex, enum swizzley, 108 enum swizzlez, enum swizzlew); 109 110 The viewport specified by <index> has its x,y,z,w swizzle state set to the 111 corresponding <swizzlex>, <swizzley>, <swizzlez>, <swizzlew> value. If the 112 value of VIEWPORT_SWIZZLE_X_NV is denoted by <swizzlex>, swizzling computes 113 the new x component of the position as 114 115 if (swizzlex == VIEWPORT_SWIZZLE_POSITIVE_X_NV) x' = x; 116 if (swizzlex == VIEWPORT_SWIZZLE_NEGATIVE_X_NV) x' = -x; 117 if (swizzlex == VIEWPORT_SWIZZLE_POSITIVE_Y_NV) x' = y; 118 if (swizzlex == VIEWPORT_SWIZZLE_NEGATIVE_Y_NV) x' = -y; 119 if (swizzlex == VIEWPORT_SWIZZLE_POSITIVE_Z_NV) x' = z; 120 if (swizzlex == VIEWPORT_SWIZZLE_NEGATIVE_Z_NV) x' = -z; 121 if (swizzlex == VIEWPORT_SWIZZLE_POSITIVE_W_NV) x' = w; 122 if (swizzlex == VIEWPORT_SWIZZLE_NEGATIVE_W_NV) x' = -w; 123 124 Similar selections are performed for the y, z, and w coordinates. This 125 swizzling is applied after transform feedback, but before clipping and 126 perspective divide. 127 128 Errors: 129 130 - The error INVALID_VALUE is generated if <index> is greater than or equal 131 to the value of MAX_VIEWPORTS. 132 133 - The error INVALID_ENUM is generated if any of <swizzlex>, <swizzley>, 134 <swizzlez>, or <swizzlew> are not one of 135 VIEWPORT_SWIZZLE_{POSITIVE,NEGATIVE}_{X,Y,Z,W}. 136 137 138 Modify Section 13.6.1 (Controlling the Viewport) 139 140 (modify the first paragraph, p. 470, as edited by NV_viewport_array2, 141 using "transformed and swizzled" instead of "transformed") 142 143 Multiple viewports are available ... The primitive is transformed and 144 swizzled using the state of the selected viewport. ... 145 146 ... If bit <i> is set in the mask, the primitive is emitted to viewport 147 <i> and transformed and swizzled using the state of viewport <i>. ... 148 149 150New Implementation Dependent State 151 152 None. 153 154New State 155 156 Get Value Get Command Type Initial Value Description Sec. Attribute 157 --------- ----------- ---- ------------- ----------- ---- --------- 158 VIEWPORT_SWIZZLE_X_NV GetIntegeri_v nxZ8 VIEWPORT_SWIZZLE- coordinate and sign for 13.X viewport 159 POSITIVE_X viewport swizzling 160 VIEWPORT_SWIZZLE_Y_NV GetIntegeri_v nxZ8 VIEWPORT_SWIZZLE- coordinate and sign for 13.X viewport 161 POSITIVE_Y viewport swizzling 162 VIEWPORT_SWIZZLE_Z_NV GetIntegeri_v nxZ8 VIEWPORT_SWIZZLE- coordinate and sign for 13.X viewport 163 POSITIVE_Z viewport swizzling 164 VIEWPORT_SWIZZLE_W_NV GetIntegeri_v nxZ8 VIEWPORT_SWIZZLE- coordinate and sign for 13.X viewport 165 POSITIVE_W viewport swizzling 166 167Additions to the AGL/GLX/WGL Specifications 168 169 None. 170 171GLX Protocol 172 173 None. 174 175Errors 176 177 The error INVALID_VALUE is generated by ViewportSwizzleNV if <index> is 178 greater than or equal to the value of MAX_VIEWPORTS. 179 180 The error INVALID_ENUM is generated by ViewportSwizzleNV if any of 181 <swizzlex>, <swizzley>, <swizzlez>, or <swizzlew> are not one of 182 VIEWPORT_SWIZZLE_{POSITIVE,NEGATIVE}_{X,Y,Z,W}. 183 184Interactions with OpenGL ES 3.1 185 186 Remove references to GetDoublei_v and GetBooleani_v. Also remove the 187 reference to 'vertex color clamping'. 188 189Interactions with NV_viewport_array2 190 191 This specification modifies language added/changed by NV_viewport_array2. 192 There are no functional interactions between the two extensions, though we 193 expect that all implementations of this extension will support 194 NV_viewport_array2 or similar functionality. 195 196Issues 197 198 (1) Where does viewport swizzling occur in the pipeline? 199 200 RESOLVED: Despite being associated with the viewport, viewport swizzling 201 must happen prior to the viewport transform. In particular, it needs to 202 be performed before clipping and perspective division. 203 204 The viewport mask expansion (NV_viewport_array2) and the viewport swizzle 205 could potentially be performed before or after transform feedback, but 206 feeding back several viewports worth of primitives with different swizzles 207 doesn't seem particularly useful. This specification applies the viewport 208 mask and swizzle after transform feedback, and makes primitive queries 209 only count each primitive once. 210 211 (2) Any interesting examples of how this extension, NV_viewport_array2, 212 and NV_geometry_shader_passthrough can be used together in practice? 213 214 RESOLVED: One interesting use case for this extension is for single-pass 215 rendering to a cubemap. In this example, the application would attach a 216 cubemap texture to a layered FBO where the six cube faces are treated as 217 layers. Vertices are sent through the vertex shader without applying a 218 projection matrix, where the gl_Position output is (x,y,z,1) and the 219 center of the cubemap is at (0,0,0). With unextended OpenGL, one could 220 have a conventional instanced geometry shader that looks something like 221 the following: 222 223 layout(invocations = 6) in; // separate invocation per face 224 layout(triangles) in; 225 layout(triangle_strip) out; 226 layout(max_vertices = 3) out; 227 228 in Inputs { 229 vec2 texcoord; 230 vec3 normal; 231 vec4 baseColor; 232 } v[]; 233 234 out Outputs { 235 vec2 texcoord; 236 vec3 normal; 237 vec4 baseColor; 238 }; 239 240 void main() 241 { 242 int face = gl_InvocationID; // which face am I? 243 244 // Project gl_Position for each vertex onto the cube map face. 245 vec4 positions[3]; 246 for (int i = 0; i < 3; i++) { 247 positions[i] = rotate(gl_in[i].gl_Position, face); 248 } 249 250 // If the primitive doesn't project onto this face, we're done. 251 if (shouldCull(positions)) { 252 return; 253 } 254 255 // Otherwise, emit a copy of the input primitive to the 256 // appropriate face (using gl_Layer). 257 for (int i = 0; i < 3; i++) { 258 gl_Layer = face; 259 gl_Position = positions[i]; 260 texcoord = v[i].texcoord; 261 normal = v[i].normal; 262 baseColor = v[i].baseColor; 263 EmitVertex(); 264 } 265 } 266 267 With passthrough geometry shaders, this can be done using a much simpler 268 shader: 269 270 layout(triangles) in; 271 layout(passthrough) in Inputs { 272 vec2 texcoord; 273 vec3 normal; 274 vec4 baseColor; 275 } 276 layout(passthrough) in gl_PerVertex { 277 vec4 gl_Position; 278 } gl_in[]; 279 layout(viewport_relative) out int gl_Layer; 280 281 void main() 282 { 283 // Figure out which faces the primitive projects onto and 284 // generate a corresponding viewport mask. 285 uint mask = 0; 286 for (int i = 0; i < 6; i++) { 287 if (!shouldCull(face)) { 288 mask |= 1U << i; 289 } 290 } 291 gl_ViewportMask = mask; 292 gl_Layer = 0; 293 } 294 295 The application code is set up so that each of the six cube faces has a 296 separate viewport (numbered 0..5). Each face also has a separate swizzle, 297 programmed via the ViewportSwizzleNV() command. The viewport swizzle 298 feature performs the coordinate transformation handled by the rotate() 299 function in the original shader. The "viewport_relative" layout qualifier 300 says that the viewport number (0..5) is added to the base gl_Layer value 301 of zero to determine which layer (cube face) the primitive should be sent 302 to. 303 304 Note that the use of the passed through input <normal> in this example 305 suggests that the fragment shader in this example would perform an 306 operation like per-fragment lighting. The viewport swizzle would 307 transform the position to be face-relative, but <normal> would remain in 308 the original coordinate system. It seems likely that the fragment shader 309 in either version of the example would want to perform lighting in the 310 original coordinate system. It would likely do this by reconstructing the 311 position of the fragment in the original coordinate system using 312 gl_FragCoord, a constant or uniform holding the size of the cube face, and 313 the input gl_ViewportIndex (or gl_Layer), which identifies the cube face. 314 Since the value of <normal> is in the original coordinate system, it would 315 not need to be modified as part of this coordinate transformation. 316 317 Note that while the rotate() operation in the regular geometry shader 318 above could include an arbitrary post-rotation projection matrix, the 319 viewport swizzle does not support arbitrary math. To get proper 320 projection, 1/W buffering should be used. To do this: 321 322 (1) Program the viewport swizzles to move the pre-projection W eye 323 coordinate (typically 1.0) into the Z coordinate of the swizzle output 324 and the eye coordinate component used for depth into the W coordinate. 325 For example, the viewport corresponding to the +Z face might use a 326 swizzle of (+X, -Y, +W, +Z). The Z normalized device coordinate 327 computed after swizzling would then be z'/w' = 1/Z_eye. 328 329 (2a) On NVIDIA implementations supporting floating-point depth buffers 330 with values outside [0,1], prevent unwanted near plane clipping by 331 enabling DEPTH_CLAMP. Ensure that the depth clamp doesn't mess up depth 332 testing by programming the depth range to very large values, such as 333 glDepthRangedNV(-z, +z), where z == 2^127. It should be possible to use 334 IEEE infinity encodings also (0xFF800000 for -INF, 0x7F800000 for +INF). 335 Even when near/far clipping is disabled, primitives extending behind the 336 eye will still be clipped because one or more vertices will have a 337 negative W coordinate and fail X/Y clipping tests. 338 339 (2b) On other implementations, scale X, Y, and Z eye coordinates so that 340 vertices on the near plane have a post-swizzle W coordinate of 1.0. For 341 example, if the near plane is at Z_eye = 1/256, scale X, Y, and Z by 342 256. Also, ideally, program the depth range transformation to be a NOP 343 by using a clip control depth mode (OpenGL 4.5) of ZERO_TO_ONE. 344 345 (3) Adjust depth testing to reflect the fact that 1/W values are large 346 near the eye and small away from the eye. Clear the depth buffer to 347 zero (infinitely far away) and use a depth test of GREATER instead of 348 LESS. 349 350Revision History 351 352 Revision 1 353 - Internal revisions. 354