From a3c8d8ec636e20f27c89d4b540d1bb8c43010b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 12 Aug 2019 13:06:10 +0200 Subject: [PATCH] Shaders: allow Phong with zero lights. Which makes it equivalent to Flat3D. Useful to reduce complexity in apps that render models with pre-baked lighting. --- doc/changelog.dox | 3 + src/Magnum/Shaders/Flat.h | 7 +- src/Magnum/Shaders/Phong.cpp | 100 ++++++++++++---------- src/Magnum/Shaders/Phong.frag | 10 +++ src/Magnum/Shaders/Phong.h | 42 ++++++--- src/Magnum/Shaders/Phong.vert | 10 +++ src/Magnum/Shaders/Test/CMakeLists.txt | 5 +- src/Magnum/Shaders/Test/PhongGLTest.cpp | 108 +++++++++++++++++++++++- 8 files changed, 224 insertions(+), 61 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index aac749448..bf83773c6 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -346,6 +346,9 @@ See also: - @ref Shaders::Phong now clamps the specular factor to minimize artifacts when shininess is near zero +- @ref Shaders::Phong can now handle zero lights, in which case its output is + equivalent to @ref Shaders::Flat3D. See @ref Shaders-Phong-zero-lights for + more information. @subsubsection changelog-latest-changes-texturetools TextureTools library diff --git a/src/Magnum/Shaders/Flat.h b/src/Magnum/Shaders/Flat.h index ca8edc278..9d82dd7cc 100644 --- a/src/Magnum/Shaders/Flat.h +++ b/src/Magnum/Shaders/Flat.h @@ -65,10 +65,13 @@ and then at render time don't forget to bind also the texture via @ref bindTexture(). The texture is multipled by the color, which is by default set to @cpp 0xffffffff_rgbaf @ce. -For coloring the texture based on intensity you can use the @ref Vector shader. - @image html shaders-flat.png width=256px +For coloring the texture based on intensity you can use the @ref Vector shader. +The 3D version of this shader is equivalent to @ref Phong with zero lights, +however this implementation is much simpler and thus likely also faster. See +@ref Shaders-Phong-zero-lights "its documentation" for more information. + @section Shaders-Flat-usage Example usage @subsection Shaders-Flat-usage-colored Colored mesh diff --git a/src/Magnum/Shaders/Phong.cpp b/src/Magnum/Shaders/Phong.cpp index 2128c1cb0..148042a14 100644 --- a/src/Magnum/Shaders/Phong.cpp +++ b/src/Magnum/Shaders/Phong.cpp @@ -69,23 +69,25 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment); #ifndef MAGNUM_TARGET_GLES - /* Initializer for the light color array -- we need a list of vec4(1.0) - joined by commas. For GLES we'll simply upload the values directly. */ - constexpr const char lightInitializerPreamble[] = "#define LIGHT_COLOR_INITIALIZER "; - constexpr std::size_t lightInitializerPreambleSize = - Containers::arraySize(lightInitializerPreamble) - 1; - constexpr const char lightInitializerItem[] = "vec4(1.0), "; - constexpr std::size_t lightInitializerItemSize = - Containers::arraySize(lightInitializerItem) - 1; std::string lightInitializer; - lightInitializer.reserve(Containers::arraySize(lightInitializerPreamble) - 1 + lightCount*lightInitializerItemSize); - lightInitializer.append(lightInitializerPreamble, lightInitializerPreambleSize); - for(std::size_t i = 0; i != lightCount; ++i) - lightInitializer.append(lightInitializerItem, lightInitializerItemSize); - - /* Drop the last comma and add a newline at the end */ - lightInitializer[lightInitializer.size() - 2] = '\n'; - lightInitializer.resize(lightInitializer.size() - 1); + if(lightCount) { + /* Initializer for the light color array -- we need a list of vec4(1.0) + joined by commas. For GLES we'll simply upload the values directly. */ + constexpr const char lightInitializerPreamble[] = "#define LIGHT_COLOR_INITIALIZER "; + constexpr std::size_t lightInitializerPreambleSize = + Containers::arraySize(lightInitializerPreamble) - 1; + constexpr const char lightInitializerItem[] = "vec4(1.0), "; + constexpr std::size_t lightInitializerItemSize = + Containers::arraySize(lightInitializerItem) - 1; + lightInitializer.reserve(Containers::arraySize(lightInitializerPreamble) - 1 + lightCount*lightInitializerItemSize); + lightInitializer.append(lightInitializerPreamble, lightInitializerPreambleSize); + for(std::size_t i = 0; i != lightCount; ++i) + lightInitializer.append(lightInitializerItem, lightInitializerItemSize); + + /* Drop the last comma and add a newline at the end */ + lightInitializer[lightInitializer.size() - 2] = '\n'; + lightInitializer.resize(lightInitializer.size() - 1); + } #endif vert.addSource(flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture|Flag::NormalTexture) ? "#define TEXTURED\n" : "") @@ -103,11 +105,11 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l #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")) + "#define LIGHT_COLORS_LOCATION {}\n", lightCount, _lightPositionsUniform + lightCount)); + #ifndef MAGNUM_TARGET_GLES + if(lightCount) frag.addSource(std::move(lightInitializer)); + #endif + frag.addSource(rs.get("generic.glsl")) .addSource(rs.get("Phong.frag")); CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); @@ -122,8 +124,9 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l #endif { bindAttributeLocation(Position::Location, "position"); - bindAttributeLocation(Normal::Location, "normal"); - if(flags & Flag::NormalTexture) + if(lightCount) + bindAttributeLocation(Normal::Location, "normal"); + if((flags & Flag::NormalTexture) && lightCount) bindAttributeLocation(Tangent::Location, "tangent"); if(flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture)) bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates"); @@ -144,17 +147,19 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l { _transformationMatrixUniform = uniformLocation("transformationMatrix"); _projectionMatrixUniform = uniformLocation("projectionMatrix"); - _normalMatrixUniform = uniformLocation("normalMatrix"); _ambientColorUniform = uniformLocation("ambientColor"); - _diffuseColorUniform = uniformLocation("diffuseColor"); - _specularColorUniform = uniformLocation("specularColor"); - _shininessUniform = uniformLocation("shininess"); + if(lightCount) { + _normalMatrixUniform = uniformLocation("normalMatrix"); + _diffuseColorUniform = uniformLocation("diffuseColor"); + _specularColorUniform = uniformLocation("specularColor"); + _shininessUniform = uniformLocation("shininess"); + _lightPositionsUniform = uniformLocation("lightPositions"); + _lightColorsUniform = uniformLocation("lightColors"); + } 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"); } #ifndef MAGNUM_TARGET_GLES @@ -162,9 +167,11 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l #endif { if(flags & Flag::AmbientTexture) setUniform(uniformLocation("ambientTexture"), AmbientTextureLayer); - if(flags & Flag::DiffuseTexture) setUniform(uniformLocation("diffuseTexture"), DiffuseTextureLayer); - if(flags & Flag::SpecularTexture) setUniform(uniformLocation("specularTexture"), SpecularTextureLayer); - if(flags & Flag::NormalTexture) setUniform(uniformLocation("normalTexture"), NormalTextureLayer); + if(lightCount) { + if(flags & Flag::DiffuseTexture) setUniform(uniformLocation("diffuseTexture"), DiffuseTextureLayer); + if(flags & Flag::SpecularTexture) setUniform(uniformLocation("specularTexture"), SpecularTextureLayer); + if(flags & Flag::NormalTexture) setUniform(uniformLocation("normalTexture"), NormalTextureLayer); + } } /* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */ @@ -172,17 +179,18 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l /* Default to fully opaque white so we can see the textures */ if(flags & Flag::AmbientTexture) setAmbientColor(Color4{1.0f}); else setAmbientColor(Color4{0.0f}); - setDiffuseColor(Color4{1.0f}); - 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({}); + if(lightCount) { + setDiffuseColor(Color4{1.0f}); + 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 */ + setNormalMatrix({}); + } #endif } @@ -196,21 +204,21 @@ Phong& Phong::bindAmbientTexture(GL::Texture2D& texture) { Phong& Phong::bindDiffuseTexture(GL::Texture2D& texture) { CORRADE_ASSERT(_flags & Flag::DiffuseTexture, "Shaders::Phong::bindDiffuseTexture(): the shader was not created with diffuse texture enabled", *this); - texture.bind(DiffuseTextureLayer); + if(_lightCount) texture.bind(DiffuseTextureLayer); return *this; } Phong& Phong::bindSpecularTexture(GL::Texture2D& texture) { CORRADE_ASSERT(_flags & Flag::SpecularTexture, "Shaders::Phong::bindSpecularTexture(): the shader was not created with specular texture enabled", *this); - texture.bind(SpecularTextureLayer); + if(_lightCount) texture.bind(SpecularTextureLayer); return *this; } Phong& Phong::bindNormalTexture(GL::Texture2D& texture) { CORRADE_ASSERT(_flags & Flag::NormalTexture, "Shaders::Phong::bindNormalTexture(): the shader was not created with normal texture enabled", *this); - texture.bind(NormalTextureLayer); + if(_lightCount) texture.bind(NormalTextureLayer); return *this; } @@ -240,7 +248,7 @@ Phong& Phong::setObjectId(UnsignedInt id) { Phong& Phong::setLightPositions(const Containers::ArrayView positions) { CORRADE_ASSERT(_lightCount == positions.size(), "Shaders::Phong::setLightPositions(): expected" << _lightCount << "items but got" << positions.size(), *this); - setUniform(_lightPositionsUniform, positions); + if(_lightCount) setUniform(_lightPositionsUniform, positions); return *this; } @@ -254,7 +262,7 @@ Phong& Phong::setLightPosition(UnsignedInt id, const Vector3& position) { Phong& Phong::setLightColors(const Containers::ArrayView colors) { CORRADE_ASSERT(_lightCount == colors.size(), "Shaders::Phong::setLightColors(): expected" << _lightCount << "items but got" << colors.size(), *this); - setUniform(_lightColorsUniform, colors); + if(_lightCount) setUniform(_lightColorsUniform, colors); return *this; } diff --git a/src/Magnum/Shaders/Phong.frag b/src/Magnum/Shaders/Phong.frag index abfbc3345..84e3233f5 100644 --- a/src/Magnum/Shaders/Phong.frag +++ b/src/Magnum/Shaders/Phong.frag @@ -53,6 +53,7 @@ uniform lowp vec4 ambientColor #endif ; +#if LIGHT_COUNT #ifdef DIFFUSE_TEXTURE #ifdef EXPLICIT_TEXTURE_LAYER layout(binding = 1) @@ -100,6 +101,7 @@ uniform mediump float shininess = 80.0 #endif ; +#endif #ifdef ALPHA_MASK #ifdef EXPLICIT_UNIFORM_LOCATION @@ -120,6 +122,7 @@ layout(location = 9) uniform highp uint objectId; /* defaults to zero */ #endif +#if LIGHT_COUNT /* 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. */ @@ -131,13 +134,16 @@ uniform lowp vec4 lightColors[LIGHT_COUNT] = vec4[](LIGHT_COLOR_INITIALIZER) #endif ; +#endif +#if LIGHT_COUNT in mediump vec3 transformedNormal; #ifdef NORMAL_TEXTURE in mediump vec3 transformedTangent; #endif in highp vec3 lightDirections[LIGHT_COUNT]; in highp vec3 cameraDirection; +#endif #if defined(AMBIENT_TEXTURE) || defined(DIFFUSE_TEXTURE) || defined(SPECULAR_TEXTURE) || defined(NORMAL_TEXTURE) in mediump vec2 interpolatedTextureCoords; @@ -163,6 +169,7 @@ void main() { texture(ambientTexture, interpolatedTextureCoords)* #endif ambientColor; + #if LIGHT_COUNT lowp const vec4 finalDiffuseColor = #ifdef DIFFUSE_TEXTURE texture(diffuseTexture, interpolatedTextureCoords)* @@ -173,10 +180,12 @@ void main() { texture(specularTexture, interpolatedTextureCoords)* #endif specularColor; + #endif /* Ambient color */ fragmentColor = finalAmbientColor; + #if LIGHT_COUNT /* Normal */ mediump vec3 normalizedTransformedNormal = normalize(transformedNormal); #ifdef NORMAL_TEXTURE @@ -203,6 +212,7 @@ void main() { fragmentColor += vec4(finalSpecularColor.rgb*specularity, finalSpecularColor.a); } } + #endif #ifdef ALPHA_MASK /* Using <= because if mask is set to 1.0, it should discard all, similarly diff --git a/src/Magnum/Shaders/Phong.h b/src/Magnum/Shaders/Phong.h index b8b695295..377430bb6 100644 --- a/src/Magnum/Shaders/Phong.h +++ b/src/Magnum/Shaders/Phong.h @@ -107,6 +107,14 @@ example. @requires_gles30 Object ID output requires integer buffer attachments, which are not available in OpenGL ES 2.0 or WebGL 1.0. +@section Shaders-Phong-zero-lights Zero lights + +Creating this shader with zero lights makes its output equivalent to the +@ref Flat3D shader --- only @ref setAmbientColor() and @ref bindAmbientTexture() +(if @ref Flag::AmbientTexture is enabled) are taken into account, which +correspond to @ref Flat::setColor() and @ref Flat::bindTexture(). This is +useful to reduce complexity in apps that render models with pre-baked lights. + @see @ref shaders */ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { @@ -305,11 +313,13 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * @brief Set diffuse color * @return Reference to self (for method chaining) * - * Initial value is @cpp 0xffffffff_rgbaf @ce. + * Initial value is @cpp 0xffffffff_rgbaf @ce. If @ref lightCount() is + * zero, this function is a no-op, as diffuse color doesn't contribute + * to the output in that case. * @see @ref bindDiffuseTexture() */ Phong& setDiffuseColor(const Color4& color) { - setUniform(_diffuseColorUniform, color); + if(_lightCount) setUniform(_diffuseColorUniform, color); return *this; } @@ -318,7 +328,8 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * @return Reference to self (for method chaining) * * Expects that the shader was created with @ref Flag::DiffuseTexture - * enabled. + * enabled. If @ref lightCount() is zero, this function is a no-op, as + * diffuse color doesn't contribute to the output in that case. * @see @ref bindTextures(), @ref setDiffuseColor() */ Phong& bindDiffuseTexture(GL::Texture2D& texture); @@ -337,7 +348,9 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * @return Reference to self (for method chaining) * * Expects that the shader was created with @ref Flag::NormalTexture - * enabled and the @ref Tangent attribute was supplied. + * enabled and the @ref Tangent attribute was supplied. If + * @ref lightCount() is zero, this function is a no-op, as normals + * dosn't contribute to the output in that case. * @see @ref bindTextures() */ Phong& bindNormalTexture(GL::Texture2D& texture); @@ -349,11 +362,13 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * Initial value is @cpp 0xffffffff_rgbaf @ce. Color will be multiplied * with specular texture if @ref Flag::SpecularTexture is set. If you * want to have a fully diffuse material, set specular color to - * @cpp 0x000000ff_rgbaf @ce. + * @cpp 0x000000ff_rgbaf @ce. If @ref lightCount() is zero, this + * function is a no-op, as specular color doesn't contribute to the + * output in that case. * @see @ref bindSpecularTexture() */ Phong& setSpecularColor(const Color4& color) { - setUniform(_specularColorUniform, color); + if(_lightCount) setUniform(_specularColorUniform, color); return *this; } @@ -362,7 +377,8 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * @return Reference to self (for method chaining) * * Expects that the shader was created with @ref Flag::SpecularTexture - * enabled. + * enabled. If @ref lightCount() is zero, this function is a no-op, as + * specular color doesn't contribute to the output in that case. * @see @ref bindTextures(), @ref setSpecularColor() */ Phong& bindSpecularTexture(GL::Texture2D& texture); @@ -409,10 +425,12 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * @return Reference to self (for method chaining) * * The larger value, the harder surface (smaller specular highlight). - * Initial value is @cpp 80.0f @ce. + * Initial value is @cpp 80.0f @ce. If @ref lightCount() is zero, this + * function is a no-op, as specular color doesn't contribute to the + * output in that case. */ Phong& setShininess(Float shininess) { - setUniform(_shininessUniform, shininess); + if(_lightCount) setUniform(_shininessUniform, shininess); return *this; } @@ -462,10 +480,12 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram { * The matrix doesn't need to be normalized, as the renormalization * must be done in the shader anyway. You need to set also * @ref setTransformationMatrix() with a corresponding value. Initial - * value is an identity matrix. + * value is an identity matrix. If @ref lightCount() is zero, this + * function is a no-op, as normals don't contribute to the output in + * that case. */ Phong& setNormalMatrix(const Matrix3x3& matrix) { - setUniform(_normalMatrixUniform, matrix); + if(_lightCount) setUniform(_normalMatrixUniform, matrix); return *this; } diff --git a/src/Magnum/Shaders/Phong.vert b/src/Magnum/Shaders/Phong.vert index 8a152617c..e57662ec0 100644 --- a/src/Magnum/Shaders/Phong.vert +++ b/src/Magnum/Shaders/Phong.vert @@ -46,6 +46,7 @@ uniform highp mat4 projectionMatrix #endif ; +#if LIGHT_COUNT #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 2) #endif @@ -54,18 +55,22 @@ uniform mediump mat3 normalMatrix = mat3(1.0) #endif ; +#endif +#if LIGHT_COUNT /* Needs to be last because it uses locations 10 to 10 + LIGHT_COUNT - 1 */ #ifdef EXPLICIT_UNIFORM_LOCATION layout(location = 10) #endif uniform highp vec3 lightPositions[LIGHT_COUNT]; /* defaults to zero */ +#endif #ifdef EXPLICIT_ATTRIB_LOCATION layout(location = POSITION_ATTRIBUTE_LOCATION) #endif in highp vec4 position; +#if LIGHT_COUNT #ifdef EXPLICIT_ATTRIB_LOCATION layout(location = NORMAL_ATTRIBUTE_LOCATION) #endif @@ -77,6 +82,7 @@ layout(location = TANGENT_ATTRIBUTE_LOCATION) #endif in mediump vec3 tangent; #endif +#endif #ifdef TEXTURED #ifdef EXPLICIT_ATTRIB_LOCATION @@ -87,18 +93,21 @@ in mediump vec2 textureCoords; out mediump vec2 interpolatedTextureCoords; #endif +#if LIGHT_COUNT out mediump vec3 transformedNormal; #ifdef NORMAL_TEXTURE out mediump vec3 transformedTangent; #endif out highp vec3 lightDirections[LIGHT_COUNT]; out highp vec3 cameraDirection; +#endif void main() { /* Transformed vertex position */ highp vec4 transformedPosition4 = transformationMatrix*position; highp vec3 transformedPosition = transformedPosition4.xyz/transformedPosition4.w; + #if LIGHT_COUNT /* Transformed normal and tangent vector */ transformedNormal = normalMatrix*normal; #ifdef NORMAL_TEXTURE @@ -111,6 +120,7 @@ void main() { /* Direction to the camera */ cameraDirection = -transformedPosition; + #endif /* Transform the position */ gl_Position = projectionMatrix*transformedPosition4; diff --git a/src/Magnum/Shaders/Test/CMakeLists.txt b/src/Magnum/Shaders/Test/CMakeLists.txt index e5088b1f1..91fc643bc 100644 --- a/src/Magnum/Shaders/Test/CMakeLists.txt +++ b/src/Magnum/Shaders/Test/CMakeLists.txt @@ -139,7 +139,10 @@ if(BUILD_GL_TESTS) PhongTestFiles/textured-diffuse.tga PhongTestFiles/textured-normal.tga PhongTestFiles/textured-specular.tga - PhongTestFiles/textured.tga) + PhongTestFiles/textured.tga + + # For zero lights test (equivalency to Flat3D) + FlatTestFiles/textured3D-alpha-mask0.5.tga) if(NOT BUILD_PLUGINS_STATIC) target_include_directories(ShadersPhongGLTest PRIVATE $) else() diff --git a/src/Magnum/Shaders/Test/PhongGLTest.cpp b/src/Magnum/Shaders/Test/PhongGLTest.cpp index 430b9868f..76e2cd299 100644 --- a/src/Magnum/Shaders/Test/PhongGLTest.cpp +++ b/src/Magnum/Shaders/Test/PhongGLTest.cpp @@ -95,6 +95,8 @@ struct PhongGLTest: GL::OpenGLTester { void renderObjectId(); #endif + void renderZeroLights(); + private: PluginManager::Manager _manager{"nonexistent"}; @@ -139,7 +141,8 @@ constexpr struct { {"object ID", Phong::Flag::ObjectId, 1}, {"object ID + alpha mask + specular texture", Phong::Flag::ObjectId|Phong::Flag::AlphaMask|Phong::Flag::SpecularTexture, 1}, #endif - {"five lights", {}, 5} + {"five lights", {}, 5}, + {"zero lights", {}, 0} }; using namespace Math::Literals; @@ -299,6 +302,16 @@ PhongGLTest::PhongGLTest() { &PhongGLTest::renderObjectIdTeardown); #endif + addTests({&PhongGLTest::renderZeroLights}, + #ifndef MAGNUM_TARGET_GLES2 + &PhongGLTest::renderObjectIdSetup, + &PhongGLTest::renderObjectIdTeardown + #else + &PhongGLTest::renderSetup, + &PhongGLTest::renderTeardown + #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 @@ -1065,6 +1078,99 @@ void PhongGLTest::renderObjectId() { } #endif +void PhongGLTest::renderZeroLights() { + CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Draw), GL::Framebuffer::Status::Complete); + + if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) || + !(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded)) + CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found."); + + GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32, + Primitives::UVSphereTextureCoords::Generate)); + + Phong shader{ + Phong::Flag::AmbientTexture|Phong::Flag::AlphaMask + #ifndef MAGNUM_TARGET_GLES2 + |Phong::Flag::ObjectId + #endif + , 0}; + + Containers::Pointer importer = _manager.loadAndInstantiate("AnyImageImporter"); + CORRADE_VERIFY(importer); + + GL::Texture2D ambient; + Containers::Optional ambientImage; + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(SHADERS_TEST_DIR, "TestFiles/diffuse-alpha-texture.tga")) && (ambientImage = importer->image2D(0))); + ambient.setMinificationFilter(GL::SamplerFilter::Linear) + .setMagnificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, TextureFormatRGBA, ambientImage->size()) + .setSubImage(0, {}, *ambientImage); + + GL::Texture2D bogus; + + shader + .bindAmbientTexture(ambient) + .setAmbientColor(0x9999ff_rgbf) + .setTransformationMatrix( + Matrix4::translation(Vector3::zAxis(-2.15f))* + Matrix4::rotationY(-15.0_degf)* + Matrix4::rotationX(15.0_degf)) + .setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)) + .setAlphaMask(0.5f) + #ifndef MAGNUM_TARGET_GLES2 + .setObjectId(65534) + #endif + /* Passing a zero-sized light position / color array, shouldn't assert */ + .setLightPositions({}) + .setLightColors({}) + /* Using a bogus normal matrix -- it's not used so it should be okay. + Same for all other unused values, they should get ignored. */ + .setNormalMatrix(Matrix3x3{Math::ZeroInit}) + .setDiffuseColor(0xfa9922_rgbf) + .setSpecularColor(0xfa9922_rgbf) + .setShininess(0.2f); + + /* For proper Z order draw back faces first and then front faces */ + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Front); + sphere.draw(shader); + GL::Renderer::setFaceCullingMode(GL::Renderer::PolygonFacing::Back); + sphere.draw(shader); + + MAGNUM_VERIFY_NO_GL_ERROR(); + + #if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL)) + /* Compared to FlatGLTest::renderAlpha3D(0.5), there's a bit more different + pixels on the edges, caused by matrix multiplication being done in the + shader and not on the CPU side. */ + const Float maxThreshold = 139.0f, meanThreshold = 0.122f; + #else + /* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */ + const Float maxThreshold = 139.0f, meanThreshold = 2.896f; + #endif + CORRADE_COMPARE_WITH( + /* Dropping the alpha channel, as it's always 1.0 */ + Containers::arrayCast(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels()), + /* Should be equivalent to masked Flat3D */ + Utility::Directory::join(SHADERS_TEST_DIR, "FlatTestFiles/textured3D-alpha-mask0.5.tga"), + (DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold})); + + #ifndef MAGNUM_TARGET_GLES2 + /* 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], 65534); + #endif +} + }}}} CORRADE_TEST_MAIN(Magnum::Shaders::Test::PhongGLTest)