diff --git a/doc/changelog.dox b/doc/changelog.dox index f0a9017e3..5f3415dd0 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -226,6 +226,7 @@ See also: - Normal texture support in @ref Shaders::Phong - Added @ref Shaders::Generic3D::Tangent generic vertex attribute definition +- Object ID output in @ref Shaders::Flat and @ref Shaders::Phong @subsubsection changelog-latest-new-text Text library diff --git a/doc/shaders.dox b/doc/shaders.dox index 0369dc8d8..48e5891a7 100644 --- a/doc/shaders.dox +++ b/doc/shaders.dox @@ -73,27 +73,35 @@ Example configuration and rendering using @link Shaders::Phong @endlink: @snippet MagnumShaders.cpp shaders-rendering -@section shaders-generic Generic vertex attributes +@section shaders-generic Generic vertex attributes and framebuffer attachments Many shaders share the same vertex attribute definitions, such as positions, normals, texture coordinates etc. It's thus possible to configure the mesh -for *generic* shader and then render it with any compatible shader. Definition -of generic attributes is available in @ref Shaders::Generic class. -Configuration of the above mesh using generic attributes could then look like -this: +for a *generic* shader and then render it with any compatible shader. +Definition of all generic attributes is available in the @ref Shaders::Generic +class. Configuration of the above mesh using generic attributes could then look +like this: @snippet MagnumShaders.cpp shaders-generic Note that in this particular case both configurations are equivalent, because -@ref Shaders::Phong also uses generic vertex attribute definitions. - -Then you can render the mesh using @ref Shaders::Phong shader like above, or -use for example @ref Shaders::Flat3D or even @ref Shaders::MeshVisualizer with -the same mesh reconfiguration. The unused attributes will be simply ignored. +@ref Shaders::Phong also uses generic vertex attribute definitions. Then you +can render the mesh using @ref Shaders::Phong shader like above, or use for +example @ref Shaders::Flat3D or even @ref Shaders::MeshVisualizer with the same +mesh reconfiguration. The unused attributes will be simply ignored. @snippet MagnumShaders.cpp shaders-meshvisualizer The @ref MeshTools::compile() utility configures meshes using generic vertex attribute definitions to make them usable with any shader. + +Besides vertex attributes, the @ref Shaders::Generic contains generic +definitions for framebuffer outputs as well --- in many cases a shader has just +one (color) output, but some shaders such as @ref Shaders::Flat or +@ref Shaders::Phong offer an object ID output as well. A setup equivalent to +what's done in Flat shader's @ref Shaders-Flat-usage-object-id but using the +generic definitions would look like this: + +@snippet MagnumShaders.cpp shaders-generic-object-id */ } diff --git a/doc/snippets/MagnumShaders.cpp b/doc/snippets/MagnumShaders.cpp index b0737b522..954527a8e 100644 --- a/doc/snippets/MagnumShaders.cpp +++ b/doc/snippets/MagnumShaders.cpp @@ -29,7 +29,10 @@ #include "Magnum/GL/Buffer.h" #include "Magnum/GL/DefaultFramebuffer.h" +#include "Magnum/GL/Framebuffer.h" #include "Magnum/GL/Mesh.h" +#include "Magnum/GL/Renderbuffer.h" +#include "Magnum/GL/RenderbufferFormat.h" #include "Magnum/GL/Texture.h" #include "Magnum/Math/Color.h" #include "Magnum/MeshTools/Duplicate.h" @@ -204,6 +207,42 @@ mesh.draw(shader); /* [Flat-usage-textured2] */ } +#ifndef MAGNUM_TARGET_GLES2 +{ +GL::Framebuffer framebuffer{{}}; +GL::Mesh mesh; +Vector2i size; +UnsignedInt meshId{}; +/* [Flat-usage-object-id] */ +GL::Renderbuffer color, objectId; +color.setStorage(GL::RenderbufferFormat::RGBA8, size); +objectId.setStorage(GL::RenderbufferFormat::R16UI, size); // large as needed +framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, color) + .attachRenderbuffer(GL::Framebuffer::ColorAttachment{1}, objectId); + +Shaders::Flat3D shader{Shaders::Flat3D::Flag::ObjectId}; + +// ... + +framebuffer.mapForDraw({ + {Shaders::Flat3D::ColorOutput, GL::Framebuffer::ColorAttachment{0}}, + {Shaders::Flat3D::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}}}) + .clearColor(0, 0x1f1f1f_rgbf) + .clearColor(1, Vector4ui{0}) + .bind(); + +shader.setObjectId(meshId); +mesh.draw(shader); +/* [Flat-usage-object-id] */ + +/* [shaders-generic-object-id] */ +framebuffer.mapForDraw({ + {Shaders::Generic3D::ColorOutput, GL::Framebuffer::ColorAttachment{0}}, + {Shaders::Generic3D::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}}}); +/* [shaders-generic-object-id] */ +} +#endif + { /* [MeshVisualizer-usage-geom1] */ struct Vertex { diff --git a/src/Magnum/Shaders/Flat.cpp b/src/Magnum/Shaders/Flat.cpp index bb6c3cb3c..ce0855174 100644 --- a/src/Magnum/Shaders/Flat.cpp +++ b/src/Magnum/Shaders/Flat.cpp @@ -68,13 +68,18 @@ template Flat::Flat(const Flags flags): _fla .addSource(rs.get(vertexShaderName())); frag.addSource(flags & Flag::Textured ? "#define TEXTURED\n" : "") .addSource(flags & Flag::AlphaMask ? "#define ALPHA_MASK\n" : "") + #ifndef MAGNUM_TARGET_GLES2 + .addSource(flags & Flag::ObjectId ? "#define OBJECT_ID\n" : "") + #endif + .addSource(rs.get("generic.glsl")) .addSource(rs.get("Flat.frag")); CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); attachShaders({vert, frag}); - /* ES3 has this done in the shader directly */ + /* ES3 has this done in the shader directly and doesn't even provide + bindFragmentDataLocation() */ #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2) #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported(version)) @@ -82,6 +87,12 @@ template Flat::Flat(const Flags flags): _fla { bindAttributeLocation(Position::Location, "position"); if(flags & Flag::Textured) bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags & Flag::ObjectId) { + bindFragmentDataLocation(ColorOutput, "color"); + bindFragmentDataLocation(ObjectIdOutput, "objectId"); + } + #endif } #endif @@ -94,6 +105,9 @@ template Flat::Flat(const Flags flags): _fla _transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix"); _colorUniform = uniformLocation("color"); if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId"); + #endif } #ifndef MAGNUM_TARGET_GLES @@ -108,6 +122,7 @@ template Flat::Flat(const Flags flags): _fla setTransformationProjectionMatrix({}); setColor(Color4{1.0f}); if(flags & Flag::AlphaMask) setAlphaMask(0.5f); + /* Object ID is zero by default */ #endif } @@ -125,6 +140,15 @@ template Flat& Flat::setAlphaMas return *this; } +#ifndef MAGNUM_TARGET_GLES2 +template Flat& Flat::setObjectId(UnsignedInt id) { + CORRADE_ASSERT(_flags & Flag::ObjectId, + "Shaders::Flat::setObjectId(): the shader was not created with object ID enabled", *this); + setUniform(_objectIdUniform, id); + return *this; +} +#endif + template class Flat<2>; template class Flat<3>; @@ -136,6 +160,9 @@ Debug& operator<<(Debug& debug, const FlatFlag value) { #define _c(v) case FlatFlag::v: return debug << "Shaders::Flat::Flag::" #v; _c(Textured) _c(AlphaMask) + #ifndef MAGNUM_TARGET_GLES2 + _c(ObjectId) + #endif #undef _c /* LCOV_EXCL_STOP */ } @@ -146,7 +173,11 @@ Debug& operator<<(Debug& debug, const FlatFlag value) { Debug& operator<<(Debug& debug, const FlatFlags value) { return Containers::enumSetDebugOutput(debug, value, "Shaders::Flat::Flags{}", { FlatFlag::Textured, - FlatFlag::AlphaMask}); + FlatFlag::AlphaMask, + #ifndef MAGNUM_TARGET_GLES2 + FlatFlag::ObjectId + #endif + }); } } diff --git a/src/Magnum/Shaders/Flat.frag b/src/Magnum/Shaders/Flat.frag index 87ce0931f..61a576970 100644 --- a/src/Magnum/Shaders/Flat.frag +++ b/src/Magnum/Shaders/Flat.frag @@ -56,13 +56,31 @@ uniform lowp float alphaMask ; #endif +#ifdef OBJECT_ID +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 3) +#endif +/* mediump is just 2^10, which might not be enough, this is 2^16 */ +uniform highp uint objectId; /* defaults to zero */ +#endif + #ifdef TEXTURED in mediump vec2 interpolatedTextureCoordinates; #endif #ifdef NEW_GLSL +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = COLOR_OUTPUT_ATTRIBUTE_LOCATION) +#endif out lowp vec4 fragmentColor; #endif +#ifdef OBJECT_ID +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = OBJECT_ID_OUTPUT_ATTRIBUTE_LOCATION) +#endif +/* mediump is just 2^10, which might not be enough, this is 2^16 */ +out highp uint fragmentObjectId; +#endif void main() { fragmentColor = @@ -77,4 +95,8 @@ void main() { anyway. */ if(fragmentColor.a <= alphaMask) discard; #endif + + #ifdef OBJECT_ID + fragmentObjectId = objectId; + #endif } diff --git a/src/Magnum/Shaders/Flat.h b/src/Magnum/Shaders/Flat.h index d9e080e68..c67ee87a4 100644 --- a/src/Magnum/Shaders/Flat.h +++ b/src/Magnum/Shaders/Flat.h @@ -42,7 +42,10 @@ namespace Magnum { namespace Shaders { namespace Implementation { enum class FlatFlag: UnsignedByte { Textured = 1 << 0, - AlphaMask = 1 << 1 + AlphaMask = 1 << 1, + #ifndef MAGNUM_TARGET_GLES2 + ObjectId = 1 << 2 + #endif }; typedef Containers::EnumSet FlatFlags; } @@ -96,6 +99,20 @@ operation which is known to have considerable performance impact on some platforms. With proper depth sorting and blending you'll usually get much better performance and output quality. +@subsection Shaders-Flat-usage-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. Note that for +portability you should use @ref GL::Framebuffer::clearColor() instead of +@ref GL::Framebuffer::clear() as the former usually emits GL errors when called +on framebuffers with integer attachments. + +@snippet MagnumShaders.cpp Flat-usage-object-id + +@requires_gles30 Object ID output requires integer buffer attachments, which + are not available in OpenGL ES 2.0 or WebGL 1.0. + @see @ref shaders, @ref Flat2D, @ref Flat3D */ template class MAGNUM_SHADERS_EXPORT Flat: public GL::AbstractShaderProgram { @@ -118,6 +135,28 @@ template class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab */ typedef typename Generic::TextureCoordinates TextureCoordinates; + enum: UnsignedInt { + /** + * Color shader output. Present always, expects three- or + * four-component floating-point or normalized buffer attachment. + */ + ColorOutput = Generic::ColorOutput, + + #ifndef MAGNUM_TARGET_GLES2 + /** + * 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() there, see + * @ref Shaders-Phong-usage-object-id for more information. + * @requires_gles30 Object ID output requires integer buffer + * attachments, which are not available in OpenGL ES 2.0 or + * WebGL 1.0. + */ + ObjectIdOutput = Generic::ObjectIdOutput + #endif + }; + #ifdef DOXYGEN_GENERATING_OUTPUT /** * @brief Flag @@ -142,7 +181,18 @@ template class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab * with proper depth sorting and blending you'll usually get much * better performance and output quality. */ - AlphaMask = 1 << 1 + AlphaMask = 1 << 1, + + #ifndef MAGNUM_TARGET_GLES2 + /** + * Enable object ID output. See @ref Shaders-Flat-usage-object-id + * for more information. + * @requires_gles30 Object ID output requires integer buffer + * attachments, which are not available in OpenGL ES 2.0 or + * WebGL 1.0. + */ + ObjectId = 1 << 2 + #endif }; /** @@ -237,6 +287,22 @@ template class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab */ Flat& setAlphaMask(Float mask); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @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-Flat-usage-object-id for more information. Default is + * @cpp 0 @ce. + * @requires_gles30 Object ID output requires integer buffer + * attachments, which are not available in OpenGL ES 2.0 or WebGL + * 1.0. + */ + Flat& setObjectId(UnsignedInt id); + #endif + #ifdef MAGNUM_BUILD_DEPRECATED /** @brief @copybrief bindTexture() * @deprecated Use @ref bindTexture() instead. @@ -251,6 +317,9 @@ template class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab Int _transformationProjectionMatrixUniform{0}, _colorUniform{1}, _alphaMaskUniform{2}; + #ifndef MAGNUM_TARGET_GLES2 + Int _objectIdUniform{3}; + #endif }; /** @brief 2D flat shader */ diff --git a/src/Magnum/Shaders/Generic.h b/src/Magnum/Shaders/Generic.h index 6f4e4eb78..3b401e660 100644 --- a/src/Magnum/Shaders/Generic.h +++ b/src/Magnum/Shaders/Generic.h @@ -37,8 +37,8 @@ namespace Magnum { namespace Shaders { @brief Generic shader definition Definitions common for majority of shaders in the @ref Shaders namespace, -allowing mesh configured for a generic shader to be used with any of them. -See @ref shaders-generic for more information. +allowing mesh or a framebuffer configured for a generic shader to be used with +any of them. See @ref shaders-generic for more information. @see @ref shaders, @ref Generic2D, @ref Generic3D */ #ifndef DOXYGEN_GENERATING_OUTPUT @@ -47,6 +47,25 @@ template struct Generic; template struct Generic { /* Keep consistent with generic.glsl and the real definitions below */ + enum: UnsignedInt { + /** + * Color shader output. Present always, expects three- or + * four-component floating-point or normalized buffer attachment. + */ + ColorOutput = 0, + + #ifndef MAGNUM_TARGET_GLES2 + /** + * Object ID shader output. Expects a single-component unsigned + * integral attachment. + * @requires_gles30 Object ID output requires integer buffer + * attachments, which are not available in OpenGL ES 2.0 or + * WebGL 1.0. + */ + ObjectIdOutput = 1 + #endif + }; + /** * @brief Vertex position * @@ -123,6 +142,13 @@ typedef Generic<3> Generic3D; #ifndef DOXYGEN_GENERATING_OUTPUT struct BaseGeneric { + enum: UnsignedInt { + ColorOutput = 0, + #ifndef MAGNUM_TARGET_GLES2 + ObjectIdOutput = 1 + #endif + }; + typedef GL::Attribute<1, Vector2> TextureCoordinates; typedef GL::Attribute<3, Magnum::Color3> Color3; typedef GL::Attribute<3, Magnum::Color4> Color4; diff --git a/src/Magnum/Shaders/Phong.cpp b/src/Magnum/Shaders/Phong.cpp index 0c2e48b53..2128c1cb0 100644 --- a/src/Magnum/Shaders/Phong.cpp +++ b/src/Magnum/Shaders/Phong.cpp @@ -98,19 +98,24 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l .addSource(flags & Flag::SpecularTexture ? "#define SPECULAR_TEXTURE\n" : "") .addSource(flags & Flag::NormalTexture ? "#define NORMAL_TEXTURE\n" : "") .addSource(flags & Flag::AlphaMask ? "#define ALPHA_MASK\n" : "") + #ifndef MAGNUM_TARGET_GLES2 + .addSource(flags & Flag::ObjectId ? "#define OBJECT_ID\n" : "") + #endif .addSource(Utility::formatString( "#define LIGHT_COUNT {}\n" "#define LIGHT_COLORS_LOCATION {}\n", lightCount, _lightPositionsUniform + lightCount)) #ifndef MAGNUM_TARGET_GLES .addSource(std::move(lightInitializer)) #endif + .addSource(rs.get("generic.glsl")) .addSource(rs.get("Phong.frag")); CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); attachShaders({vert, frag}); - /* ES3 has this done in the shader directly */ + /* ES3 has this done in the shader directly and doesn't even provide + bindFragmentDataLocation() */ #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2) #ifndef MAGNUM_TARGET_GLES if(!GL::Context::current().isExtensionSupported(version)) @@ -122,6 +127,12 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l bindAttributeLocation(Tangent::Location, "tangent"); if(flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture)) bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags & Flag::ObjectId) { + bindFragmentDataLocation(ColorOutput, "color"); + bindFragmentDataLocation(ObjectIdOutput, "objectId"); + } + #endif } #endif @@ -139,6 +150,9 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l _specularColorUniform = uniformLocation("specularColor"); _shininessUniform = uniformLocation("shininess"); if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask"); + #ifndef MAGNUM_TARGET_GLES2 + if(flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId"); + #endif _lightPositionsUniform = uniformLocation("lightPositions"); _lightColorsUniform = uniformLocation("lightColors"); } @@ -162,12 +176,13 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l setSpecularColor(Color4{1.0f}); setShininess(80.0f); if(flags & Flag::AlphaMask) setAlphaMask(0.5f); + /* Object ID is zero by default */ setLightColors(Containers::Array{Containers::DirectInit, lightCount, Color4{1.0f}}); + /* Light position is zero by default */ setTransformationMatrix({}); setProjectionMatrix({}); setNormalMatrix({}); - /* Light position is zero by default */ #endif } @@ -213,6 +228,15 @@ Phong& Phong::setAlphaMask(Float mask) { return *this; } +#ifndef MAGNUM_TARGET_GLES2 +Phong& Phong::setObjectId(UnsignedInt id) { + CORRADE_ASSERT(_flags & Flag::ObjectId, + "Shaders::Phong::setObjectId(): the shader was not created with object ID enabled", *this); + setUniform(_objectIdUniform, id); + return *this; +} +#endif + Phong& Phong::setLightPositions(const Containers::ArrayView positions) { CORRADE_ASSERT(_lightCount == positions.size(), "Shaders::Phong::setLightPositions(): expected" << _lightCount << "items but got" << positions.size(), *this); @@ -250,6 +274,9 @@ Debug& operator<<(Debug& debug, const Phong::Flag value) { _c(SpecularTexture) _c(NormalTexture) _c(AlphaMask) + #ifndef MAGNUM_TARGET_GLES2 + _c(ObjectId) + #endif #undef _c /* LCOV_EXCL_STOP */ } @@ -263,7 +290,11 @@ Debug& operator<<(Debug& debug, const Phong::Flags value) { Phong::Flag::DiffuseTexture, Phong::Flag::SpecularTexture, Phong::Flag::NormalTexture, - Phong::Flag::AlphaMask}); + Phong::Flag::AlphaMask, + #ifndef MAGNUM_TARGET_GLES2 + Phong::Flag::ObjectId + #endif + }); } }} diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index ed6bfc4ef..abfbc3345 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -25,7 +25,7 @@ #ifndef NEW_GLSL #define in varying -#define color gl_FragColor +#define fragmentColor gl_FragColor #define texture texture2D #endif @@ -112,9 +112,17 @@ uniform lowp float alphaMask ; #endif -/* Needs to be last because it uses locations 9 + LIGHT_COUNT to - 9 + 2*LIGHT_COUNT - 1. Location 9 is lightPositions. Also it can't be - specified as 9 + LIGHT_COUNT because that requires ARB_enhanced_layouts. */ +#ifdef OBJECT_ID +#ifdef EXPLICIT_UNIFORM_LOCATION +layout(location = 9) +#endif +/* mediump is just 2^10, which might not be enough, this is 2^16 */ +uniform highp uint objectId; /* defaults to zero */ +#endif + +/* Needs to be last because it uses locations 10 + LIGHT_COUNT to + 10 + 2*LIGHT_COUNT - 1. Location 10 is lightPositions. Also it can't be + specified as 10 + LIGHT_COUNT because that requires ARB_enhanced_layouts. */ #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = LIGHT_COLORS_LOCATION) /* I fear this will blow up some drivers */ #endif @@ -136,7 +144,17 @@ in mediump vec2 interpolatedTextureCoords; #endif #ifdef NEW_GLSL -out lowp vec4 color; +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = COLOR_OUTPUT_ATTRIBUTE_LOCATION) +#endif +out lowp vec4 fragmentColor; +#endif +#ifdef OBJECT_ID +#ifdef EXPLICIT_ATTRIB_LOCATION +layout(location = OBJECT_ID_OUTPUT_ATTRIBUTE_LOCATION) +#endif +/* mediump is just 2^10, which might not be enough, this is 2^16 */ +out highp uint fragmentObjectId; #endif void main() { @@ -157,7 +175,7 @@ void main() { specularColor; /* Ambient color */ - color = finalAmbientColor; + fragmentColor = finalAmbientColor; /* Normal */ mediump vec3 normalizedTransformedNormal = normalize(transformedNormal); @@ -176,13 +194,13 @@ void main() { for(int i = 0; i < LIGHT_COUNT; ++i) { highp vec3 normalizedLightDirection = normalize(lightDirections[i]); lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection)); - color += vec4(finalDiffuseColor.rgb*lightColors[i].rgb*intensity, lightColors[i].a*finalDiffuseColor.a/float(LIGHT_COUNT)); + fragmentColor += vec4(finalDiffuseColor.rgb*lightColors[i].rgb*intensity, lightColors[i].a*finalDiffuseColor.a/float(LIGHT_COUNT)); /* Add specular color, if needed */ if(intensity > 0.001) { highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal); mediump float specularity = clamp(pow(max(0.0, dot(normalize(cameraDirection), reflection)), shininess), 0.0, 1.0); - color += vec4(finalSpecularColor.rgb*specularity, finalSpecularColor.a); + fragmentColor += vec4(finalSpecularColor.rgb*specularity, finalSpecularColor.a); } } @@ -190,6 +208,10 @@ void main() { /* Using <= because if mask is set to 1.0, it should discard all, similarly as when using 0, it should only discard what's already invisible anyway. */ - if(color.a <= alphaMask) discard; + if(fragmentColor.a <= alphaMask) discard; + #endif + + #ifdef OBJECT_ID + fragmentObjectId = objectId; #endif } diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index b2ff117b3..e25a2dcc0 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -95,6 +95,18 @@ diffuse part and then separate the alpha like this: @snippet MagnumShaders.cpp Phong-usage-alpha +@subsection Shaders-Phong-usage-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. The +functionality is practically the same as in the @ref Flat shader, see its +@ref Shaders-Flat-usage-object-id documentation for more information and usage +example. + +@requires_gles30 Object ID output requires integer buffer attachments, which + are not available in OpenGL ES 2.0 or WebGL 1.0. + @see @ref shaders */ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { @@ -134,6 +146,29 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { */ typedef Generic3D::TextureCoordinates TextureCoordinates; + enum: UnsignedInt { + /** + * Color shader output. @ref shaders-generic "Generic output", + * present always. Expects three- or four-component floating-point + * or normalized buffer attachment. + */ + ColorOutput = Generic3D::ColorOutput, + + #ifndef MAGNUM_TARGET_GLES2 + /** + * 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() there, see + * @ref Shaders-Phong-usage-object-id for more information. + * @requires_gles30 Object ID output requires integer buffer + * attachments, which are not available in OpenGL ES 2.0 or + * WebGL 1.0. + */ + ObjectIdOutput = Generic3D::ObjectIdOutput + #endif + }; + /** * @brief Flag * @@ -175,7 +210,18 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * with proper depth sorting and blending you'll usually get much * better performance and output quality. */ - AlphaMask = 1 << 3 + AlphaMask = 1 << 3, + + #ifndef MAGNUM_TARGET_GLES2 + /** + * Enable object ID output. See @ref Shaders-Phong-usage-object-id + * for more information. + * @requires_gles30 Object ID output requires integer buffer + * attachments, which are not available in OpenGL ES 2.0 or + * WebGL 1.0. + */ + ObjectId = 1 << 5 + #endif }; /** @@ -381,6 +427,22 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { */ Phong& setAlphaMask(Float mask); + #ifndef MAGNUM_TARGET_GLES2 + /** + * @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-Phong-usage-object-id for more information. Default is + * @cpp 0 @ce. + * @requires_gles30 Object ID output requires integer buffer + * attachments, which are not available in OpenGL ES 2.0 or WebGL + * 1.0. + */ + Phong& setObjectId(UnsignedInt id); + #endif + /** * @brief Set transformation matrix * @return Reference to self (for method chaining) @@ -506,9 +568,12 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { _diffuseColorUniform{5}, _specularColorUniform{6}, _shininessUniform{7}, - _alphaMaskUniform{8}, - _lightPositionsUniform{9}, - _lightColorsUniform; /* 9 + lightCount, set in the constructor */ + _alphaMaskUniform{8}; + #ifndef MAGNUM_TARGET_GLES2 + Int _objectIdUniform{9}; + #endif + Int _lightPositionsUniform{10}, + _lightColorsUniform; /* 10 + lightCount, set in the constructor */ }; /** @debugoperatorclassenum{Phong,Phong::Flag} */ diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index 082ce51f6..8a152617c 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -55,9 +55,9 @@ uniform mediump mat3 normalMatrix #endif ; -/* Needs to be last because it uses locations 9 to 9 + LIGHT_COUNT - 1 */ +/* Needs to be last because it uses locations 10 to 10 + LIGHT_COUNT - 1 */ #ifdef EXPLICIT_UNIFORM_LOCATION -layout(location = 9) +layout(location = 10) #endif uniform highp vec3 lightPositions[LIGHT_COUNT]; /* defaults to zero */ diff --git a/src/Magnum/Shaders/Test/FlatGLTest.cpp b/src/Magnum/Shaders/Test/FlatGLTest.cpp index 8577a6fcc..32649aced 100644 --- a/src/Magnum/Shaders/Test/FlatGLTest.cpp +++ b/src/Magnum/Shaders/Test/FlatGLTest.cpp @@ -64,6 +64,9 @@ struct FlatGLTest: GL::OpenGLTester { template void bindTextureNotEnabled(); template void setAlphaMaskNotEnabled(); + #ifndef MAGNUM_TARGET_GLES2 + template void setObjectIdNotEnabled(); + #endif void renderSetup(); void renderTeardown(); @@ -83,10 +86,21 @@ struct FlatGLTest: GL::OpenGLTester { void renderAlpha2D(); void renderAlpha3D(); + #ifndef MAGNUM_TARGET_GLES2 + void renderObjectIdSetup(); + void renderObjectIdTeardown(); + + void renderObjectId2D(); + void renderObjectId3D(); + #endif + private: PluginManager::Manager _manager{"nonexistent"}; GL::Renderbuffer _color{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Renderbuffer _objectId{NoCreate}; + #endif GL::Framebuffer _framebuffer{NoCreate}; }; @@ -112,7 +126,11 @@ constexpr struct { {"", {}}, {"textured", Flat2D::Flag::Textured}, {"alpha mask", Flat2D::Flag::AlphaMask}, - {"alpha mask + textured", Flat2D::Flag::AlphaMask|Flat2D::Flag::Textured} + {"alpha mask + textured", Flat2D::Flag::AlphaMask|Flat2D::Flag::Textured}, + #ifndef MAGNUM_TARGET_GLES2 + {"object ID", Flat2D::Flag::ObjectId}, + {"object ID + alpha mask + textured", Flat2D::Flag::ObjectId|Flat2D::Flag::AlphaMask|Flat2D::Flag::Textured} + #endif }; const struct { @@ -150,7 +168,12 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::bindTextureNotEnabled<2>, &FlatGLTest::bindTextureNotEnabled<3>, &FlatGLTest::setAlphaMaskNotEnabled<2>, - &FlatGLTest::setAlphaMaskNotEnabled<3>}); + &FlatGLTest::setAlphaMaskNotEnabled<3>, + #ifndef MAGNUM_TARGET_GLES2 + &FlatGLTest::setObjectIdNotEnabled<2>, + &FlatGLTest::setObjectIdNotEnabled<3> + #endif + }); addTests({&FlatGLTest::renderDefaults2D, &FlatGLTest::renderDefaults3D, @@ -169,6 +192,13 @@ FlatGLTest::FlatGLTest() { &FlatGLTest::renderAlphaSetup, &FlatGLTest::renderAlphaTeardown); + #ifndef MAGNUM_TARGET_GLES2 + addTests({&FlatGLTest::renderObjectId2D, + &FlatGLTest::renderObjectId3D}, + &FlatGLTest::renderObjectIdSetup, + &FlatGLTest::renderObjectIdTeardown); + #endif + /* Load the plugins directly from the build tree. Otherwise they're either static and already loaded or not present in the build tree */ #ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME @@ -243,6 +273,21 @@ template void FlatGLTest::setAlphaMaskNotEnabled() { "Shaders::Flat::setAlphaMask(): the shader was not created with alpha mask enabled\n"); } +#ifndef MAGNUM_TARGET_GLES2 +template void FlatGLTest::setObjectIdNotEnabled() { + setTestCaseTemplateName(std::to_string(dimensions)); + + std::ostringstream out; + Error redirectError{&out}; + + Flat shader; + shader.setObjectId(33376); + + CORRADE_COMPARE(out.str(), + "Shaders::Flat::setObjectId(): the shader was not created with object ID enabled\n"); +} +#endif + constexpr Vector2i RenderSize{80, 80}; void FlatGLTest::renderSetup() { @@ -683,6 +728,134 @@ void FlatGLTest::renderAlpha3D() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } +#ifndef MAGNUM_TARGET_GLES2 +void FlatGLTest::renderObjectIdSetup() { + GL::Renderer::enable(GL::Renderer::Feature::FaceCulling); + + _color = GL::Renderbuffer{}; + _color.setStorage(GL::RenderbufferFormat::RGBA8, RenderSize); + _objectId = GL::Renderbuffer{}; + _objectId.setStorage(GL::RenderbufferFormat::R32UI, RenderSize); + _framebuffer = GL::Framebuffer{{{}, RenderSize}}; + _framebuffer + .attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color) + .attachRenderbuffer(GL::Framebuffer::ColorAttachment{1}, _objectId) + .mapForDraw({ + {Flat3D::ColorOutput, GL::Framebuffer::ColorAttachment{0}}, + {Flat3D::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}} + }) + /* Pick a color that's directly representable on RGBA4 as well to + reduce artifacts (well, and this needs to be consistent with other + tests that *need* to run on WebGL 1) */ + .clearColor(0, 0x111111_rgbf) + .clearColor(1, Vector4ui{27}) + .bind(); +} + +void FlatGLTest::renderObjectIdTeardown() { + _color = GL::Renderbuffer{NoCreate}; + _objectId = GL::Renderbuffer{NoCreate}; + _framebuffer = GL::Framebuffer{NoCreate}; +} + +void FlatGLTest::renderObjectId2D() { + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Draw), GL::Framebuffer::Status::Complete); + + GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32)); + + Flat2D shader{Flat3D::Flag::ObjectId}; + shader.setColor(0x9999ff_rgbf) + .setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f})) + .setObjectId(47523); + + circle.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + /* Color output should have no difference -- same as in colored2D() */ + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + const Float maxThreshold = 0.0f, meanThreshold = 0.0f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 11.34f, meanThreshold = 0.51f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "FlatTestFiles/colored2D.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); + + /* Object ID -- no need to verify the whole image, just check that pixels + on known places have expected values. SwiftShader insists that the read + format has to be 32bit, so the renderbuffer format is that too to make + it the same (ES3 Mesa complains if these don't match). */ + _framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1}); + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete); + Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI}); + MAGNUM_VERIFY_NO_GL_ERROR(); + /* Outside of the object, cleared to 27 */ + CORRADE_COMPARE(image.pixels()[10][10], 27); + /* Inside of the object. Verify that it can hold 16 bits at least. */ + CORRADE_COMPARE(image.pixels()[40][46], 47523); +} + +void FlatGLTest::renderObjectId3D() { + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Draw), GL::Framebuffer::Status::Complete); + + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); + + Flat3D shader{Flat3D::Flag::ObjectId}; + shader.setColor(0x9999ff_rgbf) + .setTransformationProjectionMatrix( + Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)* + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .setObjectId(48526); + + sphere.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + /* Color output should have no difference -- same as in colored3D() */ + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has 5 different pixels on the edges */ + const Float maxThreshold = 170.0f, meanThreshold = 0.133f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 170.0f, meanThreshold = 0.456f; + #endif + _framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{0}); + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete); + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "FlatTestFiles/colored3D.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); + + /* Object ID -- no need to verify the whole image, just check that pixels + on known places have expected values. SwiftShader insists that the read + format has to be 32bit, so the renderbuffer format is that too to make + it the same (ES3 Mesa complains if these don't match). */ + _framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1}); + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete); + Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI}); + MAGNUM_VERIFY_NO_GL_ERROR(); + /* Outside of the object, cleared to 27 */ + CORRADE_COMPARE(image.pixels()[10][10], 27); + /* Inside of the object. Verify that it can hold 16 bits at least. */ + CORRADE_COMPARE(image.pixels()[40][46], 48526); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::FlatGLTest) diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 606f82712..6fe2d1531 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -65,6 +65,9 @@ struct PhongGLTest: GL::OpenGLTester { void bindTexturesNotEnabled(); void setAlphaMaskNotEnabled(); + #ifndef MAGNUM_TARGET_GLES2 + void setObjectIdNotEnabled(); + #endif void setWrongLightCount(); void setWrongLightId(); @@ -85,10 +88,20 @@ struct PhongGLTest: GL::OpenGLTester { void renderAlpha(); + #ifndef MAGNUM_TARGET_GLES2 + void renderObjectIdSetup(); + void renderObjectIdTeardown(); + + void renderObjectId(); + #endif + private: PluginManager::Manager _manager{"nonexistent"}; GL::Renderbuffer _color{NoCreate}; + #ifndef MAGNUM_TARGET_GLES2 + GL::Renderbuffer _objectId{NoCreate}; + #endif GL::Framebuffer _framebuffer{NoCreate}; }; @@ -122,7 +135,11 @@ constexpr struct { {"ambient + diffuse + specular + normal texture", Phong::Flag::AmbientTexture|Phong::Flag::DiffuseTexture|Phong::Flag::SpecularTexture|Phong::Flag::NormalTexture, 1}, {"alpha mask", Phong::Flag::AlphaMask, 1}, {"alpha mask + diffuse texture", Phong::Flag::AlphaMask|Phong::Flag::DiffuseTexture, 1}, + #ifndef MAGNUM_TARGET_GLES2 + {"object ID", Phong::Flag::ObjectId, 1}, + {"object ID + alpha mask + specular texture", Phong::Flag::ObjectId|Phong::Flag::AlphaMask|Phong::Flag::SpecularTexture, 1}, {"five lights", {}, 5} + #endif }; using namespace Math::Literals; @@ -236,6 +253,9 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::bindTexturesNotEnabled, &PhongGLTest::setAlphaMaskNotEnabled, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::setObjectIdNotEnabled, + #endif &PhongGLTest::setWrongLightCount, &PhongGLTest::setWrongLightId}); @@ -273,6 +293,12 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderAlphaSetup, &PhongGLTest::renderAlphaTeardown); + #ifndef MAGNUM_TARGET_GLES2 + addTests({&PhongGLTest::renderObjectId}, + &PhongGLTest::renderObjectIdSetup, + &PhongGLTest::renderObjectIdTeardown); + #endif + /* Load the plugins directly from the build tree. Otherwise they're either static and already loaded or not present in the build tree */ #ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME @@ -351,6 +377,19 @@ void PhongGLTest::setAlphaMaskNotEnabled() { "Shaders::Phong::setAlphaMask(): the shader was not created with alpha mask enabled\n"); } +#ifndef MAGNUM_TARGET_GLES2 +void PhongGLTest::setObjectIdNotEnabled() { + std::ostringstream out; + Error redirectError{&out}; + + Phong shader; + shader.setObjectId(33376); + + CORRADE_COMPARE(out.str(), + "Shaders::Phong::setObjectId(): the shader was not created with object ID enabled\n"); +} +#endif + void PhongGLTest::setWrongLightCount() { std::ostringstream out; Error redirectError{&out}; @@ -942,6 +981,90 @@ void PhongGLTest::renderAlpha() { (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); } +#ifndef MAGNUM_TARGET_GLES2 +void PhongGLTest::renderObjectIdSetup() { + GL::Renderer::enable(GL::Renderer::Feature::FaceCulling); + + _color = GL::Renderbuffer{}; + _color.setStorage(GL::RenderbufferFormat::RGBA8, RenderSize); + _objectId = GL::Renderbuffer{}; + _objectId.setStorage(GL::RenderbufferFormat::R32UI, RenderSize); + _framebuffer = GL::Framebuffer{{{}, RenderSize}}; + _framebuffer + .attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color) + .attachRenderbuffer(GL::Framebuffer::ColorAttachment{1}, _objectId) + .mapForDraw({ + {Phong::ColorOutput, GL::Framebuffer::ColorAttachment{0}}, + {Phong::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}} + }) + /* Pick a color that's directly representable on RGBA4 as well to + reduce artifacts (well, and this needs to be consistent with other + tests that *need* to run on WebGL 1) */ + .clearColor(0, 0x111111_rgbf) + .clearColor(1, Vector4ui{27}) + .bind(); +} + +void PhongGLTest::renderObjectIdTeardown() { + _color = GL::Renderbuffer{NoCreate}; + _objectId = GL::Renderbuffer{NoCreate}; + _framebuffer = GL::Framebuffer{NoCreate}; +} + +void PhongGLTest::renderObjectId() { + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Draw), GL::Framebuffer::Status::Complete); + + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); + + Phong shader{Phong::Flag::ObjectId, 2}; + shader.setLightColors({0x993366_rgbf, 0x669933_rgbf}) + .setLightPositions({{-3.0f, -3.0f, 0.0f}, + { 3.0f, -3.0f, 0.0f}}) + .setAmbientColor(0x330033_rgbf) + .setDiffuseColor(0xccffcc_rgbf) + .setSpecularColor(0x6666ff_rgbf) + .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .setObjectId(48526); + + sphere.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + /* Color output should have no difference -- same as in colored() */ + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* SwiftShader has some minor rounding differences (max = 1). ARM Mali G71 + has bigger rounding differences. */ + const Float maxThreshold = 8.34f, meanThreshold = 0.066f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 15.34f, meanThreshold = 3.33f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + Utility::Directory::join(SHADERS_TEST_DIR, "PhongTestFiles/colored.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); + + /* Object ID -- no need to verify the whole image, just check that pixels + on known places have expected values. SwiftShader insists that the read + format has to be 32bit, so the renderbuffer format is that too to make + it the same (ES3 Mesa complains if these don't match). */ + _framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1}); + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete); + Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI}); + MAGNUM_VERIFY_NO_GL_ERROR(); + /* Outside of the object, cleared to 27 */ + CORRADE_COMPARE(image.pixels()[10][10], 27); + /* Inside of the object. Verify that it can hold 16 bits at least. */ + CORRADE_COMPARE(image.pixels()[40][46], 48526); +} +#endif + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::PhongGLTest) diff --git a/src/Magnum/Shaders/generic.glsl b/src/Magnum/Shaders/generic.glsl index 2356c7d42..3f28ee336 100644 --- a/src/Magnum/Shaders/generic.glsl +++ b/src/Magnum/Shaders/generic.glsl @@ -30,3 +30,7 @@ #define NORMAL_ATTRIBUTE_LOCATION 2 #define COLOR_ATTRIBUTE_LOCATION 3 #define TANGENT_ATTRIBUTE_LOCATION 4 + +/* Outputs */ +#define COLOR_OUTPUT_ATTRIBUTE_LOCATION 0 +#define OBJECT_ID_OUTPUT_ATTRIBUTE_LOCATION 1