From 229641d2ac19ec020913b685872d1979da88facc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 22 Jan 2022 20:24:40 +0100 Subject: [PATCH] Shaders: support object ID textures in the PhongGL shader. --- doc/changelog.dox | 4 +- src/Magnum/Shaders/FlatGL.cpp | 2 +- src/Magnum/Shaders/Phong.frag | 18 +- src/Magnum/Shaders/Phong.h | 6 +- src/Magnum/Shaders/PhongGL.cpp | 69 +++++- src/Magnum/Shaders/PhongGL.h | 79 +++++- src/Magnum/Shaders/Test/PhongGLTest.cpp | 302 +++++++++++++++++++++-- src/Magnum/Shaders/Test/PhongGL_Test.cpp | 12 +- 8 files changed, 455 insertions(+), 37 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 13ca18fdd..edb2a8967 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -205,8 +205,8 @@ See also: updated with an introduction the new features. - @ref Shaders::FlatGL and @ref Shaders::PhongGL now support texture arrays, available also in multi-draw and instanced scenarios -- @ref Shaders::FlatGL now supports object ID textures in addition to uniform - and per-vertex object ID +- @ref Shaders::FlatGL and @ref Shaders::PhongGL now support object ID + textures in addition to uniform and per-vertex object ID - Added @ref Shaders::PhongGL::setNormalTextureScale(), consuming the recently added @ref Trade::MaterialAttribute::NormalTextureScale material attribute diff --git a/src/Magnum/Shaders/FlatGL.cpp b/src/Magnum/Shaders/FlatGL.cpp index 3a0a6aac3..c529397ef 100644 --- a/src/Magnum/Shaders/FlatGL.cpp +++ b/src/Magnum/Shaders/FlatGL.cpp @@ -52,7 +52,7 @@ namespace { enum: Int { TextureUnit = 0, /* 1/2/3 taken by Phong (D/S/N), 4 by MeshVisualizer colormap */ - ObjectIdTextureUnit = 5 + ObjectIdTextureUnit = 5 /* shared with Phong */ }; #ifndef MAGNUM_TARGET_GLES2 diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index 1c21e5b0c..018eb9b30 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -293,6 +293,19 @@ uniform lowp #endif #endif +#ifdef OBJECT_ID_TEXTURE +#ifdef EXPLICIT_BINDING +layout(binding = 5) +#endif +uniform lowp + #ifndef TEXTURE_ARRAYS + usampler2D + #else + usampler2DArray + #endif + objectIdTextureData; +#endif + /* Inputs */ #if LIGHT_COUNT @@ -308,7 +321,7 @@ in mediump vec3 transformedBitangent; in highp vec3 transformedPosition; #endif -#if defined(AMBIENT_TEXTURE) || defined(DIFFUSE_TEXTURE) || defined(SPECULAR_TEXTURE) || defined(NORMAL_TEXTURE) +#if defined(AMBIENT_TEXTURE) || defined(DIFFUSE_TEXTURE) || defined(SPECULAR_TEXTURE) || defined(NORMAL_TEXTURE) || defined(OBJECT_ID_TEXTURE) in mediump #ifndef TEXTURE_ARRAYS vec2 @@ -524,6 +537,9 @@ void main() { #ifdef INSTANCED_OBJECT_ID interpolatedInstanceObjectId + #endif + #ifdef OBJECT_ID_TEXTURE + texture(objectIdTextureData, interpolatedTextureCoordinates).r + + #endif objectId; #endif } diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index b9b76ce93..d06df0f3b 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -176,8 +176,10 @@ struct PhongDrawUniform { * is @cpp 0 @ce. * * Used only if @ref PhongGL::Flag::ObjectId is enabled, ignored otherwise. - * If @ref PhongGL::Flag::InstancedObjectId is enabled as well, this value - * is added to the ID coming from the @ref PhongGL::ObjectId attribute. + * If @ref PhongGL::Flag::InstancedObjectId and/or + * @ref PhongGL::Flag::ObjectIdTexture is enabled as well, this value is + * added to the ID coming from the @ref PhongGL::ObjectId attribute and/or + * the texture. * @see @ref PhongGL::setObjectId() */ UnsignedInt objectId; diff --git a/src/Magnum/Shaders/PhongGL.cpp b/src/Magnum/Shaders/PhongGL.cpp index 3cd3a1aa0..186d9dae9 100644 --- a/src/Magnum/Shaders/PhongGL.cpp +++ b/src/Magnum/Shaders/PhongGL.cpp @@ -57,7 +57,9 @@ namespace { AmbientTextureUnit = 0, DiffuseTextureUnit = 1, SpecularTextureUnit = 2, - NormalTextureUnit = 3 + NormalTextureUnit = 3, + /* 4 taken by MeshVisualizer colormap */ + ObjectIdTextureUnit = 5 /* shared with Flat */ }; #ifndef MAGNUM_TARGET_GLES2 @@ -87,8 +89,15 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount _lightSpecularColorsUniform{_lightPositionsUniform + 2*Int(lightCount)}, _lightRangesUniform{_lightPositionsUniform + 3*Int(lightCount)} { - CORRADE_ASSERT(!(flags & Flag::TextureTransformation) || (flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture)), - "Shaders::PhongGL: texture transformation enabled but the shader is not textured", ); + { + const bool textureTransformationNotEnabledOrTextured = !(flags & Flag::TextureTransformation) || (flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture)) + #ifndef MAGNUM_TARGET_GLES2 + || flags >= Flag::ObjectIdTexture + #endif + ; + CORRADE_ASSERT(textureTransformationNotEnabledOrTextured, + "Shaders::PhongGL: texture transformation enabled but the shader is not textured", ); + } #ifndef MAGNUM_TARGET_GLES2 CORRADE_ASSERT(!(flags >= Flag::InstancedObjectId) || !(flags & Flag::Bitangent), @@ -103,7 +112,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount #endif #ifndef MAGNUM_TARGET_GLES2 - CORRADE_ASSERT(!(flags & Flag::TextureArrays) || (flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture)), + CORRADE_ASSERT(!(flags & Flag::TextureArrays) || (flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture)) || flags >= Flag::ObjectIdTexture, "Shaders::PhongGL: texture arrays enabled but the shader is not textured", ); CORRADE_ASSERT(!(flags & Flag::UniformBuffers) || !(flags & Flag::TextureArrays) || flags >= (Flag::TextureArrays|Flag::TextureTransformation), "Shaders::PhongGL: texture arrays require texture transformation enabled as well if uniform buffers are used", ); @@ -206,7 +215,11 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount } #endif - vert.addSource(flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture) ? "#define TEXTURED\n" : "") + vert.addSource((flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture) + #ifndef MAGNUM_TARGET_GLES2 + || flags >= Flag::ObjectIdTexture + #endif + ) ? "#define TEXTURED\n" : "") .addSource(flags & Flag::NormalTexture ? "#define NORMAL_TEXTURE\n" : "") .addSource(flags & Flag::Bitangent ? "#define BITANGENT\n" : "") .addSource(flags & Flag::VertexColor ? "#define VERTEX_COLOR\n" : "") @@ -245,6 +258,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount #ifndef MAGNUM_TARGET_GLES2 .addSource(flags & Flag::ObjectId ? "#define OBJECT_ID\n" : "") .addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") + .addSource(flags >= Flag::ObjectIdTexture ? "#define OBJECT_ID_TEXTURE\n" : "") #endif .addSource(flags & Flag::NoSpecular ? "#define NO_SPECULAR\n" : "") ; @@ -301,7 +315,11 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount } if(flags & Flag::VertexColor) bindAttributeLocation(Color3::Location, "vertexColor"); /* Color4 is the same */ - if(flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture)) + if(flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture) + #ifndef MAGNUM_TARGET_GLES2 + || flags >= Flag::ObjectIdTexture + #endif + ) bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates"); #ifndef MAGNUM_TARGET_GLES2 if(flags & Flag::ObjectId) { @@ -372,6 +390,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount if(flags & Flag::NormalTexture) setUniform(uniformLocation("normalTexture"), NormalTextureUnit); } #ifndef MAGNUM_TARGET_GLES2 + if(flags >= Flag::ObjectIdTexture) setUniform(uniformLocation("objectIdTextureData"), ObjectIdTextureUnit); if(flags >= Flag::UniformBuffers) { setUniformBlockBinding(uniformBlockIndex("Projection"), ProjectionBufferBinding); setUniformBlockBinding(uniformBlockIndex("Transformation"), TransformationBufferBinding); @@ -901,6 +920,28 @@ PhongGL& PhongGL::bindNormalTexture(GL::Texture2DArray& texture) { } #endif +#ifndef MAGNUM_TARGET_GLES2 +PhongGL& PhongGL::bindObjectIdTexture(GL::Texture2D& texture) { + CORRADE_ASSERT(_flags >= Flag::ObjectIdTexture, + "Shaders::PhongGL::bindObjectIdTexture(): the shader was not created with object ID texture enabled", *this); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_ASSERT(!(_flags & Flag::TextureArrays), + "Shaders::PhongGL::bindObjectIdTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead", *this); + #endif + texture.bind(ObjectIdTextureUnit); + return *this; +} + +PhongGL& PhongGL::bindObjectIdTexture(GL::Texture2DArray& texture) { + CORRADE_ASSERT(_flags >= Flag::ObjectIdTexture, + "Shaders::PhongGL::bindObjectIdTexture(): the shader was not created with object ID texture enabled", *this); + CORRADE_ASSERT(_flags & Flag::TextureArrays, + "Shaders::PhongGL::bindObjectIdTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead", *this); + texture.bind(ObjectIdTextureUnit); + return *this; +} +#endif + PhongGL& PhongGL::bindTextures(GL::Texture2D* ambient, GL::Texture2D* diffuse, GL::Texture2D* specular, GL::Texture2D* normal) { CORRADE_ASSERT(_flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture), "Shaders::PhongGL::bindTextures(): the shader was not created with any textures enabled", *this); @@ -913,6 +954,14 @@ PhongGL& PhongGL::bindTextures(GL::Texture2D* ambient, GL::Texture2D* diffuse, G } Debug& operator<<(Debug& debug, const PhongGL::Flag value) { + #ifndef MAGNUM_TARGET_GLES2 + /* Special case coming from the Flags printer. As both flags are a superset + of ObjectId, printing just one would result in + `Flag::InstancedObjectId|Flag(0x20000)` in the output. */ + if(value == PhongGL::Flag(UnsignedInt(PhongGL::Flag::InstancedObjectId|PhongGL::Flag::ObjectIdTexture))) + return debug << PhongGL::Flag::InstancedObjectId << Debug::nospace << "|" << Debug::nospace << PhongGL::Flag::ObjectIdTexture; + #endif + debug << "Shaders::PhongGL::Flag" << Debug::nospace; switch(value) { @@ -927,8 +976,9 @@ Debug& operator<<(Debug& debug, const PhongGL::Flag value) { _c(VertexColor) _c(TextureTransformation) #ifndef MAGNUM_TARGET_GLES2 - _c(InstancedObjectId) _c(ObjectId) + _c(InstancedObjectId) + _c(ObjectIdTexture) #endif _c(InstancedTransformation) _c(InstancedTextureOffset) @@ -958,7 +1008,12 @@ Debug& operator<<(Debug& debug, const PhongGL::Flags value) { PhongGL::Flag::InstancedTextureOffset, /* Superset of TextureTransformation */ PhongGL::Flag::TextureTransformation, #ifndef MAGNUM_TARGET_GLES2 + /* Both are a superset of ObjectId, meaning printing just one would + result in `Flag::InstancedObjectId|Flag(0x20000)` in the output. So + we pass both and let the Flag printer deal with that. */ + PhongGL::Flag(UnsignedInt(PhongGL::Flag::InstancedObjectId|PhongGL::Flag::ObjectIdTexture)), PhongGL::Flag::InstancedObjectId, /* Superset of ObjectId */ + PhongGL::Flag::ObjectIdTexture, /* Superset of ObjectId */ PhongGL::Flag::ObjectId, #endif PhongGL::Flag::InstancedTransformation, diff --git a/src/Magnum/Shaders/PhongGL.h b/src/Magnum/Shaders/PhongGL.h index 93667bcbc..2a8155582 100644 --- a/src/Magnum/Shaders/PhongGL.h +++ b/src/Magnum/Shaders/PhongGL.h @@ -214,8 +214,10 @@ 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(). +and supply per-vertex IDs to the @ref ObjectId attribute. The object ID can be +also supplied from an integer texture bound via @ref bindObjectIdTexture() if +@ref Flag::ObjectIdTexture is enabled. The output will contain a sum of the +per-vertex ID, texture 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 @@ -472,8 +474,9 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * 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-PhongGL-object-id for more information. + * set in @ref setObjectId() and possibly also a per-vertex ID and + * an ID fetched from a texture, see @ref Shaders-PhongGL-object-id + * for more information. * @requires_gl30 Extension @gl_extension{EXT,texture_integer} * @requires_gles30 Object ID output requires integer support in * shaders, which is not available in OpenGL ES 2.0. @@ -585,6 +588,23 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * @m_since{2020,06} */ InstancedObjectId = (1 << 8)|ObjectId, + + /** + * Object ID texture. Retrieves object IDs from a texture bound + * with @ref bindObjectIdTexture(), outputting a sum of the object + * ID texture, the ID coming from @ref setObjectId() or + * @ref PhongDrawUniform::objectId and possibly also the per-vertex + * ID, if @ref Flag::InstancedObjectId is enabled as well. + * Implicitly enables @ref Flag::ObjectId. See + * @ref Shaders-PhongGL-object-id for more information. + * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} + * @requires_gles30 Object ID output requires integer support in + * shaders, which is not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID output requires integer support in + * shaders, which is not available in WebGL 1.0. + * @m_since_latest + */ + ObjectIdTexture = (1 << 17)|ObjectId, #endif /** @@ -976,8 +996,10 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { * Expects that the shader was created with @ref Flag::ObjectId * enabled. Value set here is written to the @ref ObjectIdOutput, see * @ref Shaders-PhongGL-object-id for more information. Default 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. + * @cpp 0 @ce. If @ref Flag::InstancedObjectId and/or + * @ref Flag::ObjectIdTexture is enabled as well, this value is added + * to the ID coming from the @ref ObjectId attribute and/or the + * texture. * * Expects that @ref Flag::UniformBuffers is not set, in that case fill * @ref PhongDrawUniform::objectId and call @ref bindDrawBuffer() @@ -1617,6 +1639,51 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram { PhongGL& bindNormalTexture(GL::Texture2DArray& texture); #endif + #ifndef MAGNUM_TARGET_GLES2 + /** + * @brief Bind an object ID texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with @ref Flag::ObjectIdTexture + * enabled. If @ref Flag::TextureArrays is enabled as well, use + * @ref bindObjectIdTexture(GL::Texture2DArray&) instead. The texture + * needs to have an unsigned integer format. + * @see @ref setObjectId(), @ref Flag::TextureTransformation, + * @ref setTextureMatrix() + * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} + * @requires_gles30 Object ID output requires integer support in + * shaders, which is not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID output requires integer support in + * shaders, which is not available in WebGL 1.0. + */ + PhongGL& bindObjectIdTexture(GL::Texture2D& texture); + + /** + * @brief Bind an object ID array texture + * @return Reference to self (for method chaining) + * @m_since_latest + * + * Expects that the shader was created with both + * @ref Flag::ObjectIdTexture and @ref Flag::TextureArrays enabled. If + * @ref Flag::UniformBuffers is not enabled, the layer is set via + * @ref setTextureLayer(); if @ref Flag::UniformBuffers is enabled, + * @ref Flag::TextureTransformation has to be enabled as well and the + * layer is set via @ref TextureTransformationUniform::layer. + * @see @ref setObjectId(), @ref Flag::TextureTransformation, + * @ref setTextureLayer() + * @requires_gl30 Extension @gl_extension{EXT,gpu_shader4} and + * @gl_extension{EXT,texture_array} + * @requires_gles30 Object ID output requires integer support in + * shaders, which is not available in OpenGL ES 2.0. Texture + * arrays are not available in OpenGL ES 2.0. + * @requires_webgl20 Object ID output requires integer support in + * shaders, which is not available in WebGL 1.0. Texture arrays + * are not available in WebGL 1.0. + */ + PhongGL& bindObjectIdTexture(GL::Texture2DArray& texture); + #endif + /** * @brief Bind textures * @return Reference to self (for method chaining) diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 1fc7e5fc5..fd15e6f08 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -235,6 +235,10 @@ constexpr struct { {"object ID + separate bitangent", PhongGL::Flag::ObjectId|PhongGL::Flag::Bitangent, 1}, {"instanced object ID", PhongGL::Flag::InstancedObjectId, 1}, {"object ID + alpha mask + specular texture", PhongGL::Flag::ObjectId|PhongGL::Flag::AlphaMask|PhongGL::Flag::SpecularTexture, 1}, + {"object ID texture", PhongGL::Flag::ObjectIdTexture, 1}, + {"instanced object ID texture array + texture transformation", PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::InstancedObjectId|PhongGL::Flag::TextureArrays|PhongGL::Flag::TextureTransformation, 1}, + {"object ID texture + diffuse texture", PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::DiffuseTexture, 1}, + {"object ID texture, zero lights", PhongGL::Flag::ObjectIdTexture, 0}, #endif {"no specular", PhongGL::Flag::NoSpecular, 1}, {"five lights", {}, 5}, @@ -269,6 +273,9 @@ constexpr struct { {"normal texture + separate bitangents", PhongGL::Flag::UniformBuffers|PhongGL::Flag::NormalTexture|PhongGL::Flag::Bitangent, 1, 1, 1}, {"alpha mask", PhongGL::Flag::UniformBuffers|PhongGL::Flag::AlphaMask, 1, 1, 1}, {"object ID", PhongGL::Flag::UniformBuffers|PhongGL::Flag::ObjectId, 1, 1, 1}, + {"object ID texture", PhongGL::Flag::ObjectIdTexture, 1, 1, 1}, + {"instanced object ID texture array + texture transformation", PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::InstancedObjectId|PhongGL::Flag::TextureArrays|PhongGL::Flag::TextureTransformation, 1, 1, 1}, + {"object ID texture + diffuse texture", PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::DiffuseTexture, 1, 1, 1}, {"no specular", PhongGL::Flag::UniformBuffers|PhongGL::Flag::NoSpecular, 1, 1, 1}, {"multidraw with all the things", PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::AmbientTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::TextureArrays|PhongGL::Flag::AlphaMask|PhongGL::Flag::ObjectId|PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::InstancedTransformation|PhongGL::Flag::InstancedObjectId|PhongGL::Flag::LightCulling, 8, 16, 24} }; @@ -280,10 +287,19 @@ constexpr struct { const char* message; } ConstructInvalidData[] { {"texture transformation but not textured", - PhongGL::Flag::TextureTransformation, + /* ObjectId shares bits with ObjectIdTexture but should still trigger + the assert */ + PhongGL::Flag::TextureTransformation + #ifndef MAGNUM_TARGET_GLES2 + |PhongGL::Flag::ObjectId + #endif + , "texture transformation enabled but the shader is not textured"}, #ifndef MAGNUM_TARGET_GLES2 - {"texture arrays but not textured", PhongGL::Flag::TextureArrays, + {"texture arrays but not textured", + /* ObjectId shares bits with ObjectIdTexture but should still trigger + the assert */ + PhongGL::Flag::TextureArrays|PhongGL::Flag::ObjectId, "texture arrays enabled but the shader is not textured"}, {"conflicting bitangent and instanced object id attribute", PhongGL::Flag::Bitangent|PhongGL::Flag::InstancedObjectId, @@ -317,18 +333,29 @@ constexpr struct { PhongGL::Flags flags; const char* message; } BindTexturesInvalidData[]{ - {"not textured", {}, + {"not textured", + PhongGL::Flags{} + #ifndef MAGNUM_TARGET_GLES2 + /* ObjectId shares bits with ObjectIdTexture but should still + trigger the assert */ + |PhongGL::Flag::ObjectId + #endif + , "Shaders::PhongGL::bindAmbientTexture(): the shader was not created with ambient texture enabled\n" "Shaders::PhongGL::bindDiffuseTexture(): the shader was not created with diffuse texture enabled\n" "Shaders::PhongGL::bindSpecularTexture(): the shader was not created with specular texture enabled\n" "Shaders::PhongGL::bindNormalTexture(): the shader was not created with normal texture enabled\n" + #ifndef MAGNUM_TARGET_GLES2 + "Shaders::PhongGL::bindObjectIdTexture(): the shader was not created with object ID texture enabled\n" + #endif "Shaders::PhongGL::bindTextures(): the shader was not created with any textures enabled\n"}, #ifndef MAGNUM_TARGET_GLES2 - {"array", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::TextureArrays, + {"array", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::TextureArrays, "Shaders::PhongGL::bindAmbientTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n" "Shaders::PhongGL::bindDiffuseTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n" "Shaders::PhongGL::bindSpecularTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n" "Shaders::PhongGL::bindNormalTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n" + "Shaders::PhongGL::bindObjectIdTexture(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n" "Shaders::PhongGL::bindTextures(): the shader was created with texture arrays enabled, use a Texture2DArray instead\n"} #endif }; @@ -339,16 +366,22 @@ constexpr struct { PhongGL::Flags flags; const char* message; } BindTextureArraysInvalidData[]{ - {"not textured", {}, + {"not textured", + /* ObjectId shares bits with ObjectIdTexture but should still trigger + the assert */ + PhongGL::Flag::ObjectId, "Shaders::PhongGL::bindAmbientTexture(): the shader was not created with ambient texture enabled\n" "Shaders::PhongGL::bindDiffuseTexture(): the shader was not created with diffuse texture enabled\n" "Shaders::PhongGL::bindSpecularTexture(): the shader was not created with specular texture enabled\n" - "Shaders::PhongGL::bindNormalTexture(): the shader was not created with normal texture enabled\n"}, - {"not array", PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture, + "Shaders::PhongGL::bindNormalTexture(): the shader was not created with normal texture enabled\n" + "Shaders::PhongGL::bindObjectIdTexture(): the shader was not created with object ID texture enabled\n"}, + {"not array", + PhongGL::Flag::AmbientTexture|PhongGL::Flag::DiffuseTexture|PhongGL::Flag::SpecularTexture|PhongGL::Flag::NormalTexture|PhongGL::Flag::ObjectIdTexture, "Shaders::PhongGL::bindAmbientTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead\n" "Shaders::PhongGL::bindDiffuseTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead\n" "Shaders::PhongGL::bindSpecularTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead\n" - "Shaders::PhongGL::bindNormalTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead\n"} + "Shaders::PhongGL::bindNormalTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead\n" + "Shaders::PhongGL::bindObjectIdTexture(): the shader was not created with texture arrays enabled, use a Texture2D instead\n"} }; #endif @@ -662,6 +695,39 @@ const struct { 1.0f, 0.0f, {}} }; +#ifndef MAGNUM_TARGET_GLES2 +const struct { + const char* name; + UnsignedInt expected[4]; + PhongGL::Flags flags; + Matrix3 textureTransformation; + Int layer; +} RenderObjectIdData[]{ + {"", + {40006, 40006, 40006, 40006}, + {}, {}, 0}, + {"textured", + {40106, 40206, 40306, 40406}, + PhongGL::Flag::ObjectIdTexture, {}, 0}, + {"textured, texture transformation", + {40406, 40306, 40206, 40106}, + PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::TextureTransformation, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), 0}, + {"texture array, first layer", + {40106, 40206, 40306, 40406}, + PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::TextureArrays, + {}, 0}, + {"texture array, arbitrary layer", + {40106, 40206, 40306, 40406}, + PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::TextureArrays, + {}, 6}, + {"texture array, texture transformation, arbitrary layer", + {40406, 40306, 40206, 40106}, + PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::TextureTransformation|PhongGL::Flag::TextureArrays, + Matrix3::translation(Vector2{1.0f})*Matrix3::scaling(Vector2{-1.0f}), 6}, +}; +#endif + constexpr struct { const char* name; const char* expected; @@ -685,6 +751,21 @@ constexpr struct { PhongGL::Flag::InstancedObjectId, /* Minor differences on SwiftShader */ 81.0f, 0.06f}, + {"diffuse color + textured object ID", + "instanced.tga", {3000, 4000, 5000}, + PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::InstancedTextureOffset, + /* Minor differences on SwiftShader */ + 81.0f, 0.06f}, + {"diffuse color + instanced textured object ID", + "instanced.tga", {3211, 8627, 40363}, + PhongGL::Flag::InstancedObjectId|PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::InstancedTextureOffset, + /* Minor differences on SwiftShader */ + 81.0f, 0.06f}, + {"diffuse color + instanced texture array object ID", + "instanced.tga", {3211, 8627, 40363}, + PhongGL::Flag::InstancedObjectId|PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::InstancedTextureOffset|PhongGL::Flag::TextureArrays, + /* Minor differences on SwiftShader */ + 81.0f, 0.06f}, #endif {"diffuse texture", "instanced-textured.tga", {}, @@ -725,6 +806,18 @@ constexpr struct { 2, 1, 1, 16, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, + {"bind with offset, colored + textured object ID", + "multidraw.tga", {3211, 8627, 40363}, + PhongGL::Flag::TextureTransformation|PhongGL::Flag::ObjectIdTexture, + 2, 1, 1, 16, + /* Minor differences on ARM Mali */ + 3.34f, 0.01f}, + {"bind with offset, colored + textured array object ID", + "multidraw.tga", {3211, 8627, 40363}, + PhongGL::Flag::TextureTransformation|PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::TextureArrays, + 2, 1, 1, 16, + /* Minor differences on ARM Mali */ + 3.34f, 0.01f}, {"bind with offset, textured", "multidraw-textured.tga", {}, PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture, @@ -750,6 +843,18 @@ constexpr struct { 4, 2, 3, 1, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, + {"draw offset, colored + textured object ID", + "multidraw.tga", {3211, 8627, 40363}, + PhongGL::Flag::TextureTransformation|PhongGL::Flag::ObjectIdTexture, + 4, 2, 3, 1, + /* Minor differences on ARM Mali */ + 3.34f, 0.01f}, + {"draw offset, colored + textured array object ID", + "multidraw.tga", {3211, 8627, 40363}, + PhongGL::Flag::TextureTransformation|PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::TextureArrays, + 4, 2, 3, 1, + /* Minor differences on ARM Mali */ + 3.34f, 0.01f}, {"draw offset, textured", "multidraw-textured.tga", {}, PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture, @@ -775,6 +880,18 @@ constexpr struct { 4, 2, 3, 1, /* Minor differences on ARM Mali */ 3.34f, 0.01f}, + {"multidraw, colored + textured object ID", + "multidraw.tga", {3211, 8627, 40363}, + PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::ObjectIdTexture, + 4, 2, 3, 1, + /* Minor differences on ARM Mali */ + 3.34f, 0.01f}, + {"multidraw, colored + textured array object ID", + "multidraw.tga", {3211, 8627, 40363}, + PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::ObjectIdTexture|PhongGL::Flag::TextureArrays, + 4, 2, 3, 1, + /* Minor differences on ARM Mali */ + 3.34f, 0.01f}, {"multidraw, textured", "multidraw-textured.tga", {}, PhongGL::Flag::MultiDraw|PhongGL::Flag::TextureTransformation|PhongGL::Flag::DiffuseTexture, @@ -943,9 +1060,10 @@ PhongGLTest::PhongGLTest() { #ifndef MAGNUM_TARGET_GLES2 /* MSVC needs explicit type due to default template args */ - addTests({ + addInstancedTests({ &PhongGLTest::renderObjectId, &PhongGLTest::renderObjectId}, + Containers::arraySize(RenderObjectIdData), &PhongGLTest::renderObjectIdSetup, &PhongGLTest::renderObjectIdTeardown); #endif @@ -1314,6 +1432,9 @@ void PhongGLTest::bindTexturesInvalid() { .bindDiffuseTexture(texture) .bindSpecularTexture(texture) .bindNormalTexture(texture) + #ifndef MAGNUM_TARGET_GLES2 + .bindObjectIdTexture(texture) + #endif .bindTextures(&texture, &texture, &texture, &texture); CORRADE_COMPARE(out.str(), data.message); @@ -1341,7 +1462,11 @@ void PhongGLTest::bindTextureArraysInvalid() { shader.bindAmbientTexture(textureArray) .bindDiffuseTexture(textureArray) .bindSpecularTexture(textureArray) - .bindNormalTexture(textureArray); + .bindNormalTexture(textureArray) + #ifndef MAGNUM_TARGET_GLES2 + .bindObjectIdTexture(textureArray) + #endif + ; CORRADE_COMPARE(out.str(), data.message); } @@ -2824,6 +2949,9 @@ void PhongGLTest::renderObjectIdTeardown() { } template void PhongGLTest::renderObjectId() { + auto&& data = RenderObjectIdData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + if(flag == PhongGL::Flag::UniformBuffers) { setTestCaseTemplateName("Flag::UniformBuffers"); @@ -2845,11 +2973,50 @@ template void PhongGLTest::renderObjectId() { CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Draw), GL::Framebuffer::Status::Complete); - GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32)); + Primitives::UVSphereFlags sphereFlags; + if(data.flags & PhongGL::Flag::ObjectIdTexture) + sphereFlags |= Primitives::UVSphereFlag::TextureCoordinates; + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, sphereFlags)); + + PhongGL::Flags flags = data.flags|flag; + if(flag == PhongGL::Flag::UniformBuffers && (data.flags & PhongGL::Flag::TextureArrays) && !(data.flags & PhongGL::Flag::TextureTransformation)) { + CORRADE_INFO("Texture arrays currently require texture transformation if UBOs are used, enabling implicitly."); + flags |= PhongGL::Flag::TextureTransformation; + } + PhongGL shader{PhongGL::Flag::ObjectId|flags, 2}; - PhongGL shader{PhongGL::Flag::ObjectId|flag, 2}; + GL::Texture2D texture{NoCreate}; + GL::Texture2DArray textureArray{NoCreate}; + if(data.flags >= PhongGL::Flag::ObjectIdTexture) { + const UnsignedShort imageData[]{ + 100, 200, 300, 400 + }; + ImageView2D image{PixelFormat::R16UI, {2, 2}, imageData}; + + if(data.flags & PhongGL::Flag::TextureArrays) { + textureArray = GL::Texture2DArray{}; + textureArray.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, {image.size(), data.layer + 1}) + .setSubImage(0, {0, 0, data.layer}, image); + shader.bindObjectIdTexture(textureArray); + if(flag != PhongGL::Flag::UniformBuffers && data.layer != 0) + shader.setTextureLayer(data.layer); /* to verify the default */ + } else { + texture = GL::Texture2D{}; + texture.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(texture); + } + } if(flag == PhongGL::Flag{}) { + if(data.textureTransformation != Matrix3{}) + shader.setTextureMatrix(data.textureTransformation); shader .setLightColors({0x993366_rgbf, 0x669933_rgbf}) .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, @@ -2859,7 +3026,7 @@ template void PhongGLTest::renderObjectId() { .setSpecularColor(0x6666ff_rgbf) .setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f))) .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) - .setObjectId(48526) + .setObjectId(40006) .draw(sphere); } else if(flag == PhongGL::Flag::UniformBuffers) { GL::Buffer projectionUniform{GL::Buffer::TargetHint::Uniform, { @@ -2874,7 +3041,7 @@ template void PhongGLTest::renderObjectId() { }}; GL::Buffer drawUniform{GL::Buffer::TargetHint::Uniform, { PhongDrawUniform{} - .setObjectId(48526) + .setObjectId(40006) }}; GL::Buffer lightUniform{GL::Buffer::TargetHint::Uniform, { PhongLightUniform{} @@ -2884,12 +3051,21 @@ template void PhongGLTest::renderObjectId() { .setPosition({3.0f, -3.0f, 2.0f, 0.0f}) .setColor(0x669933_rgbf) }}; + GL::Buffer textureTransformationUniform{GL::Buffer::TargetHint::Uniform, { + TextureTransformationUniform{} + .setTextureMatrix(data.textureTransformation) + .setLayer(data.layer) + }}; GL::Buffer materialUniform{GL::Buffer::TargetHint::Uniform, { PhongMaterialUniform{} .setAmbientColor(0x330033_rgbf) .setDiffuseColor(0xccffcc_rgbf) .setSpecularColor(0x6666ff_rgbf) }}; + /* Also take into account the case when texture transform needs to be + enabled for texture arrays, so not data.flags but flags */ + if(flags & PhongGL::Flag::TextureTransformation) + shader.bindTextureTransformationBuffer(textureTransformationUniform); shader.bindProjectionBuffer(projectionUniform) .bindTransformationBuffer(transformationUniform) .bindDrawBuffer(drawUniform) @@ -2924,8 +3100,13 @@ template void PhongGLTest::renderObjectId() { MAGNUM_VERIFY_NO_GL_ERROR(); /* Outside of the object, cleared to 27 */ CORRADE_COMPARE(image.pixels()[10][10], 27); - /* Inside of the object */ - CORRADE_COMPARE(image.pixels()[40][46], 48526); + /* Inside of the object. It's a sphere and the seam is at the front, + rotated to bottom left, meaning left is actually the right part of the + texture and right is the left part of the texture. */ + CORRADE_COMPARE(image.pixels()[20][50], data.expected[0]); + CORRADE_COMPARE(image.pixels()[20][20], data.expected[1]); + CORRADE_COMPARE(image.pixels()[50][50], data.expected[2]); + CORRADE_COMPARE(image.pixels()[50][20], data.expected[3]); } #endif @@ -3589,6 +3770,53 @@ template void PhongGLTest::renderInstanced() { shader.bindNormalTexture(normal); } } + + #ifndef MAGNUM_TARGET_GLES2 + GL::Texture2D objectIdTexture{NoCreate}; + GL::Texture2DArray objectIdTextureArray{NoCreate}; + if(data.flags >= PhongGL::Flag::ObjectIdTexture) { + /* This should match transformation done for the diffuse/normal + texture */ + if(data.flags & PhongGL::Flag::TextureArrays) { + /* 2 extra slices as a base offset, each slice has half height, + second slice has the data in the right half */ + const UnsignedShort imageData[]{ + 0, 0, + 0, 0, + 2000, 0, + 0, 3000, + 4000, 0 + }; + ImageView3D image{PixelFormat::R16UI, {2, 1, 5}, imageData}; + + objectIdTextureArray = GL::Texture2DArray{}; + objectIdTextureArray.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(objectIdTextureArray); + } else { + /* First is taken from bottom left, second from bottom right, third + from top center (there I just duplicate the pixel on both + sides) */ + const UnsignedShort imageData[]{ + 2000, 3000, + 4000, 4000 + }; + ImageView2D image{PixelFormat::R16UI, {2, 2}, imageData}; + + objectIdTexture = GL::Texture2D{}; + objectIdTexture.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(objectIdTexture); + } + } + #endif + if(flag == PhongGL::Flag{}) { shader .setLightPositions({{-3.0f, -3.0f, 2.0f, 0.0f}, @@ -3823,6 +4051,48 @@ void PhongGLTest::renderMulti() { } } + GL::Texture2D objectIdTexture{NoCreate}; + GL::Texture2DArray objectIdTextureArray{NoCreate}; + if(data.flags >= PhongGL::Flag::ObjectIdTexture) { + /* This should match transformation done for the diffuse/normal + texture */ + if(data.flags & PhongGL::Flag::TextureArrays) { + /* Each slice has half height, second slice has the data in the + right half */ + const UnsignedShort imageData[]{ + 2000, 0, + 0, 3000, + 4000, 0 + }; + ImageView3D image{PixelFormat::R16UI, {2, 1, 3}, imageData}; + + objectIdTextureArray = GL::Texture2DArray{}; + objectIdTextureArray.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(objectIdTextureArray); + } else { + /* First is taken from bottom left, second from bottom right, third + from top center (there I just duplicate the pixel on both + sides) */ + const UnsignedShort imageData[]{ + 2000, 3000, + 4000, 4000 + }; + ImageView2D image{PixelFormat::R16UI, {2, 2}, imageData}; + + objectIdTexture = GL::Texture2D{}; + objectIdTexture.setMinificationFilter(GL::SamplerFilter::Nearest) + .setMagnificationFilter(GL::SamplerFilter::Nearest) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, GL::TextureFormat::R16UI, image.size()) + .setSubImage(0, {}, image); + shader.bindObjectIdTexture(objectIdTexture); + } + } + Trade::MeshData sphereData = Primitives::uvSphereSolid(16, 32, Primitives::UVSphereFlag::TextureCoordinates| Primitives::UVSphereFlag::Tangents); diff --git a/src/Magnum/Shaders/Test/PhongGL_Test.cpp b/src/Magnum/Shaders/Test/PhongGL_Test.cpp index 07f1f9ea9..6b833c838 100644 --- a/src/Magnum/Shaders/Test/PhongGL_Test.cpp +++ b/src/Magnum/Shaders/Test/PhongGL_Test.cpp @@ -85,12 +85,20 @@ void PhongGL_Test::debugFlags() { void PhongGL_Test::debugFlagsSupersets() { #ifndef MAGNUM_TARGET_GLES2 - /* InstancedObjectId is a superset of ObjectId so only one should be - printed */ + /* InstancedObjectId and ObjectIdTexture are a superset of ObjectId so only + one should be printed, but if there are both then both should be */ { std::ostringstream out; Debug{&out} << (PhongGL::Flag::ObjectId|PhongGL::Flag::InstancedObjectId); CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::InstancedObjectId\n"); + } { + std::ostringstream out; + Debug{&out} << (PhongGL::Flag::ObjectId|PhongGL::Flag::ObjectIdTexture); + CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::ObjectIdTexture\n"); + } { + std::ostringstream out; + Debug{&out} << (PhongGL::Flag::ObjectId|PhongGL::Flag::InstancedObjectId|PhongGL::Flag::ObjectIdTexture); + CORRADE_COMPARE(out.str(), "Shaders::PhongGL::Flag::InstancedObjectId|Shaders::PhongGL::Flag::ObjectIdTexture\n"); } #endif