Browse Source

Shaders: add PhongGL::Flag::DoubleSided.

pull/617/head
Vladimír Vondruš 3 years ago
parent
commit
c1239b6619
  1. 2
      doc/changelog.dox
  2. 4
      src/Magnum/Shaders/Phong.frag
  3. 3
      src/Magnum/Shaders/PhongGL.cpp
  4. 14
      src/Magnum/Shaders/PhongGL.h
  5. 1
      src/Magnum/Shaders/Test/CMakeLists.txt
  6. 97
      src/Magnum/Shaders/Test/PhongGLTest.cpp
  7. BIN
      src/Magnum/Shaders/Test/PhongTestFiles/double-sided.tga

2
doc/changelog.dox

@ -316,6 +316,8 @@ See also:
- Added @ref Shaders::PhongGL::Flag::NoSpecular as a significantly faster
alternative to setting specular color to @cpp 0x00000000_rgbaf @ce in case
specular highlights are not desired
- Added @ref Shaders::PhongGL::Flag::DoubleSided for rendering double-sided
meshes
@subsubsection changelog-latest-new-shadertools ShaderTools library

4
src/Magnum/Shaders/Phong.frag

@ -462,6 +462,10 @@ void main() {
);
normalizedTransformedNormal = tbn*(normalize((texture(normalTexture, interpolatedTextureCoordinates).rgb*2.0 - vec3(1.0))*vec3(normalTextureScale, normalTextureScale, 1.0)));
#endif
#ifdef DOUBLE_SIDED
if(!gl_FrontFacing)
normalizedTransformedNormal = -normalizedTransformedNormal;
#endif
highp const vec3 cameraDirection = normalize(-transformedPosition);

3
src/Magnum/Shaders/PhongGL.cpp

@ -342,6 +342,7 @@ PhongGL::CompileState PhongGL::compile(const Configuration& configuration) {
#endif
.addSource(configuration.flags() & Flag::Bitangent ? "#define BITANGENT\n"_s : ""_s)
.addSource(configuration.flags() & Flag::VertexColor ? "#define VERTEX_COLOR\n"_s : ""_s)
.addSource(configuration.flags() & Flag::DoubleSided ? "#define DOUBLE_SIDED\n"_s : ""_s)
.addSource(configuration.flags() & Flag::AlphaMask ? "#define ALPHA_MASK\n"_s : ""_s)
#ifndef MAGNUM_TARGET_GLES2
.addSource(configuration.flags() & Flag::ObjectId ? "#define OBJECT_ID\n"_s : ""_s)
@ -1337,6 +1338,7 @@ Debug& operator<<(Debug& debug, const PhongGL::Flag value) {
_c(Bitangent)
_c(AlphaMask)
_c(VertexColor)
_c(DoubleSided)
_c(TextureTransformation)
#ifndef MAGNUM_TARGET_GLES2
_c(ObjectId)
@ -1374,6 +1376,7 @@ Debug& operator<<(Debug& debug, const PhongGL::Flags value) {
PhongGL::Flag::Bitangent,
PhongGL::Flag::AlphaMask,
PhongGL::Flag::VertexColor,
PhongGL::Flag::DoubleSided,
PhongGL::Flag::InstancedTextureOffset, /* Superset of TextureTransformation */
PhongGL::Flag::TextureTransformation,
#ifndef MAGNUM_TARGET_GLES2

14
src/Magnum/Shaders/PhongGL.h

@ -667,6 +667,20 @@ class MAGNUM_SHADERS_EXPORT PhongGL: public GL::AbstractShaderProgram {
*/
VertexColor = 1 << 5,
/**
* Double-sided rendering. By default, lighting is applied only to
* front-facing triangles, with back-facing triangles receiving
* just the ambient color or being culled away. If enabled, the
* shader will evaluate the lighting also on back-facing triangles
* with the normal flipped. Has no effect if no lights are used.
*
* Rendering back-facing triangles requires
* @ref GL::Renderer::Feature::FaceCulling to be disabled.
* @see @ref Trade::MaterialAttribute::DoubleSided
* @m_since_latest
*/
DoubleSided = 1 << 20,
/**
* Use the separate @ref Bitangent attribute for retrieving vertex
* bitangents. If this flag is not present, the last component of

1
src/Magnum/Shaders/Test/CMakeLists.txt

@ -306,6 +306,7 @@ if(MAGNUM_BUILD_GL_TESTS)
PhongTestFiles/colored.tga
PhongTestFiles/defaults.tga
PhongTestFiles/double-sided.tga
PhongTestFiles/instanced.tga
PhongTestFiles/instanced-textured.tga
PhongTestFiles/instanced-normal.tga

97
src/Magnum/Shaders/Test/PhongGLTest.cpp

@ -59,6 +59,7 @@
#include "Magnum/Math/Color.h"
#include "Magnum/Math/Matrix4.h"
#include "Magnum/Math/Swizzle.h"
#include "Magnum/MeshTools/FlipNormals.h"
#include "Magnum/MeshTools/Compile.h"
#include "Magnum/MeshTools/Transform.h"
#include "Magnum/Primitives/Plane.h"
@ -170,6 +171,9 @@ struct PhongGLTest: GL::OpenGLTester {
template<PhongGL::Flag flag = PhongGL::Flag{}> void renderZeroLights();
/* This tests something that's irrelevant to UBOs */
void renderDoubleSided();
#ifndef MAGNUM_TARGET_GLES2
template<PhongGL::Flag flag = PhongGL::Flag{}> void renderSkinning();
#endif
@ -842,6 +846,15 @@ const struct {
};
#endif
const struct {
const char* name;
PhongGL::Flags flags;
bool flipNormals;
} RenderDoubleSidedData[]{
{"normals flipped", {}, true},
{"double-sided rendering", PhongGL::Flag::DoubleSided, false}
};
#ifndef MAGNUM_TARGET_GLES2
const struct {
const char* name;
@ -1457,6 +1470,11 @@ PhongGLTest::PhongGLTest() {
&PhongGLTest::renderSetup,
&PhongGLTest::renderTeardown);
addInstancedTests<PhongGLTest>({&PhongGLTest::renderDoubleSided},
Containers::arraySize(RenderDoubleSidedData),
&PhongGLTest::renderSetup,
&PhongGLTest::renderTeardown);
#ifndef MAGNUM_TARGET_GLES2
/* MSVC needs explicit type due to default template args */
addInstancedTests<PhongGLTest>({
@ -4398,6 +4416,85 @@ template<PhongGL::Flag flag> void PhongGLTest::renderZeroLights() {
#endif
}
void PhongGLTest::renderDoubleSided() {
auto&& data = RenderDoubleSidedData[testCaseInstanceId()];
setTestCaseDescription(data.name);
Trade::MeshData sphere = Primitives::uvSphereSolid(16, 32);
Trade::MeshData sphereFlippedWinding = Primitives::uvSphereSolid(16, 32);
MeshTools::flipFaceWindingInPlace(sphereFlippedWinding.mutableIndices());
Trade::MeshData sphereFlippedNormalsWinding = Primitives::uvSphereSolid(16, 32);
MeshTools::flipNormalsInPlace(
sphereFlippedNormalsWinding.mutableIndices(),
sphereFlippedNormalsWinding.mutableAttribute<Vector3>(Trade::MeshAttribute::Normal));
/* Double-sided sphere, renders from both sides if DoubleSided is
enabled and face culling disabled, otherwise only one depending on the
normal direction */
Trade::MeshData sphereDoubleSided = Primitives::uvSphereSolid(16, 32);
if(data.flipNormals)
MeshTools::flipNormalsInPlace(sphereDoubleSided.mutableAttribute<Vector3>(Trade::MeshAttribute::Normal));
PhongGL shader{PhongGL::Configuration{}
.setFlags(data.flags)
.setLightCount(1)};
shader
.setLightPositions({{-3.0f, 3.0f, 3.0f, 0.0f}})
.setAmbientColor(0x111111_rgbf)
.setDiffuseColor(0xff3333_rgbf)
.setSpecularColor(0x00000000_rgbaf);
/* Top left is a sphere from the outside, with CCW triangles, with the back
cut off by the far plane */
shader
.setProjectionMatrix(Matrix4::orthographicProjection(Vector2{4.5f}, -1.0f, 0.0f))
.setTransformationMatrix(Matrix4::translation({-1.05f, 1.05f, 0.0f}))
.draw(MeshTools::compile(sphere));
/* Bottom left is a sphere from the inside, with CCW triangles, with the
front cut off by the near plane. Normals pointing outside so only top
left should be slightly lighted. */
shader
.setProjectionMatrix(Matrix4::orthographicProjection(Vector2{4.5f}, 0.0f, 1.0f))
.setTransformationMatrix(Matrix4::translation({-1.05f, -1.05f, 0.0f}))
.draw(MeshTools::compile(sphereFlippedWinding));
/* Top right is a sphere from the inside, with CCW triangles, with face
winding and normals flipped */
shader
.setProjectionMatrix(Matrix4::orthographicProjection(Vector2{4.5f}, 0.0f, 1.0f))
.setTransformationMatrix(Matrix4::translation({+1.05f, 1.05f, 0.0f}))
.draw(MeshTools::compile(sphereFlippedNormalsWinding));
GL::Renderer::disable(GL::Renderer::Feature::FaceCulling);
/* Bottom right is a sphere from the inside, with CW triangles and face
culling disabled. Should render like bottom right.
- If DoubleSided isn't enabled on the shader, the code above flipped
normals to point inside. If DoubleSided is accidentally active
always, it will flip them back outside, resulting in the same result
as on the bottom left.
- If DoubleSided is enabled on the shader, the normals weren't flipped
by the code above and the shader should do that instead. If it
doesn't, it will again wrongly render as on the bottom left. */
shader
.setProjectionMatrix(Matrix4::orthographicProjection(Vector2{4.5f}, 0.0f, 1.0f))
.setTransformationMatrix(Matrix4::translation({+1.05f, -1.05f, 0.0f}))
.draw(MeshTools::compile(sphereDoubleSided));
GL::Renderer::enable(GL::Renderer::Feature::FaceCulling);
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE_WITH(
/* Dropping the alpha channel, as it's always 1.0 */
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
Utility::Path::join(_testDir, "PhongTestFiles/double-sided.tga"),
(DebugTools::CompareImageToFile{_manager, 1.34f, 0.04f}));
}
#ifndef MAGNUM_TARGET_GLES2
template<PhongGL::Flag flag> void PhongGLTest::renderSkinning() {
auto&& data = RenderSkinningData[testCaseInstanceId()];

BIN
src/Magnum/Shaders/Test/PhongTestFiles/double-sided.tga

Binary file not shown.
Loading…
Cancel
Save