| Name |
| |
| NV_viewport_swizzle |
| |
| Name Strings |
| |
| GL_NV_viewport_swizzle |
| |
| Contact |
| |
| Jeff Bolz, NVIDIA Corporation (jbolz 'at' nvidia.com) |
| Pat Brown, NVIDIA Corporation (pbrown 'at' nvidia.com) |
| |
| Contributors |
| |
| Mathias Heyer, NVIDIA |
| |
| Status |
| |
| Shipping. |
| |
| Version |
| |
| Last Modified Date: April 7, 2015 |
| Revision: 1 |
| |
| Number |
| |
| OpenGL Extension #483 |
| OpenGL ES Extension #258 |
| |
| Dependencies |
| |
| This extension is written against the OpenGL 4.3 specification |
| (Compatibility Profile). |
| |
| This extension interacts with the OpenGL ES 3.1 (March 17, 2014) |
| specification. |
| |
| This extension interacts with NV_viewport_array2. |
| |
| Overview |
| |
| This extension provides a new per-viewport swizzle that can modify the |
| position of primitives sent to each viewport. New viewport swizzle state |
| is added for each viewport, and a new position vector is computed for each |
| vertex by selecting from and optionally negating any of the four |
| components of the original position vector. |
| |
| This new viewport swizzle is useful for a number of algorithms, including |
| single-pass cubemap rendering (broadcasting a primitive to multiple faces |
| and reorienting the vertex position for each face) and voxel |
| rasterization. The per-viewport component remapping and negation provided |
| by the swizzle allows application code to re-orient three-dimensional |
| geometry with a view along any of the X, Y, or Z axes. If a perspective |
| projection and depth buffering is required, 1/W buffering should be used, |
| as described in the single-pass cubemap rendering example in the "Issues" |
| section below. |
| |
| New Procedures and Functions |
| |
| void ViewportSwizzleNV(uint index, |
| enum swizzlex, enum swizzley, |
| enum swizzlez, enum swizzlew); |
| |
| New Tokens |
| |
| Accepted by the <swizzlex>, <swizzley>, <swizzlez>, and <swizzlew> |
| parameters of ViewportSwizzleNV: |
| |
| VIEWPORT_SWIZZLE_POSITIVE_X_NV 0x9350 |
| VIEWPORT_SWIZZLE_NEGATIVE_X_NV 0x9351 |
| VIEWPORT_SWIZZLE_POSITIVE_Y_NV 0x9352 |
| VIEWPORT_SWIZZLE_NEGATIVE_Y_NV 0x9353 |
| VIEWPORT_SWIZZLE_POSITIVE_Z_NV 0x9354 |
| VIEWPORT_SWIZZLE_NEGATIVE_Z_NV 0x9355 |
| VIEWPORT_SWIZZLE_POSITIVE_W_NV 0x9356 |
| VIEWPORT_SWIZZLE_NEGATIVE_W_NV 0x9357 |
| |
| Accepted by the <pname> parameter of GetBooleani_v, GetDoublei_v, |
| GetIntegeri_v, GetFloati_v, and GetInteger64i_v: |
| |
| VIEWPORT_SWIZZLE_X_NV 0x9358 |
| VIEWPORT_SWIZZLE_Y_NV 0x9359 |
| VIEWPORT_SWIZZLE_Z_NV 0x935A |
| VIEWPORT_SWIZZLE_W_NV 0x935B |
| |
| Additions to Chapter 13 of the OpenGL 4.3 (Compatibility Profile) |
| Specification (Fixed-Function Vertex Post-Processing) |
| |
| Modify Section 13.2 (Transform Feedback), p. 453 |
| |
| Modify the first paragraph: |
| |
| ...The vertices are fed back after vertex color clamping, but before |
| viewport swizzling and viewport mask expansion, flatshading, and |
| clipping. ... |
| |
| |
| Add a new Section 13.X (Viewport Swizzle) after 13.3 (Primitive Queries) |
| |
| Each primitive sent to a given viewport has a swizzle and optional |
| negation applied to its clip coordinates. The swizzle that is applied |
| depends on the viewport index, and is controlled by the command |
| |
| void ViewportSwizzleNV(uint index, |
| enum swizzlex, enum swizzley, |
| enum swizzlez, enum swizzlew); |
| |
| The viewport specified by <index> has its x,y,z,w swizzle state set to the |
| corresponding <swizzlex>, <swizzley>, <swizzlez>, <swizzlew> value. If the |
| value of VIEWPORT_SWIZZLE_X_NV is denoted by <swizzlex>, swizzling computes |
| the new x component of the position as |
| |
| if (swizzlex == VIEWPORT_SWIZZLE_POSITIVE_X_NV) x' = x; |
| if (swizzlex == VIEWPORT_SWIZZLE_NEGATIVE_X_NV) x' = -x; |
| if (swizzlex == VIEWPORT_SWIZZLE_POSITIVE_Y_NV) x' = y; |
| if (swizzlex == VIEWPORT_SWIZZLE_NEGATIVE_Y_NV) x' = -y; |
| if (swizzlex == VIEWPORT_SWIZZLE_POSITIVE_Z_NV) x' = z; |
| if (swizzlex == VIEWPORT_SWIZZLE_NEGATIVE_Z_NV) x' = -z; |
| if (swizzlex == VIEWPORT_SWIZZLE_POSITIVE_W_NV) x' = w; |
| if (swizzlex == VIEWPORT_SWIZZLE_NEGATIVE_W_NV) x' = -w; |
| |
| Similar selections are performed for the y, z, and w coordinates. This |
| swizzling is applied after transform feedback, but before clipping and |
| perspective divide. |
| |
| Errors: |
| |
| - The error INVALID_VALUE is generated if <index> is greater than or equal |
| to the value of MAX_VIEWPORTS. |
| |
| - The error INVALID_ENUM is generated if any of <swizzlex>, <swizzley>, |
| <swizzlez>, or <swizzlew> are not one of |
| VIEWPORT_SWIZZLE_{POSITIVE,NEGATIVE}_{X,Y,Z,W}. |
| |
| |
| Modify Section 13.6.1 (Controlling the Viewport) |
| |
| (modify the first paragraph, p. 470, as edited by NV_viewport_array2, |
| using "transformed and swizzled" instead of "transformed") |
| |
| Multiple viewports are available ... The primitive is transformed and |
| swizzled using the state of the selected viewport. ... |
| |
| ... If bit <i> is set in the mask, the primitive is emitted to viewport |
| <i> and transformed and swizzled using the state of viewport <i>. ... |
| |
| |
| New Implementation Dependent State |
| |
| None. |
| |
| New State |
| |
| Get Value Get Command Type Initial Value Description Sec. Attribute |
| --------- ----------- ---- ------------- ----------- ---- --------- |
| VIEWPORT_SWIZZLE_X_NV GetIntegeri_v nxZ8 VIEWPORT_SWIZZLE- coordinate and sign for 13.X viewport |
| POSITIVE_X viewport swizzling |
| VIEWPORT_SWIZZLE_Y_NV GetIntegeri_v nxZ8 VIEWPORT_SWIZZLE- coordinate and sign for 13.X viewport |
| POSITIVE_Y viewport swizzling |
| VIEWPORT_SWIZZLE_Z_NV GetIntegeri_v nxZ8 VIEWPORT_SWIZZLE- coordinate and sign for 13.X viewport |
| POSITIVE_Z viewport swizzling |
| VIEWPORT_SWIZZLE_W_NV GetIntegeri_v nxZ8 VIEWPORT_SWIZZLE- coordinate and sign for 13.X viewport |
| POSITIVE_W viewport swizzling |
| |
| Additions to the AGL/GLX/WGL Specifications |
| |
| None. |
| |
| GLX Protocol |
| |
| None. |
| |
| Errors |
| |
| The error INVALID_VALUE is generated by ViewportSwizzleNV if <index> is |
| greater than or equal to the value of MAX_VIEWPORTS. |
| |
| The error INVALID_ENUM is generated by ViewportSwizzleNV if any of |
| <swizzlex>, <swizzley>, <swizzlez>, or <swizzlew> are not one of |
| VIEWPORT_SWIZZLE_{POSITIVE,NEGATIVE}_{X,Y,Z,W}. |
| |
| Interactions with OpenGL ES 3.1 |
| |
| Remove references to GetDoublei_v and GetBooleani_v. Also remove the |
| reference to 'vertex color clamping'. |
| |
| Interactions with NV_viewport_array2 |
| |
| This specification modifies language added/changed by NV_viewport_array2. |
| There are no functional interactions between the two extensions, though we |
| expect that all implementations of this extension will support |
| NV_viewport_array2 or similar functionality. |
| |
| Issues |
| |
| (1) Where does viewport swizzling occur in the pipeline? |
| |
| RESOLVED: Despite being associated with the viewport, viewport swizzling |
| must happen prior to the viewport transform. In particular, it needs to |
| be performed before clipping and perspective division. |
| |
| The viewport mask expansion (NV_viewport_array2) and the viewport swizzle |
| could potentially be performed before or after transform feedback, but |
| feeding back several viewports worth of primitives with different swizzles |
| doesn't seem particularly useful. This specification applies the viewport |
| mask and swizzle after transform feedback, and makes primitive queries |
| only count each primitive once. |
| |
| (2) Any interesting examples of how this extension, NV_viewport_array2, |
| and NV_geometry_shader_passthrough can be used together in practice? |
| |
| RESOLVED: One interesting use case for this extension is for single-pass |
| rendering to a cubemap. In this example, the application would attach a |
| cubemap texture to a layered FBO where the six cube faces are treated as |
| layers. Vertices are sent through the vertex shader without applying a |
| projection matrix, where the gl_Position output is (x,y,z,1) and the |
| center of the cubemap is at (0,0,0). With unextended OpenGL, one could |
| have a conventional instanced geometry shader that looks something like |
| the following: |
| |
| layout(invocations = 6) in; // separate invocation per face |
| layout(triangles) in; |
| layout(triangle_strip) out; |
| layout(max_vertices = 3) out; |
| |
| in Inputs { |
| vec2 texcoord; |
| vec3 normal; |
| vec4 baseColor; |
| } v[]; |
| |
| out Outputs { |
| vec2 texcoord; |
| vec3 normal; |
| vec4 baseColor; |
| }; |
| |
| void main() |
| { |
| int face = gl_InvocationID; // which face am I? |
| |
| // Project gl_Position for each vertex onto the cube map face. |
| vec4 positions[3]; |
| for (int i = 0; i < 3; i++) { |
| positions[i] = rotate(gl_in[i].gl_Position, face); |
| } |
| |
| // If the primitive doesn't project onto this face, we're done. |
| if (shouldCull(positions)) { |
| return; |
| } |
| |
| // Otherwise, emit a copy of the input primitive to the |
| // appropriate face (using gl_Layer). |
| for (int i = 0; i < 3; i++) { |
| gl_Layer = face; |
| gl_Position = positions[i]; |
| texcoord = v[i].texcoord; |
| normal = v[i].normal; |
| baseColor = v[i].baseColor; |
| EmitVertex(); |
| } |
| } |
| |
| With passthrough geometry shaders, this can be done using a much simpler |
| shader: |
| |
| layout(triangles) in; |
| layout(passthrough) in Inputs { |
| vec2 texcoord; |
| vec3 normal; |
| vec4 baseColor; |
| } |
| layout(passthrough) in gl_PerVertex { |
| vec4 gl_Position; |
| } gl_in[]; |
| layout(viewport_relative) out int gl_Layer; |
| |
| void main() |
| { |
| // Figure out which faces the primitive projects onto and |
| // generate a corresponding viewport mask. |
| uint mask = 0; |
| for (int i = 0; i < 6; i++) { |
| if (!shouldCull(face)) { |
| mask |= 1U << i; |
| } |
| } |
| gl_ViewportMask = mask; |
| gl_Layer = 0; |
| } |
| |
| The application code is set up so that each of the six cube faces has a |
| separate viewport (numbered 0..5). Each face also has a separate swizzle, |
| programmed via the ViewportSwizzleNV() command. The viewport swizzle |
| feature performs the coordinate transformation handled by the rotate() |
| function in the original shader. The "viewport_relative" layout qualifier |
| says that the viewport number (0..5) is added to the base gl_Layer value |
| of zero to determine which layer (cube face) the primitive should be sent |
| to. |
| |
| Note that the use of the passed through input <normal> in this example |
| suggests that the fragment shader in this example would perform an |
| operation like per-fragment lighting. The viewport swizzle would |
| transform the position to be face-relative, but <normal> would remain in |
| the original coordinate system. It seems likely that the fragment shader |
| in either version of the example would want to perform lighting in the |
| original coordinate system. It would likely do this by reconstructing the |
| position of the fragment in the original coordinate system using |
| gl_FragCoord, a constant or uniform holding the size of the cube face, and |
| the input gl_ViewportIndex (or gl_Layer), which identifies the cube face. |
| Since the value of <normal> is in the original coordinate system, it would |
| not need to be modified as part of this coordinate transformation. |
| |
| Note that while the rotate() operation in the regular geometry shader |
| above could include an arbitrary post-rotation projection matrix, the |
| viewport swizzle does not support arbitrary math. To get proper |
| projection, 1/W buffering should be used. To do this: |
| |
| (1) Program the viewport swizzles to move the pre-projection W eye |
| coordinate (typically 1.0) into the Z coordinate of the swizzle output |
| and the eye coordinate component used for depth into the W coordinate. |
| For example, the viewport corresponding to the +Z face might use a |
| swizzle of (+X, -Y, +W, +Z). The Z normalized device coordinate |
| computed after swizzling would then be z'/w' = 1/Z_eye. |
| |
| (2a) On NVIDIA implementations supporting floating-point depth buffers |
| with values outside [0,1], prevent unwanted near plane clipping by |
| enabling DEPTH_CLAMP. Ensure that the depth clamp doesn't mess up depth |
| testing by programming the depth range to very large values, such as |
| glDepthRangedNV(-z, +z), where z == 2^127. It should be possible to use |
| IEEE infinity encodings also (0xFF800000 for -INF, 0x7F800000 for +INF). |
| Even when near/far clipping is disabled, primitives extending behind the |
| eye will still be clipped because one or more vertices will have a |
| negative W coordinate and fail X/Y clipping tests. |
| |
| (2b) On other implementations, scale X, Y, and Z eye coordinates so that |
| vertices on the near plane have a post-swizzle W coordinate of 1.0. For |
| example, if the near plane is at Z_eye = 1/256, scale X, Y, and Z by |
| 256. Also, ideally, program the depth range transformation to be a NOP |
| by using a clip control depth mode (OpenGL 4.5) of ZERO_TO_ONE. |
| |
| (3) Adjust depth testing to reflect the fact that 1/W values are large |
| near the eye and small away from the eye. Clear the depth buffer to |
| zero (infinitely far away) and use a depth test of GREATER instead of |
| LESS. |
| |
| Revision History |
| |
| Revision 1 |
| - Internal revisions. |