Per-Sample Processing: Difference between revisions

From OpenGL Wiki
Jump to navigation Jump to search
m (Alfonse moved page Per-Sample Operations to Per-Sample Processing without leaving a redirect: Wrong name.)
 
(17 intermediate revisions by 2 users not shown)
Line 1: Line 1:
'''Per-Sample Processing''' is a stage of the [[Rendering Pipeline Overview|OpenGL rendering pipeline]], where fragment data output from a [[Fragment Shader]] is processed and written to various buffers. There are a number of operations that are performed in the order below.
{{pipeline float}}
'''Per-Sample Processing''' is a stage of the [[Rendering Pipeline Overview|OpenGL rendering pipeline]], where [[Fragment]]s output from a [[Fragment Shader]] are processed, and their resulting data are written to various buffers.
 
== Early operations ==
 
The specification states that the per-sample processing operations take place after the [[Fragment Shader]] has executed. However, it is possible for these to be performed [[Early Fragment Test|before the fragment shader]]. The following operations are able to be performed before the fragment shader, under certain circumstances (and in the following order):
 
* [[#Pixel ownership test|The pixel ownership test]]
* [[#Scissor test|The scissor test]]
* [[#Stencil test|The stencil test]]
* [[#Depth test|The depth test]]
* [[#Occlusion query updating|Occlusion query updating]]
 
There are two circumstances when these operations can be performed before the fragment shader. It can happen as an implicit optimization, if OpenGL detects that the [[Early Fragment Test#Limitations|fragment shader's execution will not interfere with these tests]]. Or it can happen if the [[Force Early Test|fragment shader explicitly requests it]].
 
If it happens by explicit request of the fragment shader, then all of these operations will happen first. Also, all '''writes''' to these buffers will happen first. Therefore, operations done in the fragment shader that affect such writes will be effectively ignored. For example, executing a {{code|discard}} in an early-test fragment shader will not stop the depth or stencil values from being ''updated'' (the update is considered part of the test, [[#Write mask|write masks notwithstanding]]), nor will it stop the occlusion query counter from being bumped.
 
{{require|4.2|shader_image_load_store}} explicitly requires the pixel ownership test and scissor test to always be performed early, no matter what. Granted, earlier hardware probably did them early anyway, but without [[Image Load Store]] functionality, you would never be able to tell the difference (outside of improved performance).
 
All operations are processed in the following order, for each fragment generated by the fragment shader:


== Pixel ownership test ==
== Pixel ownership test ==
{{snippet|:Per-Sample Processing/Pixel ownership test}}


== Scissor test ==
== Scissor test ==
{{main|Scissor Test}}
A rectangular area of the destination framebuffer can be designated as the only valid area for rendering to. All fragments aimed at pixels outside of this rectangle will be discarded at this stage.


== Multisample operations ==
== Multisample operations ==
{{main|Multisampling}}


== Stencil test ==
== Stencil test ==
{{main|Stencil Test}}
The stencil test, when enabled, can cause a fragment to be discarded based on a bitwise operation between the fragment's stencil value and the stencil value stored in the current [[Stencil Buffer]] at that fragment's sample position. This allows the user to lay down stencil values in one rendering pass, then conditionally cull fragments based on this pattern.


== Depth test ==
== Depth test ==
{{main|Depth Test}}
The depth test, when enabled, allows a fragment to be culled based on a conditional test between the fragment's depth value and the depth value stored in the current [[Depth Buffer]] at that fragment's sample position. This is useful to cause geometry to be hidden behind other geometry. The closer geometry lays down depth values that mask the rendering of any fragments behind them, using the proper depth test condition.


== Occlusion query updating ==
== Occlusion query updating ==
{{main|Occlusion Query}}


Fragments at this point are considered to have passed any conditional tests. Thus, their outputs will be written to one or more buffers ([[Write Mask]]s not withstanding). As such, this is the point where the fragment is counted for [[Occlusion Query]] counters. If an occlusion query of type {{enum|GL_SAMPLES_PASSED}} is active, then the query will be incremented every time a fragment hits this stage.
If the fragment passed the depth test (and only passing the depth test is checked), then at this point, an active occlusion query will be considered to have had a fragment pass. Therefore, if the query is a {{enum|GL_SAMPLES_PASSED}} query, the counter will be incremented. If it is one of the boolean queries, the boolean value will be set to true.
 
Note that reaching this point does not guarantee that any particular value will actually be written to any particular buffer. The [[#Write masks|write masks below]] can turn off writing to particular buffers. The [[Framebuffer Draw Buffer|framebuffer's draw buffer]] settings can effectively ignore one or more fragment values. Indeed, a [[Empty FBO|framebuffer object may be empty entirely]], with no images attached at all.


== Blending ==
== Blending ==
{{main|Blending}}
{{main|Blending}}
Each of the colors in the fragment can be combined with the corresponding pixel color in the buffer that the fragment will be written to. The result of this blending operation is what will be written.
If the image being read from is in the sRGB colorspace ''and'' [[Framebuffer Colorspace|{{enum|GL_FRAMEBUFFER_SRGB}} is currently enabled]], then the color read for the blending operation will be converted to linear RGB before blending with the fragment color.


== sRGB conversion ==
== sRGB conversion ==
If [[Framebuffer Colorspace|{{enum|GL_FRAMEBUFFER_SRGB}} is currently enabled]], then for each fragment color that is being written to an image with an [[SRGB Image Format|sRGB colorspace image format]], the RGB components of the color are converted from linear to sRGB.


== Dithering ==
== Dithering ==
When a color is being written to a framebuffer image, the image may have less precision than the incoming value. This only matters when writing floating-point values to [[Normalized Integer]] [[Image Format]]s. For a given image format precision and a given color value, there will be at most two colors that could be used for that color: the one from rounding up and one from rounding down.
When dithering is disabled, which color is chosen is implementation-defined, but it is not allowed to depend on the fragment's position in the window. For low-precision image formats, a subtle gradation in floating-point values is rendered as several sharp and distinct bands.
[https://en.wikipedia.org/wiki/Dither Dithering] can be used to avoid this banding. Dithering algorithms essentially fake a smooth gradient by varying which color is selected based on the position of the fragment. Therefore, if the color value is half-way between the two representable colors, then half of the pixels will be one color and half the other.
Dithering is enabled by enabling {{enum|GL_DITHER}}. Dithering is enabled by default.


== Logic operations ==
== Logic operations ==
{{main|Logical Operation}}
{{main|Logical Operation}}
Fragment colors can be conditionally combined with the corresponding value in the framebuffer by performing boolean operations on them. This overrides [[Blending]], and it only works for colors that are writing to integer ([[Normalized Integer|normalized]] or not) [[Image Format]]s that are not using sRGB writing to sRGB images.


== Write mask ==
== Write mask ==
{{main|Write Mask}}
{{main|Write Mask}}


 
Writes to particular buffers can be masked off. This also allows masking off of particular components of those writes, such that you can only update the red color component if you want. Each output buffer can have its own mask.
{{stub}}


[[Category:General OpenGL]]
[[Category:General OpenGL]]
[[Category:Sample Writing]]

Latest revision as of 16:52, 3 April 2019

Per-Sample Processing is a stage of the OpenGL rendering pipeline, where Fragments output from a Fragment Shader are processed, and their resulting data are written to various buffers.

Early operations

The specification states that the per-sample processing operations take place after the Fragment Shader has executed. However, it is possible for these to be performed before the fragment shader. The following operations are able to be performed before the fragment shader, under certain circumstances (and in the following order):

There are two circumstances when these operations can be performed before the fragment shader. It can happen as an implicit optimization, if OpenGL detects that the fragment shader's execution will not interfere with these tests. Or it can happen if the fragment shader explicitly requests it.

If it happens by explicit request of the fragment shader, then all of these operations will happen first. Also, all writes to these buffers will happen first. Therefore, operations done in the fragment shader that affect such writes will be effectively ignored. For example, executing a discard in an early-test fragment shader will not stop the depth or stencil values from being updated (the update is considered part of the test, write masks notwithstanding), nor will it stop the occlusion query counter from being bumped.

OpenGL 4.2 or ARB_shader_image_load_store explicitly requires the pixel ownership test and scissor test to always be performed early, no matter what. Granted, earlier hardware probably did them early anyway, but without Image Load Store functionality, you would never be able to tell the difference (outside of improved performance).

All operations are processed in the following order, for each fragment generated by the fragment shader:

Pixel ownership test

V · E

Because the Default Framebuffer is owned by a resource external to OpenGL, it is possible that particular pixels of the default framebuffer are not owned by OpenGL. And therefore, OpenGL cannot write to those pixels. Fragments aimed at such pixels are therefore discarded at this stage of the pipeline.

Generally speaking, if the window you are rendering to is partially obscured by another window, the pixels covered by the other window are no longer owned by OpenGL and thus fail the ownership test. Any fragments that cover those pixels will be discarded. This also includes framebuffer clearing operations.

Note that this test only affects rendering to the default framebuffer. When rendering to a Framebuffer Object, all fragments pass this test.

Scissor test

A rectangular area of the destination framebuffer can be designated as the only valid area for rendering to. All fragments aimed at pixels outside of this rectangle will be discarded at this stage.

Multisample operations

Stencil test

The stencil test, when enabled, can cause a fragment to be discarded based on a bitwise operation between the fragment's stencil value and the stencil value stored in the current Stencil Buffer at that fragment's sample position. This allows the user to lay down stencil values in one rendering pass, then conditionally cull fragments based on this pattern.

Depth test

The depth test, when enabled, allows a fragment to be culled based on a conditional test between the fragment's depth value and the depth value stored in the current Depth Buffer at that fragment's sample position. This is useful to cause geometry to be hidden behind other geometry. The closer geometry lays down depth values that mask the rendering of any fragments behind them, using the proper depth test condition.

Occlusion query updating

If the fragment passed the depth test (and only passing the depth test is checked), then at this point, an active occlusion query will be considered to have had a fragment pass. Therefore, if the query is a GL_SAMPLES_PASSED query, the counter will be incremented. If it is one of the boolean queries, the boolean value will be set to true.

Note that reaching this point does not guarantee that any particular value will actually be written to any particular buffer. The write masks below can turn off writing to particular buffers. The framebuffer's draw buffer settings can effectively ignore one or more fragment values. Indeed, a framebuffer object may be empty entirely, with no images attached at all.

Blending

Each of the colors in the fragment can be combined with the corresponding pixel color in the buffer that the fragment will be written to. The result of this blending operation is what will be written.

If the image being read from is in the sRGB colorspace and GL_FRAMEBUFFER_SRGB is currently enabled, then the color read for the blending operation will be converted to linear RGB before blending with the fragment color.

sRGB conversion

If GL_FRAMEBUFFER_SRGB is currently enabled, then for each fragment color that is being written to an image with an sRGB colorspace image format, the RGB components of the color are converted from linear to sRGB.

Dithering

When a color is being written to a framebuffer image, the image may have less precision than the incoming value. This only matters when writing floating-point values to Normalized Integer Image Formats. For a given image format precision and a given color value, there will be at most two colors that could be used for that color: the one from rounding up and one from rounding down.

When dithering is disabled, which color is chosen is implementation-defined, but it is not allowed to depend on the fragment's position in the window. For low-precision image formats, a subtle gradation in floating-point values is rendered as several sharp and distinct bands.

Dithering can be used to avoid this banding. Dithering algorithms essentially fake a smooth gradient by varying which color is selected based on the position of the fragment. Therefore, if the color value is half-way between the two representable colors, then half of the pixels will be one color and half the other.

Dithering is enabled by enabling GL_DITHER. Dithering is enabled by default.

Logic operations

Fragment colors can be conditionally combined with the corresponding value in the framebuffer by performing boolean operations on them. This overrides Blending, and it only works for colors that are writing to integer (normalized or not) Image Formats that are not using sRGB writing to sRGB images.

Write mask

Writes to particular buffers can be masked off. This also allows masking off of particular components of those writes, such that you can only update the red color component if you want. Each output buffer can have its own mask.