Vulkan-Docs/chapters/VK_EXT_blend_operation_adva.../advanced_blend.txt

806 lines
39 KiB
Plaintext
Raw Normal View History

Change log for June 24, 2017 Vulkan 1.0.53 spec update: * Bump API patch number and header version number to 53 for this update. Github Issues: Internal Issues: * Clarify mappings of coordinates for mutable, compatible image views in slink:VkImageViewCreateInfo (internal issue 815). * Make ename:VK_BIND_SFR_BIT require a logical device with multiple physical devices, so that standard sparse image block dimensions are only required on systems that support multi-GPU (internal issue 835). * Convert all files from use of // refBegin .. // refEnd comments to delimit ref pages, to use of open blocks, and update style guide accordingly (internal issue 839). * Add valid usage for slink:VkWriteDescriptorSet when performing updates to a ename:VK_STORAGE_IMAGE descriptor with layout ename:VK_IMAGE_LAYOUT_GENERAL. * Add a hack to the validity generator script to support an odd interaction between flink:vkCmdFillBuffer and an extension (internal issue 853). * Remove redundant text describing slink:VkBufferCreateInfo::pname:usage, which was already covered by implicit valid usage (internal issue 854). * Update implicit validity generator script to properly handle the pname:sType and pname:pNext members of "returnedonly" structures (internal issue 874). * Note that slink:VkApplicationInfo::pname:pApplicationName & slink:VkApplicationInfo::pname:pEngineName are optional, and add missing implicit valid usage statements for flink:vkDestroyInstance. * Added missing valid usage for flink:vkCmdWriteTimestamp to require a timestamp query pool. * Simplify and/or split "`non-atomic`" valid usage statements. New Extensions: * `VK_AMD_gpu_shader_int16` * `VK_EXT_blend_operation_advanced` * `VK_EXT_sampler_filter_minmax` * `VK_NV_framebuffer_mixed_samples` ----------------------------------------------------- Note: the 1.0.52 spec wasn't published on github, so the 1.0.53 release combines both change sets. ----------------------------------------------------- Change log for June 13, 2017 Vulkan 1.0.52 spec update: * Bump API patch number and header version number to 52 for this update. Github Issues: Internal Issues: * Clarify behavior when non-coherent memory has <<memory-device-unmap-does-not-flush, not been flushed before being unmapped>> (internal issue 819). * Fix description of code:WorkgroupSize builtin to note it decorates an object, not a variable (internal issue 836). * Fix asciidoc attributes so that trailing '{plus}' symbols in [eq] style equations are rendered properly (internal issue 845). * Add language to the "`Extension Handles, Objects, Enums, and Typedefs`" section of the Procedures and Conventions document stating that any new handle type requires a corresponding entry in the elink:VkObjectType enumerated type (internal issue 856). * Update style guide to use slink macro for Vulkan handle type names, and define narrow conditions under which to use the *name and *text macros instead of *link (internal issue 886). * Add a dependency of the <<VK_KHX_device_group,VK_KHX_device_group>> extension on VK_KHX_device_group_creation to +vk.xml+ and the extension appendix. * Change the copyright on Vulkan specification asciidoc *source* files to CC-BY 4.0, and update the proprietary Khronos copyright applied to the generated *output* formats (internal issue 327). This enables broader re-use and modification of the Vulkan specification sources, while not affecting the Reciprocal IP License between Vulkan Adopters and Working Group Members. New Extensions: * `VK_NV_fill_rectangle` * `VK_NV_fragment_coverage_to_color`
2017-06-27 02:32:10 +00:00
[[framebuffer-blend-advanced]]
=== Advanced Blend Operations
The _advanced blend operations_ are those listed in tables
<<framebuffer-blend-advanced-fxyz-modes,f/X/Y/Z Advanced Blend Operations>>,
<<framebuffer-blend-advanced-hsl-modes,Hue-Saturation-Luminosity Advanced
Blend Operations>>, and
<<framebuffer-blend-advanced-additional-rgb,Additional RGB Blend
Operations>>.
[open,refpage='VkPipelineColorBlendAdvancedStateCreateInfoEXT',desc='Structure specifying parameters that affect advanced blend operations',type='structs']
--
Change log for August 14, 2017 Vulkan 1.0.58 spec update: * Bump API patch number and header version number to 58 for this update. Github Issues: * Update the <<interfaces-resources-descset,Descriptor Set Interface>> section to allow multiple variables with the same descriptor set/binding decorations, and require that all variables that are statically used must be consistent with the pipeline layout. Allow ename:VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER to be used with texture and sampler variables (public issues 522, 524). Internal Issues: * Replace networkx package used for extension dependency generation with a homegrown network dependency traverser (internal issue 713). * Specify in the <<interfaces-fragmentoutput, Fragment Output Interface>> section that if a fragment shader writes integers that cannot be represented in the format of the colour attachment, then the result is undefined (internal issue 893). * Separate malformed valid usage statement for slink:VkPipelineRasterizationStateCreateInfo into two (internal issue 918). * Fix cases where the term 'pNext chain' is incorrectly used in reference to functions, rather than their parameters. Replace 'pNext list' with 'pNext chain'. Fixed typo in the example code of +VK_KHR_dedicated_allocation+ (internal issue 944). * Fix typo in elink:VkExternalSemaphoreHandleTypeFlagBitsKHR enum descriptions, replacing etext:VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHR with ename:VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR, and make the description more consistent with VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR (internal issue 950). Other Issues: * Clarify how pipeline stage masks affect <<synchronization-pipeline-stages-masks, access and synchronization scopes>>. * Clarify that dedicated allocations do not allow aliasing in the flink:vkBindBufferMemory and flink:vkBindImageMemory valid usage statements. * Correct specification of pname:dynamicCount for push_constant token in slink:VkIndirectCommandsLayoutNVX. New Extensions: * `VK_EXT_shader_viewport_index_layer`
2017-08-14 08:58:07 +00:00
If the pname:pNext chain of slink:VkPipelineColorBlendStateCreateInfo
Change log for June 24, 2017 Vulkan 1.0.53 spec update: * Bump API patch number and header version number to 53 for this update. Github Issues: Internal Issues: * Clarify mappings of coordinates for mutable, compatible image views in slink:VkImageViewCreateInfo (internal issue 815). * Make ename:VK_BIND_SFR_BIT require a logical device with multiple physical devices, so that standard sparse image block dimensions are only required on systems that support multi-GPU (internal issue 835). * Convert all files from use of // refBegin .. // refEnd comments to delimit ref pages, to use of open blocks, and update style guide accordingly (internal issue 839). * Add valid usage for slink:VkWriteDescriptorSet when performing updates to a ename:VK_STORAGE_IMAGE descriptor with layout ename:VK_IMAGE_LAYOUT_GENERAL. * Add a hack to the validity generator script to support an odd interaction between flink:vkCmdFillBuffer and an extension (internal issue 853). * Remove redundant text describing slink:VkBufferCreateInfo::pname:usage, which was already covered by implicit valid usage (internal issue 854). * Update implicit validity generator script to properly handle the pname:sType and pname:pNext members of "returnedonly" structures (internal issue 874). * Note that slink:VkApplicationInfo::pname:pApplicationName & slink:VkApplicationInfo::pname:pEngineName are optional, and add missing implicit valid usage statements for flink:vkDestroyInstance. * Added missing valid usage for flink:vkCmdWriteTimestamp to require a timestamp query pool. * Simplify and/or split "`non-atomic`" valid usage statements. New Extensions: * `VK_AMD_gpu_shader_int16` * `VK_EXT_blend_operation_advanced` * `VK_EXT_sampler_filter_minmax` * `VK_NV_framebuffer_mixed_samples` ----------------------------------------------------- Note: the 1.0.52 spec wasn't published on github, so the 1.0.53 release combines both change sets. ----------------------------------------------------- Change log for June 13, 2017 Vulkan 1.0.52 spec update: * Bump API patch number and header version number to 52 for this update. Github Issues: Internal Issues: * Clarify behavior when non-coherent memory has <<memory-device-unmap-does-not-flush, not been flushed before being unmapped>> (internal issue 819). * Fix description of code:WorkgroupSize builtin to note it decorates an object, not a variable (internal issue 836). * Fix asciidoc attributes so that trailing '{plus}' symbols in [eq] style equations are rendered properly (internal issue 845). * Add language to the "`Extension Handles, Objects, Enums, and Typedefs`" section of the Procedures and Conventions document stating that any new handle type requires a corresponding entry in the elink:VkObjectType enumerated type (internal issue 856). * Update style guide to use slink macro for Vulkan handle type names, and define narrow conditions under which to use the *name and *text macros instead of *link (internal issue 886). * Add a dependency of the <<VK_KHX_device_group,VK_KHX_device_group>> extension on VK_KHX_device_group_creation to +vk.xml+ and the extension appendix. * Change the copyright on Vulkan specification asciidoc *source* files to CC-BY 4.0, and update the proprietary Khronos copyright applied to the generated *output* formats (internal issue 327). This enables broader re-use and modification of the Vulkan specification sources, while not affecting the Reciprocal IP License between Vulkan Adopters and Working Group Members. New Extensions: * `VK_NV_fill_rectangle` * `VK_NV_fragment_coverage_to_color`
2017-06-27 02:32:10 +00:00
includes a sname:VkPipelineColorBlendAdvancedStateCreateInfoEXT structure,
then that structure includes parameters that affect advanced blend
operations.
The sname:VkPipelineColorBlendAdvancedStateCreateInfoEXT structure is
defined as:
include::../../api/structs/VkPipelineColorBlendAdvancedStateCreateInfoEXT.txt[]
* pname:sType is the type of this structure.
* pname:pNext is `NULL` or a pointer to an extension-specific structure.
* pname:srcPremultiplied specifies whether the source color of the blend
operation is treated as premultiplied.
* pname:dstPremultiplied specifies whether the destination color of the
blend operation is treated as premultiplied.
* pname:blendOverlap is a elink:VkBlendOverlapEXT value specifying how the
source and destination sample's coverage is correlated.
If this structure is not present, pname:srcPremultiplied and
pname:dstPremultiplied are both considered to be ename:VK_TRUE, and
pname:blendOverlap is considered to be
ename:VK_BLEND_OVERLAP_UNCORRELATED_EXT.
.Valid Usage
****
* [[VUID-VkPipelineColorBlendAdvancedStateCreateInfoEXT-srcPremultiplied-01424]]
If the
<<features-limits-advancedBlendNonPremultipliedSrcColor,non-premultiplied
source color>> property is not supported, pname:srcPremultiplied must:
be ename:VK_TRUE
* [[VUID-VkPipelineColorBlendAdvancedStateCreateInfoEXT-dstPremultiplied-01425]]
If the
<<features-limits-advancedBlendNonPremultipliedDstColor,non-premultiplied
destination color>> property is not supported, pname:dstPremultiplied
must: be ename:VK_TRUE
* [[VUID-VkPipelineColorBlendAdvancedStateCreateInfoEXT-blendOverlap-01426]]
If the <<features-limits-advancedBlendCorrelatedOverlap,correlated
overlap>> property is not supported, pname:blendOverlap must: be
ename:VK_BLEND_OVERLAP_UNCORRELATED_EXT
****
include::../../validity/structs/VkPipelineColorBlendAdvancedStateCreateInfoEXT.txt[]
--
When using one of the operations in table
<<framebuffer-blend-advanced-fxyz-modes,f/X/Y/Z Advanced Blend Operations>>
or <<framebuffer-blend-advanced-hsl-modes,Hue-Saturation-Luminosity Advanced
Blend Operations>>, blending is performed according to the following
equations:
[latexmath]
+++++++++++++++++++
\begin{aligned}
R & = & f(R_s',R_d')*p_0(A_s,A_d) & + & Y*R_s'*p_1(A_s,A_d) & + & Z*R_d'*p_2(A_s,A_d) \\
G & = & f(G_s',G_d')*p_0(A_s,A_d) & + & Y*G_s'*p_1(A_s,A_d) & + & Z*G_d'*p_2(A_s,A_d) \\
B & = & f(B_s',B_d')*p_0(A_s,A_d) & + & Y*B_s'*p_1(A_s,A_d) & + & Z*B_d'*p_2(A_s,A_d) \\
A & = & X*p_0(A_s,A_d) & + & Y*p_1(A_s,A_d) & + & Z*p_2(A_s,A_d)
\end{aligned}
+++++++++++++++++++
where the function f and terms X, Y, and Z are specified in the table.
The R, G, and B components of the source color used for blending are derived
according to pname:srcPremultiplied.
If pname:srcPremultiplied is set to ename:VK_TRUE, the fragment color
components are considered to have been premultiplied by the A component
prior to blending.
The base source color [eq]#(R~s~',G~s~',B~s~')# is obtained by dividing
through by the A component:
[latexmath]
+++++++++++++++++++
\begin{aligned}
(R_s', G_s', B_s') & =
\begin{cases}
(0, 0, 0) & A_s = 0 \\
(\frac{R_s}{A_s}, \frac{G_s}{A_s}, \frac{B_s}{A_s}) & \text{otherwise}
\end{cases}
\end{aligned}
+++++++++++++++++++
If pname:srcPremultiplied is ename:VK_FALSE, the fragment color components
are used as the base color:
[latexmath]
+++++++++++++++++++
\begin{aligned}
(R_s', G_s', B_s') & = (R_s, G_s, B_s)
\end{aligned}
+++++++++++++++++++
The R, G, and B components of the destination color used for blending are
derived according to pname:dstPremultiplied.
If pname:dstPremultiplied is set to ename:VK_TRUE, the destination
components are considered to have been premultiplied by the A component
prior to blending.
The base destination color [eq]#(R~d~',G~d~',B~d~')# is obtained by dividing
through by the A component:
[latexmath]
+++++++++++++++++++
\begin{aligned}
(R_d', G_d', B_d') & =
\begin{cases}
(0, 0, 0) & A_d = 0 \\
(\frac{R_d}{A_d}, \frac{G_d}{A_d}, \frac{B_d}{A_d}) & \text{otherwise}
\end{cases}
\end{aligned}
+++++++++++++++++++
If pname:dstPremultiplied is ename:VK_FALSE, the destination color
components are used as the base color:
[latexmath]
+++++++++++++++++++
\begin{aligned}
(R_d', G_d', B_d') & = (R_d, G_d, B_d)
\end{aligned}
+++++++++++++++++++
[open,refpage='VkBlendOverlapEXT',desc='Enumerant specifying the blend overlap parameter',type='enums']
--
When blending using advanced blend operations, we expect that the R, G, and
B components of premultiplied source and destination color inputs be stored
as the product of non-premultiplied R, G, and B component values and the A
component of the color.
If any R, G, or B component of a premultiplied input color is non-zero and
the A component is zero, the color is considered ill-formed, and the
Change log for October 7, 2018 Vulkan 1.1.87 spec update: * Update release number to 87. Public Issues: * Merge flink:vkCmdPipelineBarrier self-dependency barrier VUs referring to the same subpass dependency (public pull request 756). * Describe default value of `"optional"` attribute in the registry schema document (public issue 769) * Fix links in <<VK_NVX_raytracing>> extension (public pull request 805). * Mark the <<VK_KHR_mir_surface>> extension obsolete (see public issue 814 - does not close this, however). * Fix missing endif in Image Creation block (public issue 817). Internal Issues: * Clarify that the compressed texture formats corresponding to <<features-features-textureCompressionETC2>>, <<features-features-textureCompressionASTC_LDR>>, and <<features-features-textureCompressionBC>> is not contingent on the feature bits, and may be supported even if the features are not enabled (internal issue 663). * Clarify that code:FragStencilRefEXT is output only in the <<interfaces-builtin-variables, Built-In Variables>> section (internal issue 1173). * Identify and correct many overly-aggressive uses of "`undefined`", and narrow them down, where straightforward to do so. Mark such resolved uses of "`undefined`" with the custom undefined: macro. Add a new <<writing-undefined, Describing Undefined Behavior>> section (internal issue 1267). * Don't require code:inline_uniform_block descriptors to be populated before use in the flink:vkAllocateDescriptorSets section (internal issue 1380). * Allow suppressing inline SVG images by controlling this with an attribute set in the Makefile, rather than the explicit [%inline] directive (internal issue 1391). * Mark 'Khronos' as a registered trademark in several places, now that it is one. * Fix typo in the <<VK_KHR_shader_atomic_int64>> appendix using the GLSL naming of the compare exchange op when referring to the SPIR-V op. * Specify in the flink:vkGetPhysicalDeviceQueueFamilyProperties section that all implementations must support at least one queue family, and that every queue family must contain at least one queue. * Make slink:VkPipelineDynamicStateCreateInfo::pname:dynamicStateCount, slink:VkSampleLocationsInfoEXT::pname:sampleLocationsPerPixel, and slink:VkSampleLocationsInfoEXT::pname:sampleLocationsCount optional, to fix bogus implicit valid usage checks that were causing failures in the conformance tests. * Fix vendor tag in reserved extension 237 constants. Does not affect anything since it's just a placeholder, but this should avoid further comments. * Minor markup fixes in some extension appendices. New Extensions: * `<<VK_FUCHSIA_imagepipe_surface>>`
2018-10-07 13:10:21 +00:00
corresponding component of the blend result is undefined:.
Change log for June 24, 2017 Vulkan 1.0.53 spec update: * Bump API patch number and header version number to 53 for this update. Github Issues: Internal Issues: * Clarify mappings of coordinates for mutable, compatible image views in slink:VkImageViewCreateInfo (internal issue 815). * Make ename:VK_BIND_SFR_BIT require a logical device with multiple physical devices, so that standard sparse image block dimensions are only required on systems that support multi-GPU (internal issue 835). * Convert all files from use of // refBegin .. // refEnd comments to delimit ref pages, to use of open blocks, and update style guide accordingly (internal issue 839). * Add valid usage for slink:VkWriteDescriptorSet when performing updates to a ename:VK_STORAGE_IMAGE descriptor with layout ename:VK_IMAGE_LAYOUT_GENERAL. * Add a hack to the validity generator script to support an odd interaction between flink:vkCmdFillBuffer and an extension (internal issue 853). * Remove redundant text describing slink:VkBufferCreateInfo::pname:usage, which was already covered by implicit valid usage (internal issue 854). * Update implicit validity generator script to properly handle the pname:sType and pname:pNext members of "returnedonly" structures (internal issue 874). * Note that slink:VkApplicationInfo::pname:pApplicationName & slink:VkApplicationInfo::pname:pEngineName are optional, and add missing implicit valid usage statements for flink:vkDestroyInstance. * Added missing valid usage for flink:vkCmdWriteTimestamp to require a timestamp query pool. * Simplify and/or split "`non-atomic`" valid usage statements. New Extensions: * `VK_AMD_gpu_shader_int16` * `VK_EXT_blend_operation_advanced` * `VK_EXT_sampler_filter_minmax` * `VK_NV_framebuffer_mixed_samples` ----------------------------------------------------- Note: the 1.0.52 spec wasn't published on github, so the 1.0.53 release combines both change sets. ----------------------------------------------------- Change log for June 13, 2017 Vulkan 1.0.52 spec update: * Bump API patch number and header version number to 52 for this update. Github Issues: Internal Issues: * Clarify behavior when non-coherent memory has <<memory-device-unmap-does-not-flush, not been flushed before being unmapped>> (internal issue 819). * Fix description of code:WorkgroupSize builtin to note it decorates an object, not a variable (internal issue 836). * Fix asciidoc attributes so that trailing '{plus}' symbols in [eq] style equations are rendered properly (internal issue 845). * Add language to the "`Extension Handles, Objects, Enums, and Typedefs`" section of the Procedures and Conventions document stating that any new handle type requires a corresponding entry in the elink:VkObjectType enumerated type (internal issue 856). * Update style guide to use slink macro for Vulkan handle type names, and define narrow conditions under which to use the *name and *text macros instead of *link (internal issue 886). * Add a dependency of the <<VK_KHX_device_group,VK_KHX_device_group>> extension on VK_KHX_device_group_creation to +vk.xml+ and the extension appendix. * Change the copyright on Vulkan specification asciidoc *source* files to CC-BY 4.0, and update the proprietary Khronos copyright applied to the generated *output* formats (internal issue 327). This enables broader re-use and modification of the Vulkan specification sources, while not affecting the Reciprocal IP License between Vulkan Adopters and Working Group Members. New Extensions: * `VK_NV_fill_rectangle` * `VK_NV_fragment_coverage_to_color`
2017-06-27 02:32:10 +00:00
The weighting functions [eq]#p~0~#, [eq]#p~1~#, and [eq]#p~2~# are defined
in table <<framebuffer-blend-advanced-overlap-modes,Advanced Blend Overlap
Modes>>.
In these functions, the A components of the source and destination colors
are taken to indicate the portion of the pixel covered by the fragment
(source) and the fragments previously accumulated in the pixel
(destination).
The functions [eq]#p~0~#, [eq]#p~1~#, and [eq]#p~2~# approximate the
relative portion of the pixel covered by the intersection of the source and
destination, covered only by the source, and covered only by the
destination, respectively.
Possible values of
slink:VkPipelineColorBlendAdvancedStateCreateInfoEXT::pname:blendOverlap,
specifying the blend overlap functions, are:
include::../../api/enums/VkBlendOverlapEXT.txt[]
* ename:VK_BLEND_OVERLAP_UNCORRELATED_EXT specifies that there is no
correlation between the source and destination coverage.
* ename:VK_BLEND_OVERLAP_CONJOINT_EXT specifies that the source and
destination coverage are considered to have maximal overlap.
* ename:VK_BLEND_OVERLAP_DISJOINT_EXT specifies that the source and
destination coverage are considered to have minimal overlap.
[[framebuffer-blend-advanced-overlap-modes]]
.Advanced Blend Overlap Modes
[width="80%",options="header"]
|====
| Overlap Mode | Weighting Equations
| ename:VK_BLEND_OVERLAP_UNCORRELATED_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
p_0(A_s,A_d) & = A_sA_d \\
p_1(A_s,A_d) & = A_s(1-A_d) \\
p_2(A_s,A_d) & = A_d(1-A_s) \\
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OVERLAP_CONJOINT_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
p_0(A_s,A_d) & = min(A_s,A_d) \\
p_1(A_s,A_d) & = max(A_s-A_d,0) \\
p_2(A_s,A_d) & = max(A_d-A_s,0) \\
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OVERLAP_DISJOINT_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
p_0(A_s,A_d) & = max(A_s+A_d-1,0) \\
p_1(A_s,A_d) & = min(A_s,1-A_d) \\
p_2(A_s,A_d) & = min(A_d,1-A_s) \\
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|====
--
[[framebuffer-blend-advanced-fxyz-modes]]
.f/X/Y/Z Advanced Blend Operations
[width="80%",options="header"]
|====
| Mode | Blend Coefficients
| ename:VK_BLEND_OP_ZERO_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (0,0,0) \\
f(C_s,C_d) & = 0
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_SRC_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,0) \\
f(C_s,C_d) & = C_s
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_DST_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,0,1) \\
f(C_s,C_d) & = C_d
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_SRC_OVER_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & = C_s
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_DST_OVER_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & = C_d
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_SRC_IN_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,0,0) \\
f(C_s,C_d) & = C_s
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_DST_IN_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,0,0) \\
f(C_s,C_d) & = C_d
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_SRC_OUT_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (0,1,0) \\
f(C_s,C_d) & = 0
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_DST_OUT_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (0,0,1) \\
f(C_s,C_d) & = 0
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_SRC_ATOP_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,0,1) \\
f(C_s,C_d) & = C_s
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_DST_ATOP_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,0) \\
f(C_s,C_d) & = C_d
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_XOR_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (0,1,1) \\
f(C_s,C_d) & = 0
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_MULTIPLY_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & = C_sC_d
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_SCREEN_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & = C_s+C_d-C_sC_d
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_OVERLAY_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & =
\begin{cases}
2 C_sC_d & C_d \leq 0.5 \\
1-2 (1-C_s)(1-C_d) & \text{otherwise}
\end{cases}
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_DARKEN_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & = min(C_s,C_d)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_LIGHTEN_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & = max(C_s,C_d)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_COLORDODGE_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & =
\begin{cases}
0 & C_d \leq 0 \\
min(1,\frac{C_d}{1-C_s}) & C_d \gt 0 \text{ and } C_s \lt 1 \\
1 & C_d \gt 0 \text{ and } C_s \geq 1
\end{cases}
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_COLORBURN_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & =
\begin{cases}
1 & C_d \geq 1 \\
1 - min(1,\frac{1-C_d}{C_s}) & C_d \lt 1 \text{ and } C_s \gt 0 \\
0 & C_d \lt 1 \text{ and } C_s \leq 0
\end{cases}
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_HARDLIGHT_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & =
\begin{cases}
2 C_sC_d & C_s \leq 0.5 \\
1-2 (1-C_s)(1-C_d) & \text{otherwise}
\end{cases}
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_SOFTLIGHT_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & =
\begin{cases}
C_d-(1-2 C_s)C_d(1-C_d) & C_s \leq 0.5 \\
C_d+(2 C_s-1)C_d((16 C_d-12)C_d+3) & C_s \gt 0.5 \text{ and } C_d \leq 0.25 \\
C_d+(2 C_s-1)(\sqrt{C_d}-C_d) & C_s \gt 0.5 \text{ and } C_d \gt 0.25
\end{cases}
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_DIFFERENCE_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & = \lvert C_d-C_s \rvert
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_EXCLUSION_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & = C_s+C_d-2C_sC_d
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_INVERT_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,0,1) \\
f(C_s,C_d) & = 1-C_d
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_INVERT_RGB_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,0,1) \\
f(C_s,C_d) & = C_s(1-C_d)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_LINEARDODGE_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & =
\begin{cases}
C_s+C_d & C_s+C_d \leq 1 \\
1 & \text{otherwise}
\end{cases}
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_LINEARBURN_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & =
\begin{cases}
C_s+C_d-1 & C_s+C_d \gt 1 \\
0 & \text{otherwise}
\end{cases}
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_VIVIDLIGHT_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & =
\begin{cases}
1-min(1,\frac{1-C_d}{2C_s}) & 0 \lt C_s \lt 0.5 \\
0 & C_s \leq 0 \\
min(1,\frac{C_d}{2(1-C_s)}) & 0.5 \leq C_s \lt 1 \\
1 & C_s \geq 1
\end{cases}
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_LINEARLIGHT_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & =
\begin{cases}
1 & 2C_s+C_d \gt 2 \\
2C_s+C_d-1 & 1 \lt 2C_s+C_d \leq 2 \\
0 & 2C_s+C_d \leq 1
\end{cases}
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_PINLIGHT_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & =
\begin{cases}
0 & 2C_s-1 \gt C_d \text{ and } C_s \lt 0.5 \\
2C_s-1 & 2C_s-1 \gt C_d \text{ and } C_s \geq 0.5 \\
2C_s & 2C_s-1 \leq C_d \text{ and } C_s \lt 0.5C_d \\
C_d & 2C_s-1 \leq C_d \text{ and } C_s \geq 0.5C_d
\end{cases}
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_HARDMIX_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & =
\begin{cases}
0 & C_s+C_d \lt 1 \\
1 & \text{otherwise}
\end{cases}
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|====
When using one of the HSL blend operations in table
<<framebuffer-blend-advanced-hsl-modes,Hue-Saturation-Luminosity Advanced
Blend Operations>> as the blend operation, the RGB color components produced
by the function f are effectively obtained by converting both the
non-premultiplied source and destination colors to the HSL (hue, saturation,
luminosity) color space, generating a new HSL color by selecting H, S, and L
components from the source or destination according to the blend operation,
and then converting the result back to RGB.
In the equations below, a blended RGB color is produced according to the
following pseudocode:
[source,c++]
----------------------------------------
float minv3(vec3 c) {
return min(min(c.r, c.g), c.b);
}
float maxv3(vec3 c) {
return max(max(c.r, c.g), c.b);
}
float lumv3(vec3 c) {
return dot(c, vec3(0.30, 0.59, 0.11));
}
float satv3(vec3 c) {
return maxv3(c) - minv3(c);
}
// If any color components are outside [0,1], adjust the color to
// get the components in range.
vec3 ClipColor(vec3 color) {
float lum = lumv3(color);
float mincol = minv3(color);
float maxcol = maxv3(color);
if (mincol < 0.0) {
color = lum + ((color-lum)*lum) / (lum-mincol);
}
if (maxcol > 1.0) {
color = lum + ((color-lum)*lum) / (maxcol-lum);
}
return color;
}
// Take the base RGB color <cbase> and override its luminosity
// with that of the RGB color <clum>.
vec3 SetLum(vec3 cbase, vec3 clum) {
float lbase = lumv3(cbase);
float llum = lumv3(clum);
float ldiff = llum - lbase;
vec3 color = cbase + vec3(ldiff);
return ClipColor(color);
}
// Take the base RGB color <cbase> and override its saturation with
// that of the RGB color <csat>. The override the luminosity of the
// result with that of the RGB color <clum>.
vec3 SetLumSat(vec3 cbase, vec3 csat, vec3 clum)
{
float minbase = minv3(cbase);
float sbase = satv3(cbase);
float ssat = satv3(csat);
vec3 color;
if (sbase > 0) {
// Equivalent (modulo rounding errors) to setting the
// smallest (R,G,B) component to 0, the largest to <ssat>,
// and interpolating the "middle" component based on its
// original value relative to the smallest/largest.
color = (cbase - minbase) * ssat / sbase;
} else {
color = vec3(0.0);
}
return SetLum(color, clum);
}
----------------------------------------
[[framebuffer-blend-advanced-hsl-modes]]
.Hue-Saturation-Luminosity Advanced Blend Operations
[width="80%",options="header"]
|====
| Mode | Result
| ename:VK_BLEND_OP_HSL_HUE_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & = SetLumSat(C_s,C_d,C_d)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_HSL_SATURATION_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & = SetLumSat(C_d,C_s,C_d)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_HSL_COLOR_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & = SetLum(C_s,C_d)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_HSL_LUMINOSITY_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(X,Y,Z) & = (1,1,1) \\
f(C_s,C_d) & = SetLum(C_d,C_s)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|====
When using one of the operations in table
<<framebuffer-blend-advanced-additional-rgb,Additional RGB Blend
Operations>> as the blend operation, the source and destination colors used
by these blending operations are interpreted according to
pname:srcPremultiplied and pname:dstPremultiplied.
The blending operations below are evaluated where the RGB source and
destination color components are both considered to have been premultiplied
by the corresponding A component.
[latexmath]
+++++++++++++++++++
\begin{aligned}
(R_s', G_s', B_s') & =
\begin{cases}
(R_s, G_s, B_s) & \text{if srcPremultiplied is VK\_TRUE} \\
(R_sA_s, G_sA_s, B_sA_s) & \text{if srcPremultiplied is VK\_FALSE}
\end{cases} \\
(R_d', G_d', B_d') & =
\begin{cases}
(R_d, G_d, B_d) & \text{if dstPremultiplied is VK\_TRUE} \\
(R_dA_d, G_dA_d, B_dA_d) & \text{if dstPremultiplied is VK\_FALSE}
\end{cases}
\end{aligned}
+++++++++++++++++++
[[framebuffer-blend-advanced-additional-rgb]]
.Additional RGB Blend Operations
[width="80%",options="header"]
|====
| Mode | Result
| ename:VK_BLEND_OP_PLUS_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(R,G,B,A) = ( & R_s'+R_d', \\
& G_s'+G_d', \\
& B_s'+B_d', \\
& A_s+A_d)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_PLUS_CLAMPED_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(R,G,B,A) = ( & min(1,R_s'+R_d'), \\
& min(1,G_s'+G_d'), \\
& min(1,B_s'+B_d'), \\
& min(1,A_s+A_d))
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_PLUS_CLAMPED_ALPHA_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(R,G,B,A) = ( & min(min(1,A_s+A_d),R_s'+R_d'), \\
& min(min(1,A_s+A_d),G_s'+G_d'), \\
& min(min(1,A_s+A_d),B_s'+B_d'), \\
& min(1,A_s+A_d))
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_PLUS_DARKER_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(R,G,B,A) = ( & max(0,min(1,A_s+A_d)-((A_s-R_s')+(A_d-R_d'))), \\
& max(0,min(1,A_s+A_d)-((A_s-G_s')+(A_d-G_d'))), \\
& max(0,min(1,A_s+A_d)-((A_s-B_s')+(A_d-B_d'))), \\
& min(1,A_s+A_d))
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_MINUS_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(R,G,B,A) = ( & R_d'-R_s', \\
& G_d'-G_s', \\
& B_d'-B_s', \\
& A_d-A_s)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_MINUS_CLAMPED_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(R,G,B,A) = ( & max(0,R_d'-R_s'), \\
& max(0,G_d'-G_s'), \\
& max(0,B_d'-B_s'), \\
& max(0,A_d-A_s))
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_CONTRAST_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(R,G,B,A) = ( & \frac{A_d}{2} + 2(R_d'-\frac{A_d}{2})(R_s'-\frac{A_s}{2}), \\
& \frac{A_d}{2} + 2(G_d'-\frac{A_d}{2})(G_s'-\frac{A_s}{2}), \\
& \frac{A_d}{2} + 2(B_d'-\frac{A_d}{2})(B_s'-\frac{A_s}{2}), \\
& A_d)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_INVERT_OVG_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(R,G,B,A) = ( & A_s(1-R_d') + (1-A_s)R_d', \\
& A_s(1-G_d') + (1-A_s)G_d', \\
& A_s(1-B_d') + (1-A_s)B_d', \\
& A_s+A_d-A_sA_d)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_RED_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(R,G,B,A) & = (R_s', G_d', B_d', A_d)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_GREEN_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(R,G,B,A) & = (R_d', G_s', B_d', A_d)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| ename:VK_BLEND_OP_BLUE_EXT a|
[latexmath]
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\begin{aligned}
(R,G,B,A) & = (R_d', G_d', B_s', A_d)
\end{aligned}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|====