You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1178 lines
50 KiB

#ifndef Magnum_Shaders_LineGL_h
#define Magnum_Shaders_LineGL_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022, 2023 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#ifndef MAGNUM_TARGET_GLES2
/** @file
* @brief Class @ref Magnum::Shaders::LineGL, typedef @ref Magnum::Shaders::LineGL2D, @ref Magnum::Shaders::LineGL3D
* @m_since_latest
*/
#endif
#include <Corrade/Utility/Move.h>
#include "Magnum/DimensionTraits.h"
#include "Magnum/GL/AbstractShaderProgram.h"
#include "Magnum/Shaders/GenericGL.h"
#include "Magnum/Shaders/glShaderWrapper.h"
#include "Magnum/Shaders/visibility.h"
#ifndef MAGNUM_TARGET_GLES2
namespace Magnum { namespace Shaders {
namespace Implementation {
enum class LineGLFlag: UnsignedShort {
VertexColor = 1 << 0,
ObjectId = 1 << 1,
InstancedObjectId = (1 << 2)|ObjectId,
InstancedTransformation = 1 << 3,
UniformBuffers = 1 << 4,
#ifndef MAGNUM_TARGET_WEBGL
ShaderStorageBuffers = UniformBuffers|(1 << 6),
#endif
MultiDraw = UniformBuffers|(1 << 5)
};
typedef Containers::EnumSet<LineGLFlag> LineGLFlags;
CORRADE_ENUMSET_OPERATORS(LineGLFlags)
}
/**
@brief Line GL shader
@m_since_latest
Renders lines expanded to quads in screen space. Compared to builtin GPU line
rendering, the lines can be of arbitrary width, with configurable join and cap
styles, and antialiased independently of MSAA being used or not.
@image html shaders-line.png width=256px
@experimental
@requires_gl30 Extension @gl_extension{EXT,gpu_shader4}
@requires_gles30 Requires integer support in shaders which is not available in
OpenGL ES 2.0.
@requires_webgl20 Requires integer support in shaders which is not available in
WebGL 1.0.
@section Shaders-LineGL-usage Usage
The shader doesn't work with @ref MeshPrimitive::Lines,
@ref MeshPrimitive::LineStrip or @ref MeshPrimitive::LineLoop directly, as that
would only be implementable with a relatively expensive geometry shader.
Instead, it requires the input data to be organized in quads, with
@ref Position, @ref PreviousPosition and @ref NextPosition attributes
describing point wíth their surroundings, and @ref Annotation with
point-specific annotation such as whether given point is a line cap or a join
with neighboring segment. The data layout is described in detail in
@ref Shaders-LineGL-mesh-representation below, however in practice it's easiest
to convert an existing line @ref Trade::MeshData to a form accepted by this
shader with @ref MeshTools::generateLines() and then compile it to a
@ref GL::Mesh with @ref MeshTools::compileLines():
@snippet Shaders-gl.cpp LineGL-usage
For rendering use @ref setTransformationProjectionMatrix(),
@ref setColor(), @ref setWidth() and others. It's important to pass viewport
size in @ref setViewportSize() as the line width is interpreted relative to it.
@snippet Shaders-gl.cpp LineGL-usage2
@subsection Shaders-LineGL-usage-triangulation Line triangulation
Each line segment is rendered as a quad consisting of two triangles. Standalone
segments have cap style configurable via @ref Configuration::setCapStyle() with
the following styles, segments of zero length can be also used to render
points. The @m_span{m-label m-default} white @m_endspan lines show generated
triangles, the @m_span{m-label m-info} blue @m_endspan line is the actually
visible edge between foreground and background:
@m_class{m-row}
@parblock
@m_div{m-col-l-6 m-text-center}
@htmlinclude line-cap-butt.svg
@ref LineCapStyle::Butt
@m_enddiv
@m_div{m-col-l-6 m-text-center}
@htmlinclude line-cap-square.svg
@ref LineCapStyle::Square
@m_enddiv
@m_div{m-col-l-6 m-text-center}
@htmlinclude line-cap-round.svg
@ref LineCapStyle::Round
@m_enddiv
@m_div{m-col-l-6 m-text-center}
@htmlinclude line-cap-triangle.svg
@ref LineCapStyle::Triangle
@m_enddiv
@endparblock
Joins between consecutive segments in contiguous line strips are expanded to
form a gap-less mesh without overlaps. Depending on join style picked in
@ref Configuration::setJoinStyle() and angle between the segments the area
between points `A`, `B` and `C` may be filled with another triangle:
@m_class{m-row}
@parblock
@m_div{m-col-l-6 m-text-center}
@htmlinclude line-join-miter.svg
@ref LineJoinStyle::Miter
@m_enddiv
@m_div{m-col-l-6 m-text-center}
@htmlinclude line-join-bevel.svg
@ref LineJoinStyle::Bevel
@m_enddiv
@endparblock
@subsection Shaders-LineGL-usage-antialiasing Antialiasing
The lines aren't smoothed out by default, use @ref setSmoothness() to pick a
tradeoff between the line being aliased and blurry. This is implemented by
interpolating between the foreground color and the background, which assumes
blending is set up for pre-multiplied alpha. If you're drawing lines on a
single-color background, you can @ref setBackgroundColor() to a color matching
the background and keep blending disabled, but note that you may get artifacts
if the lines are self-overlapping.
@snippet Shaders-gl.cpp LineGL-usage-antialiasing
@subsection Shaders-LineGL-usage-3d Lines in 3D
The 3D variant of this shader renders the geometry with depth values derived
from the original line endpoints, however without any perspective shortening
applied --- the line width is the same viewport-relative value independently of
the depth the point is at.
@section Shaders-LineGL-object-id Object ID output
The shader supports writing object ID to the framebuffer for object picking or
other annotation purposes. Enable it using @ref Flag::ObjectId and set up an
integer buffer attached to the @ref ObjectIdOutput attachment. If you have a
batch of meshes with different object IDs, enable @ref Flag::InstancedObjectId
and supply per-vertex IDs to the @ref ObjectId attribute. The output will
contain a sum of the per-vertex ID and ID coming from @ref setObjectId().
The functionality is practically the same as in the @ref FlatGL shader, see
@ref Shaders-FlatGL-object-id "its documentation" for more information and usage
example.
Note that the object ID is emitted for the whole triangle area, including
transparent areas of caps when using @ref LineCapStyle::Round or
@ref LineCapStyle::Triangle as well as semi-transparent edges with smoothness
values larger than zero. In particular, the object ID output will be aliased
even if the color output isn't.
@section Shaders-LineGL-instancing Instanced rendering
Enabling @ref Flag::InstancedTransformation will turn the shader into an
instanced one. It'll take per-instance transformation from the
@ref TransformationMatrix attribute, applying it before the matrix set by
@ref setTransformationProjectionMatrix(). Besides that, @ref Flag::VertexColor
(and the @ref Color3 / @ref Color4) attributes can work as both per-vertex and
per-instance. The functionality is practically the same as in the @ref FlatGL
shader, see @ref Shaders-FlatGL-instancing "its documentation" for more
information and usage example.
@requires_gl33 Extension @gl_extension{ARB,instanced_arrays}
@section Shaders-LineGL-ubo Uniform buffers
See @ref shaders-usage-ubo for a high-level overview that applies to all
shaders. In this particular case, because the shader doesn't need a separate
projection and transformation matrix, a combined one is supplied via a
@ref TransformationProjectionUniform2D / @ref TransformationProjectionUniform3D
buffer bound with @ref bindTransformationProjectionBuffer(). To maximize use of the limited uniform buffer memory, materials are supplied separately in a
@ref LineMaterialUniform buffer bound with @ref bindMaterialBuffer() and then
referenced via @relativeref{LineDrawUniform,materialId} from a
@ref LineDrawUniform bound with @ref bindDrawBuffer(). A uniform buffer setup
equivalent to the @ref Shaders-LineGL-usage "snippet at the top" would look
like this --- note that @ref setViewportSize() is an immediate uniform
here as well, as it's assumed to be set globally and rarely changed:
@snippet Shaders-gl.cpp LineGL-ubo
For a multidraw workflow enable @ref Flag::MultiDraw and supply desired
material and draw count via @ref Configuration::setMaterialCount() and
@relativeref{Configuration,setDrawCount()}. For every draw then specify
material references. Besides that, the usage is similar for all shaders, see
@ref shaders-usage-multidraw for an example.
@requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object} for uniform
buffers.
@requires_gl46 Extension @gl_extension{ARB,shader_draw_parameters} for
multidraw.
@requires_es_extension Extension @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt)
(unlisted) for multidraw.
@requires_webgl_extension Extension @webgl_extension{ANGLE,multi_draw} for
multidraw.
@section Shaders-LineGL-mesh-representation Line mesh representation
In order to avoid performing expensive CPU-side expansion of the quads every
time the transformation, line width and other parameters change, the shader
gets just the original line segment endpoints as an input, transforms them in
2D or 3D as usual, and then expands them in screen space for a desired line
width.
@htmlinclude line-quad-expansion.svg
Ignoring all complexity related to line caps and joins for now, an example
expansion of three line segments into quads is shown above --- the first two
segments form a join at the @m_span{m-label m-info} blue @m_endspan point, the
third segment is standalone. In order to form a quad, each of the points has
to be present twice in the vertex stream, with first copy expanding up and
second copy expanding down. The @ref Position data needed to render quads would
then look like below, color-coded to match the above, and in order following
the segment direction. An index buffer would then form two triangles out of
every four points --- @cpp {0, 1, 2, 2, 1, 3, …} @ce.
@htmlinclude line-quad-data.svg
To figure out the direction in which to expand, for given endpoint position the
shader needs also *screen-space direction* to the other endpoint. But since a
2D / 3D transformation has to be applied for both endpoints before calculating
their screen-space position, it makes more sense to supply directly its
position instead, and calculate the direction only after transforming both
points. The input data would then look like this, with "previous" positions
shown above and "next" positions shown below:
@htmlinclude line-quad-data-other.svg
With line joins and caps present, the quad expansion changes in the following
way. In the general case, to avoid overlapping geometry and gaps, points `B`
and `D` collapse to a single position and the area in between is filled with an
extra triangle. Depending on the transformation, it can however also happen
that `A` and `C` collapse into a single point instead, for example if the
@m_span{m-label m-primary} azure @m_endspan point would appear above the
@m_span{m-label m-success} green @m_endspan one instead of below. Thus the
index buffer needs to handle both cases --- @cpp {…, 2, 3, 4, 4, 3, 5, …} @ce
--- and one of them always denegerates to a zero-area triangle.
@htmlinclude line-quad-expansion-joins-caps.svg
To handle the join, the shader needs to know whether there's a neighboring line
segment to join with, and what is the position of its other endpoint. Thus,
every vertex gets *two* neighboring positions, a @ref PreviousPosition and a
@ref NextPosition. Both of them are filled only in case the point forms a line
join; if the point is a line cap, one of them is left unspecified.
@htmlinclude line-quad-data-neighbor.svg
What's left is giving the shader an ability to distinguish the direction in
which to expand the point (@ref LineVertexAnnotation::Up or downwards), whether
it's a @relativeref{LineVertexAnnotation,Join} or a cap and whether the point
is a @relativeref{LineVertexAnnotation,Begin} or an end of the segment in order
to know what the neigboring positions represent. This info is stored in the
@ref Annotation attribute and shown with `U`, `J` and `B` letters above. In
this particular case the info could also be inferred from the vertex index and
for example NaNs in the neigbor positions, but a dedicated attribute makes it
more flexible for optimized data layouts explained below.
@subsection Shaders-LineGL-data-representation-overlap Overlapping layouts with less data redundancy
Assuming a 3D line mesh with floating-point position attributes, the
@ref Annotation attribute packed into a single byte and
@ref MeshIndexType::UnsignedShort indices, a a single contiguous line strip
consisting of @f$ n @f$ line segments would need
@f$ (4(36 + 1) + 24)n = 172n @f$ bytes of data. In comparison,
CPU-side-generated indexed quads would need just
@f$ (24 + 18)n + 24 = 42n + 24 @f$ bytes, and a (non-indexed)
@ref MeshPrimitive::LineStrip only @f$ 12n + 12 @f$ bytes, which is ~14x less.
Fortunately, the position data can be organized in a way that makes it possible
to reuse them for previous and next positions as well, by binding the same data
again under an offset.
There's the following possibilites, each with different tradeoffs depending on
the use case. Such data layout variants require no special-casing in the
shader, only a different mesh setup, making it possible to pick the best option
for each line mesh without having to pay for expensive shader switching.
@subsubsection Shaders-LineGL-data-representation-overlap-nojoin Standalone line segments without joins
If the mesh consists just of loose line segments and no joints need to be
drawn, the @ref Position attribute can be bound with an offset of @cpp -2 @ce
elements to the @ref PreviousPosition and @cpp +2 @ce elements to the
@ref NextPosition. To avoid out-of-bound reads, the position buffer needs to be
padded with two elements at the front and at the end. Together with no indices
needed for joint triangles the memory requirement would be reduced to
@f$ (4(12 + 1) + 12)n + 12 = 64 n + 12 @f$ bytes, which is roughly the same
amount of data as for loose CPU-side-generated indexed quads, and ~2.7x as much
as @f$ 24n @f$ bytes a sufficiently large (non-indexed)
@ref MeshPrimitive::Lines would need.
@m_div{m-row m-container-inflate}
@m_div{m-col-l-12 m-nopady m-nopadx}
@htmlinclude line-quad-data-overlap-nojoin.svg
@m_enddiv
@m_enddiv
@subsubsection Shaders-LineGL-data-representation-overlap-generic Generic lines
For arbitrary lines that consist of both joined strips and standalone segments
and the joins can be of any style in any direction, the @ref Position attribute
has to be additionally padded with two elements at begin and end of every
contiguous line strip together with skipping the elements in the index buffer
appropriately, and then bound with an offset of @cpp -4 @ce elements to the
@ref PreviousPosition and @cpp +4 @ce elements to the @ref NextPosition.
This needs only one triangle in the index buffer for each join instead of two
and has a memory requirement of
@f$ (4(12 + 1) + 24)n + (4(12 + 1) - 12)l + 12 @f$ bytes, with @f$ l @f$
being the line strip count. With a mesh consisting of just a single strip this
is @f$ 76n + 52 @f$ bytes, which is ~1.8x as much as CPU-side-generated indexed
quads and ~6.3x as much as a @ref MeshPrimitive::LineStrip would need.
@m_div{m-row m-container-inflate}
@m_div{m-col-l-12 m-nopady m-nopadx}
@htmlinclude line-quad-data-overlap-generic.svg
@m_enddiv
@m_enddiv
@subsubsection Shaders-LineGL-data-representation-overlap-fixedjoin Lines with fixed join directions
If the joint direction is known to be fixed, i.e. the B and D points always
collapse to the same position independently of the transform used, the two
points can be replaced with just one. This is commonly the case in 2D if
negative transformation scaling isn't involved and with planar line art in 3D
if it additionally also isn't viewed from the back side. This allows padding
of the @ref Position attribute at the begin and end of every contiguous line
strip to be reduced to just one element, binding it with an offset of
@cpp -3 @ce elements to the @ref PreviousPosition and @cpp +3 @ce elements to
the @ref NextPosition.
This has a memory requirement of
@f$ (3(12 + 1) + 18)n + (3(12 + 1) - 6)l + 12 @f$ bytes. With a mesh consisting
of just a single strip this is @f$ 57n + 45 @f$ bytes, which is ~1.4x as much
as CPU-side-generated indexed quads and ~4.75x as much as a
@ref MeshPrimitive::LineStrip would need.
@m_div{m-row m-container-inflate}
@m_div{m-col-l-12 m-nopady m-nopadx}
@htmlinclude line-quad-data-overlap-fixedjoin.svg
@m_enddiv
@m_enddiv
@subsubsection Shaders-LineGL-data-representation-overlap-miterjoin Lines with miter joins only
The final and most data-efficient case is for line meshes where the contiguous
segments consist of miter joints only (i.e., with the assumption that the angle
between two segments is never too sharp to fall back to
@ref LineJoinStyle::Bevel), resulting in the join collapsing to just two
vertices, with no triangle in between:
@htmlinclude line-quad-expansion-joins-miter-caps.svg
This is the usual case for finely subdivided curves. Generic line art can be
patched in a preprocessing step, subdividing sharp corners to a sequence of
joins with larger angles. This layout doesn't require any padding of the
@ref Position attribute between contiguous line strips, and it's bound with an
offset of @cpp -2 @ce elements to the @ref PreviousPosition and @cpp +2 @ce
elements to the @ref NextPosition.
The memory requirement is @f$ (2(12 + 1) + 12)n + 2(12 + 1)l + 12 @f$ bytes.
With a mesh consisting of a single strip it's @f$ 38n + 38 @f$ bytes. This is
roughly the same memory use as @f$ 36n + 24 @f$ bytes for CPU-side-generated
quads with miter joins only, and ~3.2x as much as a
@ref MeshPrimitive::LineStrip would need.
@m_div{m-row m-container-inflate}
@m_div{m-col-l-12 m-nopady m-nopadx}
@htmlinclude line-quad-data-overlap-miterjoin.svg
@m_enddiv
@m_enddiv
*/
template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT LineGL: public GL::AbstractShaderProgram {
public:
/* MSVC needs dllexport here as well */
class MAGNUM_SHADERS_EXPORT Configuration;
class CompileState;
/**
* @brief Vertex position
*
* @ref shaders-generic "Generic attribute",
* @relativeref{Magnum,Vector2} in 2D, @relativeref{Magnum,Vector3} in
* 3D.
*/
typedef typename GenericGL<dimensions>::Position Position;
/**
* @brief Previous position
*
* @relativeref{Magnum,Vector2} in 2D, @relativeref{Magnum,Vector3} in
* 3D. Uses the same location as @ref GenericGL::Tangent with the
* assumption that lines don't need tangent space information.
*
* If @ref LineVertexAnnotation::Begin is set in @ref Annotation,
* contains the other point of the neighboring line segment if
* @ref LineVertexAnnotation::Join is also set, and is ignored
* otherwise. If @ref LineVertexAnnotation::Begin is not set in
* @ref Annotation, contains the other point of the line segment.
*/
typedef GL::Attribute<3, VectorTypeFor<dimensions, Float>> PreviousPosition;
/**
* @brief Next position
*
* @relativeref{Magnum,Vector2} in 2D, @relativeref{Magnum,Vector3} in
* 3D. Uses the same location as @ref GenericGL::Normal with the
* assumption that lines don't need tangent space information.
*
* If @ref LineVertexAnnotation::Begin is set in @ref Annotation,
* contains the other point of the line segment. If
* @ref LineVertexAnnotation::Begin is not set in @ref Annotation,
* contains the other point of the neighboring line segment if
* @ref LineVertexAnnotation::Join is set, and is ignored otherwise.
*/
typedef GL::Attribute<5, VectorTypeFor<dimensions, Float>> NextPosition;
/**
* @brief Vertex annotation
*
* Uses the same location as @ref GenericGL::TextureCoordinates with
* the assumption that lines don't need a two-dimensional texture
* space information.
*
* Contains a set of @ref LineVertexAnnotation bits, see their
* documentation for more information. The values are guaranteed to fit
* into 8 bits.
*/
typedef GL::Attribute<1, UnsignedInt> Annotation;
/**
* @brief Three-component vertex color
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Color3. Use
* either this or the @ref Color4 attribute.
*/
typedef typename GenericGL<dimensions>::Color3 Color3;
/**
* @brief Four-component vertex color
*
* @ref shaders-generic "Generic attribute", @ref Magnum::Color4. Use
* either this or the @ref Color3 attribute.
*/
typedef typename GenericGL<dimensions>::Color4 Color4;
/**
* @brief (Instanced) object ID
*
* @ref shaders-generic "Generic attribute",
* @relativeref{Magnum,UnsignedInt}. Used only if
* @ref Flag::InstancedObjectId is set.
*/
typedef typename GenericGL<dimensions>::ObjectId ObjectId;
/**
* @brief (Instanced) transformation matrix
*
* @ref shaders-generic "Generic attribute",
* @relativeref{Magnum,Matrix3} in 2D, @relativeref{Magnum,Matrix4} in
* 3D. Used only if @ref Flag::InstancedTransformation is set.
* @requires_gl33 Extension @gl_extension{ARB,instanced_arrays}
*/
typedef typename GenericGL<dimensions>::TransformationMatrix TransformationMatrix;
enum: UnsignedInt {
/**
* Color shader output. Present always, expects three- or
* four-component floating-point or normalized buffer attachment.
*/
ColorOutput = GenericGL<dimensions>::ColorOutput,
/**
* Object ID shader output. @ref shaders-generic "Generic output",
* present only if @ref Flag::ObjectId is set. Expects a
* single-component unsigned integral attachment. Writes the value
* set in @ref setObjectId() and possibly also a per-vertex ID and
* an ID fetched from a texture, see @ref Shaders-LineGL-object-id
* for more information.
* @requires_gl30 Extension @gl_extension{EXT,texture_integer}
*/
ObjectIdOutput = GenericGL<dimensions>::ObjectIdOutput
};
#ifdef DOXYGEN_GENERATING_OUTPUT
/**
* @brief Flag
*
* @see @ref Flags, @ref flags(), @ref Configuration::setFlags()
*/
enum class Flag: UnsignedShort {
/**
* Multiply the color with a vertex color. Requires either the
* @ref Color3 or @ref Color4 attribute to be present.
*/
VertexColor = 1 << 0,
/**
* Enable object ID output. See @ref Shaders-LineGL-object-id for
* more information.
*/
ObjectId = 1 << 1,
/**
* Instanced object ID. Retrieves a per-instance / per-vertex
* object ID from the @ref ObjectId attribute, outputting a sum of
* the per-vertex ID and ID coming from @ref setObjectId() or
* @ref LineDrawUniform::objectId. Implicitly enables
* @ref Flag::ObjectId. See @ref Shaders-LineGL-object-id for more
* information.
*/
InstancedObjectId = (1 << 2)|ObjectId,
/**
* Instanced transformation. Retrieves a per-instance
* transformation matrix from the @ref TransformationMatrix
* attribute and uses it together with the matrix coming from
* @ref setTransformationProjectionMatrix() or
* @ref TransformationProjectionUniform2D::transformationProjectionMatrix
* / @ref TransformationProjectionUniform3D::transformationProjectionMatrix
* (first the per-instance, then the uniform matrix). See
* @ref Shaders-LineGL-instancing for more information.
* @requires_gl33 Extension @gl_extension{ARB,instanced_arrays}
*/
InstancedTransformation = 1 << 3,
/**
* Use uniform buffers. Expects that uniform data are supplied via
* @ref bindTransformationProjectionBuffer(),
* @ref bindDrawBuffer() and @ref bindMaterialBuffer() instead of
* direct uniform setters.
* @see @ref Flag::ShaderStorageBuffers
* @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object}
*/
UniformBuffers = 1 << 4,
#ifndef MAGNUM_TARGET_WEBGL
/**
* Use shader storage buffers. Superset of functionality provided
* by @ref Flag::UniformBuffers, compared to it doesn't have any
* size limits on @ref Configuration::setMaterialCount() and
* @relativeref{Configuration,setDrawCount()} in exchange for
* potentially more costly access and narrower platform support.
* @requires_gl43 Extension @gl_extension{ARB,shader_storage_buffer_object}
* @requires_gles31 Shader storage buffers are not available in
* OpenGL ES 3.0 and older.
* @requires_gles Shader storage buffers are not available in
* WebGL.
* @m_since_latest
*/
ShaderStorageBuffers = UniformBuffers|(1 << 6),
#endif
/**
* Enable multidraw functionality. Implies @ref Flag::UniformBuffers
* and adds the value from @ref setDrawOffset() with the
* @glsl gl_DrawID @ce builtin, which makes draws submitted via
* @ref GL::AbstractShaderProgram::draw(const Containers::Iterable<MeshView>&)
* and related APIs pick up per-draw parameters directly, without
* having to rebind the uniform buffers or specify
* @ref setDrawOffset() before each draw. In a non-multidraw
* scenario, @glsl gl_DrawID @ce is @cpp 0 @ce, which means a
* shader with this flag enabled can be used for regular draws as
* well.
* @requires_gl46 Extension @gl_extension{ARB,uniform_buffer_object}
* and @gl_extension{ARB,shader_draw_parameters}
* @requires_es_extension OpenGL ES 3.0 and extension
* @m_class{m-doc-external} [ANGLE_multi_draw](https://chromium.googlesource.com/angle/angle/+/master/extensions/ANGLE_multi_draw.txt)
* (unlisted). While the extension alone needs only OpenGL ES
* 2.0, the shader implementation relies on uniform buffers,
* which require OpenGL ES 3.0.
* @requires_webgl_extension WebGL 2.0 and extension
* @webgl_extension{ANGLE,multi_draw}. While the extension
* alone needs only WebGL 1.0, the shader implementation
* relies on uniform buffers, which require WebGL 2.0.
*/
MultiDraw = UniformBuffers|(1 << 5)
};
/**
* @brief Flags
*
* @see @ref flags(), @ref Configuration::setFlags()
*/
typedef Containers::EnumSet<Flag> Flags;
#else
/* Done this way to be prepared for possible future diversion of 2D
and 3D flags (e.g. introducing 3D-specific features) */
typedef Implementation::LineGLFlag Flag;
typedef Implementation::LineGLFlags Flags;
#endif
/**
* @brief Compile asynchronously
*
* Compared to @ref LineGL(const Configuration&) can perform an
* asynchronous compilation and linking. See @ref shaders-async for
* more information.
* @see @ref LineGL(CompileState&&)
*/
/* Compared to the non-templated shaders like PhongGL or
MeshVisualizerGL2D using a forward declaration is fine here. Huh. */
static CompileState compile(const Configuration& configuration = Configuration{});
/** @brief Constructor */
/* Compared to the non-templated shaders like PhongGL or
MeshVisualizerGL2D using a forward declaration is fine here. Huh. */
explicit LineGL(const Configuration& configuration = Configuration{});
/**
* @brief Finalize an asynchronous compilation
*
* Takes an asynchronous compilation state returned by @ref compile()
* and forms a ready-to-use shader object. See @ref shaders-async for
* more information.
*/
explicit LineGL(CompileState&& state);
/**
* @brief Construct without creating the underlying OpenGL object
*
* The constructed instance is equivalent to a moved-from state. Useful
* in cases where you will overwrite the instance later anyway. Move
* another object over it to make it useful.
*
* This function can be safely used for constructing (and later
* destructing) objects even without any OpenGL context being active.
* However note that this is a low-level and a potentially dangerous
* API, see the documentation of @ref NoCreate for alternatives.
*/
explicit LineGL(NoCreateT) noexcept: GL::AbstractShaderProgram{NoCreate} {}
/** @brief Copying is not allowed */
LineGL(const LineGL<dimensions>&) = delete;
/** @brief Move constructor */
LineGL(LineGL<dimensions>&&) noexcept = default;
/** @brief Copying is not allowed */
LineGL<dimensions>& operator=(const LineGL<dimensions>&) = delete;
/** @brief Move assignment */
LineGL<dimensions>& operator=(LineGL<dimensions>&&) noexcept = default;
/**
* @brief Flags
*
* @see @ref Configuration::setFlags()
*/
Flags flags() const { return _flags; }
/**
* @brief Cap style
*
* @see @ref Configuration::setCapStyle()
*/
LineCapStyle capStyle() const { return _capStyle; }
/**
* @brief Join style
*
* @see @ref Configuration::setJoinStyle()
*/
LineJoinStyle joinStyle() const { return _joinStyle; }
/**
* @brief Material count
*
* Statically defined size of the @ref LineMaterialUniform uniform
* buffer bound with @ref bindMaterialBuffer(). Has use only if
* @ref Flag::UniformBuffers is set and @ref Flag::ShaderStorageBuffers
* is not set.
* @see @ref Configuration::setMaterialCount()
*/
UnsignedInt materialCount() const { return _materialCount; }
/**
* @brief Draw count
*
* Statically defined size of each of the
* @ref TransformationProjectionUniform2D /
* @ref TransformationProjectionUniform3D and @ref LineDrawUniform
* uniform buffers bound with @ref bindTransformationProjectionBuffer()
* and @ref bindDrawBuffer(). Has use only if @ref Flag::UniformBuffers
* is set and @ref Flag::ShaderStorageBuffers is not set.
* @see @ref Configuration::setDrawCount()
*/
UnsignedInt drawCount() const { return _drawCount; }
/**
* @brief Set viewport size
* @return Reference to self (for method chaining)
*
* Line width and smoothness set in either @ref setWidth() /
* @ref setSmoothness() or @ref LineMaterialUniform::width /
* @ref LineMaterialUniform::smoothness depends on this value --- i.e.,
* a value of @cpp 1.0f @ce is one pixel only if @ref setViewportSize()
* is called with the actual pixel size of the viewport. Initial value
* is a zero vector.
*/
LineGL<dimensions>& setViewportSize(const Vector2& size);
/** @{
* @name Uniform setters
*
* Used only if @ref Flag::UniformBuffers is not set.
*/
/**
* @brief Set transformation and projection matrix
* @return Reference to self (for method chaining)
*
* Initial value is an identity matrix. If
* @ref Flag::InstancedTransformation is set, the per-instance
* transformation matrix coming from the @ref TransformationMatrix
* attribute is applied first, before this one.
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref TransformationProjectionUniform2D::transformationProjectionMatrix /
* @ref TransformationProjectionUniform3D::transformationProjectionMatrix
* and call @ref bindTransformationProjectionBuffer() instead.
*/
LineGL<dimensions>& setTransformationProjectionMatrix(const MatrixTypeFor<dimensions, Float>& matrix);
/**
* @brief Set background color
* @return Reference to self (for method chaining)
*
* Initial value is @cpp 0x00000000_rgbaf @ce. Used for edge smoothing
* if smoothness is non-zero, and for background areas if
* @ref LineCapStyle::Round or @ref LineCapStyle::Triangle is used. If
* smoothness is zero and @ref LineCapStyle::Butt or
* @ref LineCapStyle::Square is used, only the foreground color is
* used.
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref LineMaterialUniform::backgroundColor and call
* @ref bindMaterialBuffer() instead.
* @see @ref setColor(), @ref setSmoothness(),
* @ref Configuration::setCapStyle()
*/
LineGL<dimensions>& setBackgroundColor(const Magnum::Color4& color);
/**
* @brief Set color
* @return Reference to self (for method chaining)
*
* Initial value is @cpp 0xffffffff_rgbaf @ce.
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref LineMaterialUniform::color and call @ref bindMaterialBuffer()
* instead.
* @see @ref setBackgroundColor()
*/
LineGL<dimensions>& setColor(const Magnum::Color4& color);
/**
* @brief Set line width
* @return Reference to self (for method chaining)
*
* Screen-space, interpreted depending on the viewport size --- i.e.,
* a value of @cpp 1.0f @ce is one pixel only if @ref setViewportSize()
* is called with the actual pixel size of the viewport. Initial value
* is @cpp 1.0f @ce.
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref LineMaterialUniform::width and call @ref bindMaterialBuffer()
* instead.
*/
LineGL<dimensions>& setWidth(Float width);
/**
* @brief Set line smoothness
* @return Reference to self (for method chaining)
*
* Larger values will make edges look less aliased (but blurry),
* smaller values will make them more crisp (but possibly aliased).
* Screen-space, interpreted depending on the viewport size --- i.e.,
* a value of @cpp 1.0f @ce is one pixel only if @ref setViewportSize()
* is called with the actual pixel size of the viewport. Initial value
* is @cpp 0.0f @ce.
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref LineMaterialUniform::smoothness and call @ref bindMaterialBuffer()
* instead.
*/
LineGL<dimensions>& setSmoothness(Float smoothness);
/**
* @brief Set miter length limit
* @return Reference to self (for method chaining)
*
* Maximum length (relative to line width) over which a
* @ref LineJoinStyle::Miter join is converted to a
* @ref LineJoinStyle::Bevel in order to avoid sharp corners extending
* too much. Default value is @cpp 4.0f @ce, which corresponds to
* approximately 29 degrees. Alternatively you can set the limit as an
* angle using @ref setMiterAngleLimit(). Miter length is calculated
* using the following formula, where @f$ w @f$ is line half-width,
* @f$ l @f$ is miter length and @f$ \theta @f$ is angle between two
* line segments: @f[
* \frac{w}{l} = \sin(\frac{\theta}{2})
* @f]
*
* Expects that @ref joinStyle() is @ref LineJoinStyle::Miter and
* @p limit is greater than or equal to @cpp 1.0f @ce and finite.
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref LineMaterialUniform::miterLimit using
* @ref LineMaterialUniform::setMiterLengthLimit() and call
* @ref bindMaterialBuffer() instead.
*/
LineGL<dimensions>& setMiterLengthLimit(Float limit);
/**
* @brief Set miter angle limit
* @return Reference to self (for method chaining)
*
* Like @ref setMiterLengthLimit(), but specified as a minimum angle
* between two line segments below which a @ref LineJoinStyle::Miter
* join is converted to a @ref LineJoinStyle::Bevel in order to avoid
* sharp corners extending too much. Default value is approximately
* @cpp 28.955_degf @ce, see @ref setMiterLengthLimit() above for more
* information.
*
* Expects that @ref joinStyle() is @ref LineJoinStyle::Miter and
* @p limit is greater than @cpp 0.0_radf @ce. Expects that
* @ref Flag::UniformBuffers is not set, in that case fill
* @ref LineMaterialUniform::miterLimit using
* @ref LineMaterialUniform::setMiterAngleLimit() and call
* @ref bindMaterialBuffer() instead.
*/
LineGL<dimensions>& setMiterAngleLimit(Rad limit);
/**
* @brief Set object ID
* @return Reference to self (for method chaining)
*
* Expects that the shader was created with @ref Flag::ObjectId
* enabled. Value set here is written to the @ref ObjectIdOutput, see
* @ref Shaders-LineGL-object-id for more information. Initial value
* is @cpp 0 @ce. If @ref Flag::InstancedObjectId is enabled as well,
* this value is added to the ID coming from the @ref ObjectId
* attribute.
*
* Expects that @ref Flag::UniformBuffers is not set, in that case fill
* @ref LineDrawUniform::objectId and call @ref bindDrawBuffer()
* instead.
*/
LineGL<dimensions>& setObjectId(UnsignedInt id);
/**
* @}
*/
/** @{
* @name Uniform / shader storage buffer binding and related uniform setters
*
* Used if @ref Flag::UniformBuffers is set.
*/
/**
* @brief Bind a draw offset
* @return Reference to self (for method chaining)
*
* Specifies which item in the @ref TransformationProjectionUniform2D /
* @ref TransformationProjectionUniform3D and @ref LineDrawUniform
* buffers bound with @ref bindTransformationProjectionBuffer() and
* @ref bindDrawBuffer() should be used for current draw. Expects that
* @ref Flag::UniformBuffers is set and @p offset is less than
* @ref drawCount(). Initial value is @cpp 0 @ce, if @ref drawCount()
* is @cpp 1 @ce, the function is a no-op as the shader assumes draw
* offset to be always zero.
*
* If @ref Flag::MultiDraw is set, @glsl gl_DrawID @ce is added to this
* value, which makes each draw submitted via
* @ref GL::AbstractShaderProgram::draw(const Containers::Iterable<MeshView>&)
* pick up its own per-draw parameters.
* @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object}
* @requires_gles30 Uniform buffers are not available in OpenGL ES 2.0.
* @requires_webgl20 Uniform buffers are not available in WebGL 1.0.
*/
LineGL<dimensions>& setDrawOffset(UnsignedInt offset);
/**
* @brief Bind a transformation and projection uniform / shader storage buffer
* @return Reference to self (for method chaining)
*
* Expects that @ref Flag::UniformBuffers is set. The buffer is
* expected to contain @ref drawCount() instances of
* @ref TransformationProjectionUniform2D /
* @ref TransformationProjectionUniform3D. At the very least you need
* to call also @ref bindDrawBuffer() and @ref bindMaterialBuffer().
* @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object}
*/
LineGL<dimensions>& bindTransformationProjectionBuffer(GL::Buffer& buffer);
LineGL<dimensions>& bindTransformationProjectionBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /**< @overload */
/**
* @brief Bind a draw uniform / shader storage buffer
* @return Reference to self (for method chaining)
*
* Expects that @ref Flag::UniformBuffers is set. The buffer is
* expected to contain @ref drawCount() instances of
* @ref LineDrawUniform. At the very least you need to call also
* @ref bindTransformationProjectionBuffer() and
* @ref bindMaterialBuffer().
* @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object}
*/
LineGL<dimensions>& bindDrawBuffer(GL::Buffer& buffer);
LineGL<dimensions>& bindDrawBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /**< @overload */
/**
* @brief Bind a material uniform / shader storage buffer
* @return Reference to self (for method chaining)
*
* Expects that @ref Flag::UniformBuffers is set. The buffer is
* expected to contain @ref materialCount() instances of
* @ref LineMaterialUniform. At the very least you need to call also
* @ref bindTransformationProjectionBuffer() and @ref bindDrawBuffer().
* @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object}
*/
LineGL<dimensions>& bindMaterialBuffer(GL::Buffer& buffer);
LineGL<dimensions>& bindMaterialBuffer(GL::Buffer& buffer, GLintptr offset, GLsizeiptr size); /**< @overload */
/**
* @}
*/
MAGNUM_GL_ABSTRACTSHADERPROGRAM_SUBCLASS_DRAW_IMPLEMENTATION(LineGL<dimensions>)
private:
/* Creates the GL shader program object but does nothing else.
Internal, used by compile(). */
explicit LineGL(NoInitT);
Flags _flags;
LineCapStyle _capStyle;
LineJoinStyle _joinStyle;
UnsignedInt _materialCount{},
_drawCount{};
Int _viewportSizeUniform{0},
_transformationProjectionMatrixUniform{1},
_backgroundColorUniform{2},
_colorUniform{3},
_widthUniform{4},
_smoothnessUniform{5},
_miterLimitUniform{6},
_objectIdUniform{7},
/* Used instead of all other uniforms except viewportSize when
Flag::UniformBuffers is set, so it can alias them */
_drawOffsetUniform{1};
};
/**
@brief Configuration
@see @ref LineGL(const Configuration&), @ref compile(const Configuration&)
*/
template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT LineGL<dimensions>::Configuration {
public:
explicit Configuration();
/** @brief Flags */
Flags flags() const { return _flags; }
/**
* @brief Set flags
*
* No flags are set by default.
* @see @ref LineGL::flags()
*/
Configuration& setFlags(Flags flags) {
_flags = flags;
return *this;
}
/** @brief Cap style */
LineCapStyle capStyle() const { return _capStyle; }
/**
* @brief Set cap style
*
* Unlike for example the SVG specification that uses
* @ref LineCapStyle::Butt by default, the default value is
* @ref LineCapStyle::Square, in order to make zero-length lines visible.
* @see @ref LineGL::capStyle()
*/
Configuration& setCapStyle(LineCapStyle style) {
_capStyle = style;
return *this;
}
/** @brief Join style */
LineJoinStyle joinStyle() const { return _joinStyle; }
/**
* @brief Set join style
*
* Default value is @ref LineJoinStyle::Miter, consistently with the
* SVG specification.
* @see @ref LineGL::joinStyle()
*/
Configuration& setJoinStyle(LineJoinStyle style) {
_joinStyle = style;
return *this;
}
/** @brief Material count */
UnsignedInt materialCount() const { return _materialCount; }
/**
* @brief Set material count
*
* If @ref Flag::UniformBuffers is set, describes size of a
* @ref LineMaterialUniform buffer bound with
* @ref bindMaterialBuffer(). Uniform buffers have a statically defined
* size and @cpp count*sizeof(LineMaterialUniform) @ce has to be within
* @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if
* @ref Flag::ShaderStorageBuffers is set as well, the buffer is
* unbounded and @p count is ignored. The per-draw materials are
* specified via @ref LineDrawUniform::materialId. Default value is
* @cpp 1 @ce.
*
* If @ref Flag::UniformBuffers isn't set, this value is ignored.
* @see @ref setFlags(), @ref setDrawCount(),
* @ref LineGL::materialCount()
* @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object}
*/
Configuration& setMaterialCount(UnsignedInt count) {
_materialCount = count;
return *this;
}
/** @brief Draw count */
UnsignedInt drawCount() const { return _drawCount; }
/**
* @brief Set draw count
*
* If @ref Flag::UniformBuffers is set, describes size of a
* @ref TransformationProjectionUniform2D /
* @ref TransformationProjectionUniform3D /
* @ref LineDrawUniform buffer bound with
* @ref bindTransformationProjectionBuffer() and @ref bindDrawBuffer().
* Uniform buffers have a statically defined size and the maximum of
* @cpp count*sizeof(TransformationProjectionUniform2D) @ce /
* @cpp count*sizeof(TransformationProjectionUniform3D) @ce and
* @cpp count*sizeof(LineDrawUniform) @ce has to be within
* @ref GL::AbstractShaderProgram::maxUniformBlockSize(), if
* @ref Flag::ShaderStorageBuffers is set as well, the buffers are
* unbounded and @p count is ignored. The draw offset is set via
* @ref setDrawOffset(). Default value is @cpp 1 @ce.
*
* If @ref Flag::UniformBuffers isn't set, this value is ignored.
* @see @ref setFlags(), @ref setMaterialCount(),
* @ref LineGL::drawCount()
* @requires_gl31 Extension @gl_extension{ARB,uniform_buffer_object}
*/
Configuration& setDrawCount(UnsignedInt count) {
_drawCount = count;
return *this;
}
private:
Flags _flags;
LineCapStyle _capStyle;
LineJoinStyle _joinStyle;
UnsignedInt _materialCount{1};
UnsignedInt _drawCount{1};
};
/**
@brief Asynchronous compilation state
Returned by @ref compile(). See @ref shaders-async for more information.
*/
template<UnsignedInt dimensions> class LineGL<dimensions>::CompileState: public LineGL<dimensions> {
/* Everything deliberately private except for the inheritance */
friend class LineGL;
explicit CompileState(NoCreateT): LineGL{NoCreate}, _vert{NoCreate}, _frag{NoCreate} {}
explicit CompileState(LineGL<dimensions>&& shader, GL::Shader&& vert, GL::Shader&& frag
#if !defined(MAGNUM_TARGET_GLES) || !defined(MAGNUM_TARGET_WEBGL)
, GL::Version version
#endif
): LineGL<dimensions>{Utility::move(shader)}, _vert{Utility::move(vert)}, _frag{Utility::move(frag)}
#if !defined(MAGNUM_TARGET_GLES) || !defined(MAGNUM_TARGET_WEBGL)
, _version{version}
#endif
{}
Implementation::GLShaderWrapper _vert, _frag;
#if !defined(MAGNUM_TARGET_GLES) || !defined(MAGNUM_TARGET_WEBGL)
GL::Version _version;
#endif
};
/**
@brief 2D line OpenGL shader
@m_since_latest
*/
typedef LineGL<2> LineGL2D;
/**
@brief 3D LineGL OpenGL shader
@m_since_latest
*/
typedef LineGL<3> LineGL3D;
#ifdef DOXYGEN_GENERATING_OUTPUT
/**
* @debugoperatorclassenum{LineGL,LineGL::Flag}
* @m_since_latest
*/
template<UnsignedInt dimensions> Debug& operator<<(Debug& debug, LineGL<dimensions>::Flag value);
/**
* @debugoperatorclassenum{LineGL,LineGL::Flags}
* @m_since_latest
*/
template<UnsignedInt dimensions> Debug& operator<<(Debug& debug, LineGL<dimensions>::Flags value);
#else
namespace Implementation {
MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, LineGLFlag value);
MAGNUM_SHADERS_EXPORT Debug& operator<<(Debug& debug, LineGLFlags value);
}
#endif
}}
#else
#error this header is not available in the OpenGL ES 2.0 / WebGL 1.0 build
#endif
#endif