Name ARB_clip_control Name Strings GL_ARB_clip_control Contact Mark J. Kilgard, NVIDIA Corporation (mjk 'at' nvidia.com) Contributors Timo Suoranta, Broadcom Piers Daniell, NVIDIA Stefan Dösinger, CodeWeavers Jeff Bolz, NVIDIA John McDonald, NVIDIA Brian Paul, VMware, Mesa3D Jason Mitchell, Valve Alex Corscadden, VMware Simon Bennett, VMware Mark Callow, HI Corporation Patrick Doane, Blizzard Pat Brown, NVIDIA Brano Kemen Notice Copyright (c) 2014 The Khronos Group Inc. Copyright terms at http://www.khronos.org/registry/speccopyright.html Specification Update Policy Khronos-approved extension specifications are updated in response to issues and bugs prioritized by the Khronos OpenGL Working Group. For extensions which have been promoted to a core Specification, fixes will first appear in the latest version of that core Specification, and will eventually be backported to the extension document. This policy is described in more detail at https://www.khronos.org/registry/OpenGL/docs/update_policy.php Status Complete. Approved by the ARB on June 26, 2014. Ratified by the Khronos Board of Promoters on August 7, 2014. Version Last Modified Date: 2018/04/06 NVIDIA Revision: 19 Number ARB Extension #160 Dependencies Written based on the wording of the OpenGL 4.4 (Compatibility Profile) specification. Overview This extension provides additional clip control modes to configure how clip space is mapped to window space. This extension's goal is to 1) allow OpenGL to effectively match Direct3D's coordinate system conventions, and 2) potentially improve the numerical precision of the Z coordinate mapping. Developers interested in this functionality may be porting content from Direct3D to OpenGL and/or interested in improving the numerical accuracy of depth testing, particularly with floating-point depth buffers. OpenGL's initial and conventional clip control state is configured by: glClipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE); where geometry with (x,y) normalized device coordinates of (-1,-1) correspond to the lower-left corner of the viewport and the near and far planes correspond to z normalized device coordinates of -1 and +1, respectively. This extension can be used to render content used in a Direct3D application in OpenGL in a straightforward way without modifying vertex or matrix data. When rendering into a window, the command glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); configures the near clip plane to correspond to a z normalized device coordinate of 0 as in Direct3D. Geometry with (x,y) normalized device coordinates of (-1,-1) correspond to the lower-left corner of the viewport in Direct3D, so no change relative to OpenGL conventions is needed there. Other state related to screen-space coordinates may need to be modified for the application to map from Direct3D to OpenGL window coordinate conventions. For example, the viewport rectangle in Direct3D needs to be inverted within the window to work properly in OpenGL windowed rendering: glViewport(d3d_viewport_x, window_height - (d3d_viewport_y + d3d_viewport_height), d3d_viewport_width, d3d_viewport_height); When rendering Direct3D content into a framebuffer object in OpenGL, there is one complication -- how to get a correct image *out* of the related textures. Direct3D applications would expect a texture coordinate of (0,0) to correspond to the upper-left corner of a rendered image, while OpenGL FBO conventions would map (0,0) to the lower-left corner of the rendered image. For applications wishing to use Direct3D content with unmodified texture coordinates, the command glClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE); configures the OpenGL to invert geometry vertically inside the viewport. Content at the top of the viewport for Direct3D will be rendered to the bottom of the viewport from the point of view of OpenGL, but will have a texture coordinate of zero in both cases. When operating in this mode, applications need not invert the programmed viewport rectangle as recommended for windowed rendering above. Applications happy with OpenGL's origin conventions but seeking potentially improved depth precision can configure clip controls using: glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); to avoid the loss of precision from the DepthRange transformation (which by default is z_window = z_ndc * 0.5 + 0.5). New Procedures and Functions void ClipControl(enum origin, enum depth); New Tokens Accepted by the parameter of ClipControl: LOWER_LEFT 0x8CA1 UPPER_LEFT 0x8CA2 Accepted by the parameter of ClipControl: NEGATIVE_ONE_TO_ONE 0x935E ZERO_TO_ONE 0x935F Accepted by the parameter of GetBooleanv, GetIntegerv, GetFloatv, and GetDoublev: CLIP_ORIGIN 0x935C CLIP_DEPTH_MODE 0x935D Additions to Chapter 13 of the OpenGL 4.4 (Compatibility Profile) Specification (Fixed-Function Vertex Post-Processing) -- Modify section 13.5 "Primitive Clipping" Insert before the 1st paragraph... "The command ClipControl(enum origin, enum depth); controls the clipping volume behavior. /origin/ must be either LOWER_LEFT or UPPER_LEFT, otherwise the error INVALID_ENUM is generated. /depth/ must be either NEGATIVE_ONE_TO_ONE or ZERO_TO_ONE, otherwise the error INVALID_ENUM is generated. These parameters update the clip control origin and depth mode respectively. The state required for clip control is one bit for clip control origin and one bit for clip control depth mode. The initial value of the clip control origin is LOWER_LEFT and the initial value of the depth mode is NEGATIVE_ONE_TO_ONE. The error INVALID_OPERATION is generated if ClipControl is executed between the execution of Begin and the corresponding execution of End." Replace the first paragraph with... "Primitives are clipped to the clip volume. In clip coordinates, the view volume is defined by -w_c <= x_c <= w_c -w_c <= y_c <= w_c zm <= z_c <= w_c where zm is -w_c when the clip control depth mode is NEGATIVE_ONE_TO_ONE or zero when the mode is ZERO_TO_ONE." Change the last sentence of the 7th paragraph to read... "If depth clamping is enabled, the zm <= z_c <= w_c plane equation (see the clip volume definition) is ignored by view volume clipping (effectively, there is no near or far plane clipping)." -- Modify section 13.6 "Coordinate Transformations" Replace the 3rd paragraph with (where ^T means transpose): "If a vertex in clip coordinates is given by (x_c y_c z_c w_c)^T then the vertex's normalized device coordinates are (x_d y_d z_d)^T = (x_c/w_c f*y_c/w_c z_c/w_c)^T where /f/ is +1 when the clip control origin is LOWER_LEFT and -1 when the origin is UPPER_LEFT." -- Modify section 13.6.1 "Controlling the Viewport" Replace the 2nd sentence of the 1st paragraph with (where ^T means transpose): "The vertex's window coordinates, (x_w y_w z_w)^T are given by: ( x_w ) ( p_x/2 x_d + o_x ) ( y_w ) = ( p_y/2 y_d + o_y ) ( z_w ) ( s z_d + b ) where s is (f-n)/2 and b is (n+f)/2 when the clip control depth mode is NEGATIVE_ONE_TO_ONE; or s is (f-n) and b is n when the mode is ZERO_TO_ONE." Additions to Chapter 14 of the OpenGL 4.4 (Compatibility Profile) Specification (Fixed-Function Primitive Assembly and Rasterization) -- Modify section 14.6.1 "Basic Polygon Rasterization" Replace the 3rd sentence of the 1st paragraph with: "One way to compute this area is n-1 ___ \ a = 1/2 f \ x^i_w * y^i(+)1_w - x^i(+)1_w * y^i_w / /__ where f is +1 when the clip control origin is LOWER_LEFT and -1 when the origin is UPPER_LEFT, x^i_w and y^i_w are the x and y window coordinates of the ith vertex of the n-vertex polygon (vertices are numbered starting at zero for purposes of this computation), and i(+)1 is (i+1) mod n." Additions to the AGL/GLX/WGL Specifications None GLX Protocol A new GL rendering command is added. The following command is sent to the server as part of a glXRender request: ClipControl 2 12 rendering command length 2 1340 rendering command opcode 4 ENUM origin 4 ENUM depth Errors The error INVALID_ENUM is generated by ClipControl if origin is not LOWER_LEFT or UPPER_LEFT. The error INVALID_ENUM is generated by ClipControl if depth is not NEGATIVE_ONE_TO_ONE or ZERO_TO_ONE. The error INVALID_OPERATION is generated if ClipControl is executed between the execution of Begin and the corresponding execution of End. New State Get Value Type Get Command Initial Value Description Sec Attribute ---------------- ---- ----------- ------------------- --------------- ---- --------- CLIP_ORIGIN Z2 GetIntegerv LOWER_LEFT Clip origin 13.5 xform CLIP_DEPTH_MODE Z2 GetIntegerv NEGATIVE_ONE_TO_ONE Clip depth mode 13.5 xform New Implementation Dependent State None Issues 1) What should this extension be called? RESOLVED: ARB_clip_control We frame this extension in terms of how the fixed-function transformation from clip coordinates to window coordinates is specified. The crucial modifications to OpenGL's existing behavior involve controlling how clip space is interpreted. An upper-left origin is really simply negating (flipping) the clip space Y coordinate. Subsequently the sense of counter-clockwise and clockwise for face culling must be adjusted (flipped). A zero-to-one Z mode involves adjusting the clipping equation for the clip space Z coordinate. Subsequently the depth range transform equation must be adjusted (scaled and biased). Hence clip control is a sensible name. 2) Should this functionality be exposed with glEnable/glDisable or with a new command. RESOLVED: With a new command, glClipControl. We note that this extension does not actually enable or disable functionality, but rather modifies an existing transformation. We note that Direct3D is different in two ways: clip Y inversion and zero-to-one clip Z. We note the difficulty of a clear enable name. GL_NEGATIVE_ONE_TO_ONE and GL_ZERO_TO_ONE are very explicit about how the Z clip coordinate is treated by the clip equations. Likewise, GL_LOWER_LEFT and GL_UPPER_LEFT are explicit (and match the token names and meaning for the point sprite functionality). We also note the possibility for other possible conventions. For example, an origin at the center of the window. Hence an enumeration of clip modes is a better choice. Likewise, a future Z mode could expose W-buffering. 3) Why does unextended OpenGL have symmetric clip equations? RESOLVED: This is a legacy of implementation worthy of some explanation... Handling the X, Y, and Z directions the same way better facilitates vector operation for hardware efficiency. When transformations are done with 32-bit IEEE-754 floating-point values, after transformation to clip space for clipping, the Z values are typically converted to fixed-point for use by a conventional 24-bit fixed-point depth buffer. The process of scaling by 0.5 and adding 0.5 to a 32-bit floating-point number in the range [-1,+1] has the effect of appropriately rounding the value so it can be efficiently bit-shifted into a 24-bit fixed-point value in the [0,1] range suitable for depth buffering via linear fixed-point interpolation. 4) Should the face culling behavior be modified in GL_UPPER_LEFT clip origin mode? RESOLVED: Yes. See how the modifications to section 14.6.1 "Basic Polygon Rasterization" negate the sign of the polygon area when the clip control origin is GL_UPPER_LEFT. Since culling behavior is specified as CW (clockwise) or CCW (counter-clockwise) different triangle faces would be culled when the clip origin is changed, which would be an unacceptable side effect. 5) Are the projective matrices generated by glFrustum and glOrtho appropriate when the clip Z mode is GL_ZERO_TO_ONE? RESOLVED: No. 6) Should the behavior of glFrustum and glOrtho change? RESOLVED: No. It's not worth encumbering these routines with adjustments, plus it is easy to make the proper adjustments... Taking advantage of how matrix changes concatenate... If your clip control origin is GL_UPPER_LEFT, prior to your glFrustum or glOrtho command by: glScalef(1,-1,1); If your clip control depth mode is GL_ZERO_TO_ONE, precede your glFrustum or glOrtho command by: glTranslatef(0,0,0.5); glScalef(1,1,0.5); For example, if your code to configure the projection matrix reads: glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-0.5, 0.5, -0.5, 0.5, 1, 3); And you wanted to call: glClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE); Then configure your projection matrix as glMatrixMode(GL_PROJECTION); glLoadIdentity(); // adjust for GL_UPPER_LEFT glScalef(1,-1,1); // adjust for GL_ZERO_TO_ONE glTranslatef(0,0,0.5); glScalef(1,1,0.5); glFrustum(-0.5, 0.5, -0.5, 0.5, 1, 3); Technically, the two glScalef could be combined as glScalef(1,-1,0.5); 7) Has this topic been discussed elsewhere? RESOLVED: Yes, see: "Maximizing Depth Buffer Range and Precision" Brano Kemen http://outerra.blogspot.co.uk/2012/11/maximizing-depth-buffer-range-and.html November 28, 2012 "Minimum Triangle Separation for Correct Z-Buffer" Kurt Akeley and Jonathan Su http://research.microsoft.com/apps/pubs/default.aspx?id=79213 August 2006 "Tightening the Precision of Perspective Rendering" Paul Upchurch and Mathieu Desbrun http://www.geometry.caltech.edu/pubs/UD12.pdf Journal of Graphics Tools, Volume 16, Issue 1, 2012. 8) How does this extension interact with the unclamped depth range parameters of NV_depth_buffer_float's glDepthRangedNV and OpenGL 4.3? RESOLVED: Simply apply the equations as specified. The implications of this are explored... An unclamped depth range applies to floating-point depth buffers. (For a conventional [0,1] fixed-point depth buffer, the depth range is clamped to "the range appropriate to the depth buffer's representation." In practice, this means that unclamped depth values are clamped to the [0,1] range when used with a conventional depth buffer so are effectively still clamped.) If an application were to mix the two like this: glDepthRangedNV(-1,1); glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); this would lead to generating interpolated depth values in a [-1,+1] range. Because floating-point has more precision in the neighborhood of zero, the depth buffer precision is concentrated at zero in window-space Z. This corresponds to 0.5 in normalized device coordinates. Consider if our projection matrix mapped eye-space 2 to clip-space 0.0 and eye-space 1000 to 1.0 with the Z matrix row: [ 0 0 (n+f)/(n-f) f*n/(n-f) ] so that Zc = -0.5 = 0*x_e + 0*y_e + ((2+1000)/(2-1000))*z_e + (2*1000/(2-1000))*w_e solving for z_e when w_e is one, means z_e equals -1.498 OpenGL 4.3 made a slightly incompatible change in the parameter types for glDepthRange (and related commands) from clamped floating-point types (GLclampd) to unclamped floating-point types (GLdouble). Hence the functionality of glDepthRangedNV also applies to OpenGL 4.3 in the case of floating-point depth buffers. 9) Can an application be guaranteed the exact same pixels being rasterized when the clip control origin is GL_UPPER_LEFT versus GL_LOWER_LEFT, except having the scanlines reversed? RESOLVED: No such rasterization invariance is reasonable to guarantee. Slight pixel variances are possible. The polygon rasterization rules for OpenGL (section 14.6.1, "Polygon Rasterization") states: In such a case [fragment's center lies on a polygon boundary edge] we require that if two polygons lie on either side of a common edge (with identical endpoints) on which a fragment center lies, then exactly one of the polygons results in the production of the fragment during rasterization." The specification leaves it to implementations to define the exact edge rule in this case. If the sense of Y in clip space is flipped, this rule may be decided differently. This is further complicated by multisampling where the sample pattern is unlikely to be mirrored in the Y direction. These issues are razor's edge cases and should not be an issue for real applications. 10) Are all the possible combinations of glClipControl useful? RESOLVED: Yes, the initial state (GL_LOWER_LEFT/ GL_NEGATIVE_ONE_TO_ONE) corresponds to OpenGL's traditional behavior. The state GL_LOWER_LEFT/GL_ZERO_TO_ONE corresponds to OpenGL's traditional origin and Direct3D's depth mode. The state GL_UPPER_LEFT/GL_ZERO_TO_ONE corresponds to Direct3D's clip volume definition. The state GL_UPPER_LEFT/GL_NEGATIVE_ONE_TO_ONE is consistent with the upper-left origin of the window coordinate system of Microsoft Windows and the X Window System. 11) Should all the possible combinations of glClipControl parameters supported be supported? RESOLVED: Yes, all the combinations should be supported. The cost is low and this provides orthogonality. So it is legal to call: glClipControl(GL_UPPER_LEFT, GL_NEGATIVE_ONE_TO_ONE); 12) Does setting the clip control origin to GL_UPPER_LEFT change the origin of the window coordinate system use for commands such as glViewport, glScissor, glWindowPos2i, and glReadPixels? RESOLVED: No. The (x,y) window space location passed to these commands have the (0,0) origin at the lower left corner of the window, independent of the state of the clip control origin. So, for example, an application wanting a Direct3D upper-left orign specifying the scissor with upper-left (x,y) coordinates would call: glScissor(upper_left_x, window_height - upper_left_y, window_width, window_height); The rationale for this choice is to avoid confusion for how window space coordinates are passed to commands. When rendering to resizable windows, the window width and height can change asychronously. This would mean the scissor command would need to specify a "gravity" for the window origin. There would also need to be a way to "query" this state relative to difference origin conventions and subject asynchronous window resizes. Moreover, this extension is not changing how window space is specified but rather how clip space is specified. 13) Does the polygon stipple orientation change when the clip control origin is set to GL_UPPER_LEFT? RESOLVED: No. 14) Will using the GL_ZERO_TO_ONE clip control depth mode improve my depth precision? RESOLVED: Yes, if you use a floating-point depth buffer and place the near and far values at 1.0 and 0.0 (reversed from the normal convention). But not much if you use a conventional fixed-point depth buffer. This really depends on whether the particular hardware implementation can numerically improve its depth interpolation result. This depends on a lot of things including the quality of the depth plane equation setup, the number of bits of sub-pixel precision available, and how the depth interpolation is performed. With a conventional 24-bit fixed-point depth buffer, applications may be able to achieve half a least significant bit (LSB) of improved depth buffer precision. There are typically far better strategies for improving depth precision such as W-buffering, avoiding concatenation of the modelview and projection matrices, and (probably the easiest) better managing your scene's near and far planes. With a floating-point depth buffer, it is more likely the GL_ZERO_TO_ONE depth mode will improve depth precision. Unfortunately in the conventional depth range [0,1], the improvement is primarily close to the near plane where there is already excessive precision. Reversing the depth range with the command glDepthRange(1.0, 0.0); effectively reverses the near and far values in the depth buffer. Also remember to reverse the depth function; so glDepthFunc(GL_LESS) should become glDepthFunc(GL_GREATER). However, this involves computing 1-Z which effectively truncates far values (for much the same reason adding +0.5 does). Alternatively, the reversal of near and far can happen as part of the projection transformation so the depth range specified with glDepthRange can be the conventional range [0,1]. Arguably folding the near and far reversal into the projection matrix is slightly better than reversing the depth range. However the problem remains that a very large value is added with a small value close to the far plane so effective depth precision is not substantially improved with a fixed-point depth buffer but has a substantial advantage for a floating-point depth buffer. 15) How does this extension interact with geometry shaders? RESOLVED: If there is a geometry shader active, it is the geometry shader which is actually writing the clip space position (not the vertex shader). One way to implement this extension (ignoring geometry shaders for now) would be to add an epilogue to the application's vertex shader to invert the clip space Y output when the clip control origin is GL_UPPER_LEFT and perform a scale by 2 and bias by negative clip space W to the clip space Z (biasing by negative clip space W is like subtracting 1 in normalized device coordinates). However, this epilogue would need to be moved to the geometry shader if a geometry shader was active. 16) How does this extension interact with tessellation evaluation shaders (without a geometry shader)? RESOLVED: Then the epilogue discussed in issue #15 would need to be added to the tessellation evaluation shader (and not the vertex shader since it is really transforming patch control points). If there was a geometry shader active, the geometry shader is where the epilogue would be done. 17) Does the discussion of the use an epilogue in the last two issues mean using the GL_UPPER_LEFT or GL_ZERO_TO_ONE modes is necessarily slower? RESOLVED: Generally no. GPUs that support Direct3D are expected to have a mode to support GL_UPPER_LEFT and GL_ZERO_TO_ONE at full speed and OpenGL implementations for such GPUs should operate at full speed. 18) Should the clip control state be changed frequently? RESOLVED: Most applications are expected to set the clip control conventions once; for example, to match the Direct3D conventions. Some implementations may introduce a flush when changing the clip control state. Hence frequent clip control changes are not recommended. No flush is explicitly required when the clip control changes and some implementations (NVIDIA) will have no significant performance penalty for changing the clip control state. 19) Issue #6 addresses fixed-function vertex processing adjustments under different clip control modes. How would a GLSL vertex shader be adjusted, assuming how the shader computes its clip-space position does not change, for different clip control conventions? RESOLVED: The following GLSL epilogues could be added... If the clip control origin is GL_UPPER_LEFT, you could add a final operation to negate the clip-space Y component. So: gl_Position.y *= -1; If the clip control depth mode is GL_ZERO_TO_ONE, you could scale and bias the conventional [-1,+1] range of normalized-device-coordinate-space Z to the [0,1] range like this: gl_Position.z = 0.5*gl_Positon.z + 0.5*gl_Position.w; Because z_ndc = z_c / w_c so the epilogue above computes an adjusted z_ndc': z_ndc' = (0.5*z_c + 0.5+w_c)/w_c which is the same as: z_ndc' = 0.5*z_c/w_c + 0.5 = 0.5*z_ndc + 0.5 and scaling a [-1,+1] range by 0.5 and biasing by 0.5 computes a linear mapping to the range [0,1]. Alternatively, rather than adding an epilogue as described, the adjustments above could be folded into the transform matrix typically used to transform object-space to clip-space. This is a faster approach since it avoids extra math operations in the vertex shaders. 20) Explain the numerical advantage for potential increased depth precision for the GL_ZERO_TO_ONE clip control depth mode. RESOLVED: If the normal depth range sets near to 0.0 and far to 1.0, this means the effective viewport transform for Z (see section 13.6.1 "Controlling the Viewport") becomes an identity mapping. So the mapping in GL_ZERO_TO_ONE depth mode is: z_w = (f-n) * z_d + n and when near (n) is 0.0 and far (f) is 1.0, this becomes simply: z_w = z_d; This identity mapping results in no lose or change in the per-vertex window space position. Contrast this with the same situation with the GL_NEGATIVE_ONE_TO_ONE depth mode with the mapping: z_w = (f-n)/2 * z_d + (n+f)/2 and when near (n) is 0.0 and far (f) is 1.0, this becomes simply: z_w = 0.5 * z_d + 0.5 While multiplying z_d by 0.5 has no precision loss (ignoring denorms, this scale simply decrements the exponent by 1 without disturbing the mantissa). However a bias by 0.5 does distrurb the floating-point precision (also see Issue #3), losing one least-significant-bit (LSB) of precision. 21) What should the state to control the GL_NEGATIVE_ONE_TO_ONE and GL_ZERO_TO_ONE convention be called? RESOLVED: GL_CLIP_DEPTH_MODE. An alternative would be GL_CLIP_Z_MODE but the convention is well-established in the OpenGL API that Z values that are interpreted as depth values are described as DEPTH. 22) Direct3D 8 and 9 have a window-space coordinate system where pixel centers are centered on integer coordinates. OpenGL (and Direct3D 10 and 11) position pixel centers at half-pixel locations. Should ARB_clip_control account for the older Direct3D integer pixel center convention? RESOLVED: No, because existing functionality covers this... The EXT_viewport_array extension (made a core part of OpenGL with version 4.1) extends the viewport state to be specified as floating-point (technically fixed-point) values. Prior to this extension, the viewport parameters were integral. With the EXT_viewport_array functionality, you can add a bias of (0.5,0.5) to the viewport (x,y) to shift integer Direct3D 8/9 style window space locations to OpenGL's half-pixel convention. If you had a Direct3D upper-left viewport (x,y), you'd match the Direct3D clip-space AND window-space conventions like this: glClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE); const GLuint viewport_index = 0; // Prior to DirectX 10, there's only a single viewport glViewportIndexedf(viewport_index, x + 0.5f, window_height - (y + view_height) + 0.5f, view_width, view_height); where window_height is the height of the window/surface in pixels. Also note the EXT_fragment_coord_conventions (made a core part of OpenGL with version 3.2) allows the window position varying input to fragment shaders to reflect the Direct3D 9 window-space convention. See that extension for details. 23) Does this extension's state affect user clip planes? RESOLVED: No, user clip planes operate on eye-space coordinates which are not affected by this extension's state. 24) Does this extension's state affect fixed-function fog? RESOLVED: No, fixed-function fog operates on eye-space distance which is not affected by this extension's state. 25) Does this extension's state affect the point sprite origin? RESOLVED: No. When the EXT_point_sprite functionality was promoted to a core feature in OpenGL 2.0, the GL_POINT_SPRITE_ORIGIN state was added to specify whether the 2D texture coordinate has a lower-left (OpenGL's convention in EXT_point_sprite and NV_point_sprite) or upper-left (Direct3D convention). An application wanting to emulate Direct3D's upper-left point sprite texture coordinate origin should change the origin state with an explicit point parameter command. Specifically: glPointParameteri(GL_POINT_SPRITE_ORIGIN, GL_UPPER_LEFT); This command is in addition to calling glClipControl and specifying a GL_UPPER_LEFT origin. 26) Issue #12 says the origin for glWindowPos2i and other glWindowPos* commands is lower-left independent of the clip control state. What about glRasterPos2i and other glRasterPos* commands? RESOLVED: Yes, the raster position specified by glRasterPos2i, etc. is affected by the clip control state because the object-space position is transformed and clipped just as a point vertex position would be to arrive at the clip-space raster position that is further transformed to window space. 27) Is the depth value specified for glClearDepth affected by this extension's clip control state? RESOLVED: No, glClearDepth takes a window-space depth value. This extension only affects how clip space clipping and the transformation from clip space to window space operates. Likewise depth values read (glReadPixels), drawn (glDrawPixels), or copied (glCopyPixels, glBlitFramebuffer) are unaffected by this extension's clip control state. 28) Does this extension's state affect the operation of polygon offset? RESOLVED: No, polygon offset operates on window-space depth values. This extension's clip control state operates prior to polygon offset. 29) Can glClipControl be display listed? RESOLVED: Yes. 30) Should the command be glClipParameteri to anticipate more control of clipping state? RESOLVED: No. Given over 20 years with basically zero extensions in the area of clipping state, we realistically don't anticipate more clipping parameters. Even in the case of this extension, the rationale for this extension is quite limited and not about adding any "features" to clipping. Instead the purpose is simply to match the conventions of Direct3D (not a functional change). While Direct3D's convention is different from OpenGL, there simply aren't any futher 3D APIs or standards which clip differently. Also glClipControl maintains consistency with the existing glClipPlane command pattern for the clipping API. Historically "Parameter" has been used for commands that affect managed "object" state and/or have a speciailized "Get" query command (glGetColorTableParameter*) rather than fixed-function pipeline state. This isn't completely uniform (exceptions: point parameters, patch parameters) but the vast majority of fixed-function rendering state isn't set with gl*Parameter style commands. When "Parameter" commands are used there is typically a plurality of state settings with different integer/float/double/boolean types. None of the situations that justify gl*Parameter style commands are present in this extension. 31) This extension is only useful if it is widely available. So how easy would this extension be to implement? RESOLVED: No special hardware is required due to the careful way this extension is specified. The clip control functionality could all be done by inexpensive epilogue math appended to the last shader in the graphics pipeline. The Y inversion could be performed as part of the viewport transform. Given the prevalance of Direct3D-capable hardware, we expect some hardware vendors will implement this extension with special existing modes in their hardware to handle the Direct3D conventions. However we emphasize no special hardware is required and the performance benefit attributable to such hardware is likely to be extremely meager. With respect to Mesa3D and Gallium, Brian Paul observes: "This extension should be pretty easy to implement in Mesa/gallium. In Gallium we already have a state variable for Z 0/1 vs. -1/+1 clipping. And I think we could implement the Y origin via our viewport state (which is specified in floats)." 32) Is support for the GL_UPPER_LEFT convention just to match Direct3D/Windows? RESOLVED: No. Along with Windows/Direct3D/Direct2D, X11 and Java also have upper-left graphics device coordinate systems. OpenGL, PostScript, PHIGS, GKS, and Apple's Core Graphics (Quartz 2D) have lower-left graphics device coordinate systems. 33) When rendering to off-screen framebuffer objects (FBOs) that will subsequently be textured from, how do I make sure the texture coordinate origin and the window space origin are consistent? RESOLVED: Use GL_LOWER_LEFT for the origin parameter of glClipControl in this case. Use the GL_UPPER_LEFT for the origin parameter when matching Direct3D's origin *and* drawing into a window framebuffer to be directly presented. 34) Can you provide an example illustrating how applications using the coordinate system conventions of Direct3D map onto this extension? RESOLVED: Consider a Direct3D application rendering a triangle ABC (with counter-clockwise orientation) to a viewport in the upper-left quadrant of the destination surface. The surface (with Direct3D window coordinates) is illustrated here. (0,0) (960,0) (1920,0) +------(A)-------+----------------+ |^ Y_c == +W_c ^ | | | | | | | | | | | |v Y_c == -W_c v | | +-(B)--------(C)-+----------------+ | | | | | | | | | | | | | | | +----------------+----------------+ (0,1080) (1920,1080) In this example, assume vertices A, B, and C have clip coordinates of: A = (+0.0, +1.0, +0.5, +1.0) B = (-0.8, -1.0, +0.5, +1.0) C = (+0.8, -1.0, +0.5, +1.0) Direct3D's coordinate transformations are functionally similar to OpenGL's, except that (a) the Y coordinate is inverted as part of the viewport transformation mapping normalized device coordinates (NDCs) to window coordinates and (b) the depth range transformation maps Z==0 to the near value instead of halfway between near and far as in OpenGL: http://msdn.microsoft.com/en-us/library/windows/desktop/ bb206341%28v=vs.85%29.aspx Because of the Y inversion from (a), vertices in Direct3D with a Y NDC of -1.0 map to the bottom of the viewport (larger Y window coordinates in the Direct3D coordinate system). This is exactly like OpenGL windowed rendering, where a Y NDC of -1 maps to smaller Y window coordinates (bottom) in the OpenGL coordinate system. Thanks to this inversion in the Direct3D viewport transformation, rendering a Direct3D scene with the same coordinates and matrices in OpenGL will produce an image with identical vertical orientation and winding (CW/CCW). However, since the viewport rectangle itself is programmed in window coordinates, a Direct3D-centric viewport of (0,0,960,540) needs to be flipped to (0,540,960,540) to work in OpenGL. Additionally, to get identical near/far clipping and Z values, it's necessary to use the ZERO_TO_ONE mode in this extension to have OpenGL process Z coordinates identically to Direct3D. When rendering to off-screen surfaces later used as textures, the issue is a little bit more complex. A Direct3D application will use the texture coordinates (0,0) to refer to the upper left corner of the upper leftmost pixel of the image. However, in OpenGL, texture coordinates of (0,0) refer to the lower-left corner of the lower-leftmost pixel of the image. One way to compensate for this is to remap the texture coordinate with: t_OpenGL = 1.0 - t_Direct3D Unfortunately, that requires a modification to shaders or other input data in the application. Instead of doing this, the UPPER_LEFT mode in this extension provides a simple way to use Direct3D texture coordinate conventions -- by rendering the entire scene *upside-down* from the point of view of OpenGL. The image we want to produce using this technique (below) is a vertically inverted version of the previous image, where OpenGL lower-left window coordinates are depicted in the figure. (0,1080) (1920,1080) +----------------+----------------+ | | | | | | | | | | | | | | | +-(B)--------(C)-+----------------+ |v Y_c == -W_c v | | | | | | | | | | | |^ Y_c == +W_c ^ | | +------(A)-------+----------------+ (0,0) (960,0) (1920,0) In this example, the UPPER_LEFT mode in this extension inverts the geometry as part of the transformation from clip coordinates to NDCs, so that vertex A has a Y NDC of -1.0 instead of +1.0. This puts A at the bottom of the viewport, while B and C remain at the top. One thing to note is that the inversion changes the orientation of triangle ABC, which is now clockwise instead of counter-clockwise. To compensate, this extension also inverts the value computed to compute face direction when in UPPER_LEFT mode. The one other thing to note here is that when rendering this way, the Direct3D viewport should be used as-is in OpenGL. Note that a similar inversion technique can be used to implement OpenGL FBO rendering on graphics hardware supporting only Direct3D coordinate systems. If this technique is used on an implementation doing something like this, the two inversions cancel each other out. 35) Does this extension affect the primitive's winding order in tessellation evaluation shader when origin is changed to GL_UPPER_LEFT? RESOLVED: No, the winding order is not affected. If a change in winding order of the primitive is needed, it must be done from the tessellation shader explicitly. Revision History Rev. Date Author Changes ---- -------- --------- ---------------------------------------------- 3 04/26/13 mjk Add issues 15 to 21 Change CLIP_Z_MODE to CLIP_DEPTH_MODE 4 04/30/13 mjk Add issue 22 5 05/01/13 mjk Add issues 22 to 28, fix typos 6 05/09/13 mjk Add issue 29 to 31 7 05/13/13 pdaniell Internal revisions 8 05/13/13 mjk Valve feedback; change to KHR in issues 9 05/13/13 mjk Change to KHR in issues 10 05/28/13 pdaniell Fold in feedback from Mark Callow in bug 10245 11 05/30/13 pdaniell Internal revisions 12 06/06/13 pdaniell Internal revisions 13 06/17/13 pdaniell Fix the enum token values 14 07/03/13 mjk D3D off-screen discussion; issue 32 and 33 15 04/16/14 pdaniell Prepare spec for OpenGL 4.5 16 04/17/14 pdaniell Fixes "UPPER_RIGHT" typos to UPPER_LEFT 17 07/30/14 pbrown Fix incorrect language in the overview describing when to use UPPER_LEFT and LOWER_LEFT modes; add detailed examples in issue 34. 18 09/17/15 Jon Leech Correct typo in issue 7 and add contributor Brano Kemen from that issue. 19 04/06/18 Vikram Add issue 35