Browse Source

Shaders: tangent space visualization in MeshVisualizer3D.

pull/430/head
Vladimír Vondruš 6 years ago
parent
commit
5b569942b8
  1. 5
      doc/changelog.dox
  2. 3
      doc/generated/primitives.cpp
  3. 5
      doc/generated/shaders.cpp
  4. 49
      doc/snippets/MagnumShaders.cpp
  5. 186
      src/Magnum/Shaders/MeshVisualizer.cpp
  6. 81
      src/Magnum/Shaders/MeshVisualizer.frag
  7. 154
      src/Magnum/Shaders/MeshVisualizer.geom
  8. 280
      src/Magnum/Shaders/MeshVisualizer.h
  9. 81
      src/Magnum/Shaders/MeshVisualizer.vert
  10. 349
      src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp
  11. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/bitangents-from-tangents.tga
  12. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/tbn-wide.tga
  13. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/tbn.tga
  14. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-tn-smooth.tga
  15. BIN
      src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-tn.tga

5
doc/changelog.dox

@ -206,6 +206,7 @@ See also:
@subsubsection changelog-latest-new-shaders Shaders library
- New @ref Shaders::MeshVisualizer2D for 2D mesh visualization
- Tangent space visualization in @ref Shaders::MeshVisualizer3D
- Texture coordinate transformation in @ref Shaders::DistanceFieldVector,
@ref Shaders::Flat, @ref Shaders::Phong and @ref Shaders::Vector
- New attribute definitions and an location allocation scheme in
@ -502,6 +503,10 @@ See also:
@ref MeshTools::subdivideInPlace() overloads instead
- @cpp Shaders::MeshVisualizer @ce is deprecated as the shader can now handle
both 2D and 3D, use @ref Shaders::MeshVisualizer3D instead
- @cpp Shaders::MeshVisualizer::setTransformationProjectionMatrix() @ce is
deprecated on the 3D variant, use separate
@ref Shaders::MeshVisualizer3D::setTransformationMatrix() and
@ref Shaders::MeshVisualizer3D::setProjectionMatrix() instead
@subsection changelog-latest-compatibility Potential compatibility breakages, removed APIs

3
doc/generated/primitives.cpp

@ -312,7 +312,8 @@ int PrimitiveVisualizer::exec() {
.setWireframeColor(OutlineColor)
.setWireframeWidth(2.0f)
.setViewportSize(Vector2{ImageSize})
.setTransformationProjectionMatrix(Projection3D*Transformation3D);
.setTransformationMatrix(Transformation3D)
.setProjectionMatrix(Projection3D);
{
Shaders::Phong phong;

5
doc/generated/shaders.cpp

@ -186,7 +186,7 @@ std::string ShaderVisualizer::meshVisualizer2D() {
}
std::string ShaderVisualizer::meshVisualizer3D() {
const Matrix4 projection = Projection*Transformation*
const Matrix4 transformation = Transformation*
Matrix4::rotationZ(13.7_degf)*
Matrix4::rotationX(-12.6_degf);
@ -195,7 +195,8 @@ std::string ShaderVisualizer::meshVisualizer3D() {
.setWireframeColor(OutlineColor)
.setWireframeWidth(2.0f)
.setViewportSize(Vector2{ImageSize})
.setTransformationProjectionMatrix(projection)
.setTransformationMatrix(transformation)
.setProjectionMatrix(Projection)
.draw(MeshTools::compile(Primitives::icosphereSolid(1)));
return "meshvisualizer3d.png";

49
doc/snippets/MagnumShaders.cpp

@ -100,7 +100,8 @@ visualizerShader
.setColor(0x2f83cc_rgbf)
.setWireframeColor(0xdcdcdc_rgbf)
.setViewportSize(Vector2{GL::defaultFramebuffer.viewport().size()})
.setTransformationProjectionMatrix(projectionMatrix*transformationMatrix)
.setTransformationMatrix(transformationMatrix)
.setProjectionMatrix(projectionMatrix)
.draw(mesh);
/* [shaders-meshvisualizer] */
}
@ -266,7 +267,8 @@ Shaders::MeshVisualizer3D shader{Shaders::MeshVisualizer3D::Flag::Wireframe};
shader.setColor(0x2f83cc_rgbf)
.setWireframeColor(0xdcdcdc_rgbf)
.setViewportSize(Vector2{GL::defaultFramebuffer.viewport().size()})
.setTransformationProjectionMatrix(projectionMatrix*transformationMatrix)
.setTransformationMatrix(transformationMatrix)
.setProjectionMatrix(projectionMatrix)
.draw(mesh);
/* [MeshVisualizer-usage-geom2] */
@ -282,6 +284,46 @@ mesh.addVertexBuffer(vertexIndices, 0, Shaders::MeshVisualizer3D::VertexIndex{})
}
#endif
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
{
/* [MeshVisualizer-usage-tbn1] */
struct Vertex {
Vector3 position;
Vector4 tangent;
Vector3 normal;
};
Vertex data[60]{
// ...
};
GL::Buffer vertices;
vertices.setData(data);
GL::Mesh mesh;
mesh.addVertexBuffer(vertices, 0,
Shaders::MeshVisualizer3D::Position{},
Shaders::MeshVisualizer3D::Tangent4{},
Shaders::MeshVisualizer3D::Normal{});
/* [MeshVisualizer-usage-tbn1] */
/* [MeshVisualizer-usage-tbn2] */
Matrix4 transformationMatrix, projectionMatrix;
Shaders::MeshVisualizer3D shader{
Shaders::MeshVisualizer3D::Flag::TangentDirection|
Shaders::MeshVisualizer3D::Flag::BitangentFromTangentDirection|
Shaders::MeshVisualizer3D::Flag::NormalDirection};
shader.setViewportSize(Vector2{GL::defaultFramebuffer.viewport().size()})
.setTransformationMatrix(transformationMatrix)
.setProjectionMatrix(projectionMatrix)
.setNormalMatrix(transformationMatrix.normalMatrix())
.setLineLength(0.3f)
.draw(mesh);
/* [MeshVisualizer-usage-tbn2] */
}
#endif
{
/* [MeshVisualizer-usage-no-geom1] */
Containers::StridedArrayView1D<const UnsignedInt> indices;
@ -305,7 +347,8 @@ Shaders::MeshVisualizer3D shader{
Shaders::MeshVisualizer3D::Flag::NoGeometryShader};
shader.setColor(0x2f83cc_rgbf)
.setWireframeColor(0xdcdcdc_rgbf)
.setTransformationProjectionMatrix(projectionMatrix*transformationMatrix)
.setTransformationMatrix(transformationMatrix)
.setProjectionMatrix(projectionMatrix)
.draw(mesh);
/* [MeshVisualizer-usage-no-geom2] */
}

186
src/Magnum/Shaders/MeshVisualizer.cpp

@ -28,6 +28,7 @@
#include <Corrade/Containers/EnumSet.hpp>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Reference.h>
#include <Corrade/Utility/FormatStl.h>
#include <Corrade/Utility/Resource.h>
#include "Magnum/Math/Color.h"
@ -94,14 +95,6 @@ GL::Version MeshVisualizerBase::setupShaders(GL::Shader& vert, GL::Shader& frag,
return version;
}
MeshVisualizerBase& MeshVisualizerBase::setViewportSize(const Vector2& size) {
/* Not asserting here, since the relation to wireframe is a bit vague.
Also it's an ugly hack that should be removed, ideally. */
if(_flags & FlagBase::Wireframe && !(_flags & FlagBase::NoGeometryShader))
setUniform(_viewportSizeUniform, size);
return *this;
}
MeshVisualizerBase& MeshVisualizerBase::setColor(const Color4& color) {
setUniform(_colorUniform, color);
return *this;
@ -121,15 +114,6 @@ MeshVisualizerBase& MeshVisualizerBase::setWireframeWidth(const Float width) {
return *this;
}
MeshVisualizerBase& MeshVisualizerBase::setSmoothness(const Float smoothness) {
/* This is a bit vaguely related too, but less vague than setViewportSize()
so asserting. */
CORRADE_ASSERT(_flags & FlagBase::Wireframe,
"Shaders::MeshVisualizer::setSmoothness(): the shader was not created with wireframe enabled", *this);
setUniform(_smoothnessUniform, smoothness);
return *this;
}
}
MeshVisualizer2D::MeshVisualizer2D(const Flags flags): Implementation::MeshVisualizerBase{Implementation::MeshVisualizerBase::FlagBase(UnsignedByte(flags))} {
@ -148,7 +132,9 @@ MeshVisualizer2D::MeshVisualizer2D(const Flags flags): Implementation::MeshVisua
Containers::Optional<GL::Shader> geom;
if(flags & Flag::Wireframe && !(flags & Flag::NoGeometryShader)) {
geom = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Geometry);
geom->addSource(rs.get("MeshVisualizer.geom"));
(*geom)
.addSource("#define WIREFRAME_RENDERING\n#define MAX_VERTICES 3\n")
.addSource(rs.get("MeshVisualizer.geom"));
}
#else
static_cast<void>(version);
@ -214,28 +200,75 @@ MeshVisualizer2D::MeshVisualizer2D(const Flags flags): Implementation::MeshVisua
#endif
}
MeshVisualizer2D& MeshVisualizer2D::setViewportSize(const Vector2& size) {
/* Not asserting here, since the relation to wireframe is a bit vague.
Also it's an ugly hack that should be removed, ideally. */
if(flags() & Flag::Wireframe && !(flags() & Flag::NoGeometryShader))
setUniform(_viewportSizeUniform, size);
return *this;
}
MeshVisualizer2D& MeshVisualizer2D::setTransformationProjectionMatrix(const Matrix3& matrix) {
setUniform(_transformationProjectionMatrixUniform, matrix);
return *this;
}
MeshVisualizer2D& MeshVisualizer2D::setSmoothness(const Float smoothness) {
/* This is a bit vaguely related but less vague than setViewportSize() so
asserting in this case. */
CORRADE_ASSERT(flags() & Flag::Wireframe,
"Shaders::MeshVisualizer2D::setSmoothness(): the shader was not created with wireframe enabled", *this);
setUniform(_smoothnessUniform, smoothness);
return *this;
}
MeshVisualizer3D::MeshVisualizer3D(const Flags flags): Implementation::MeshVisualizerBase{Implementation::MeshVisualizerBase::FlagBase(UnsignedByte(flags))} {
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
CORRADE_ASSERT(!(flags & Flag::NoGeometryShader && flags & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection)),
"Shaders::MeshVisualizer3D: geometry shader has to be enabled when rendering TBN direction", );
CORRADE_ASSERT(!(flags & Flag::BitangentDirection && flags & Flag::BitangentFromTangentDirection),
"Shaders::MeshVisualizer3D: Flag::BitangentDirection and Flag::BitangentFromTangentDirection are mutually exclusive", );
#endif
Utility::Resource rs{"MagnumShaders"};
GL::Shader vert{NoCreate};
GL::Shader frag{NoCreate};
const GL::Version version = setupShaders(vert, frag, rs);
vert.addSource("#define THREE_DIMENSIONS\n")
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
.addSource(flags & Flag::TangentDirection ? "#define TANGENT_DIRECTION\n" : "")
.addSource(flags & Flag::BitangentFromTangentDirection ? "#define BITANGENT_FROM_TANGENT_DIRECTION\n" : "")
.addSource(flags & Flag::BitangentDirection ? "#define BITANGENT_DIRECTION\n" : "")
.addSource(flags & Flag::NormalDirection ? "#define NORMAL_DIRECTION\n" : "")
#endif
.addSource(rs.get("generic.glsl"))
.addSource(rs.get("MeshVisualizer.vert"));
frag.addSource(rs.get("generic.glsl"))
frag
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
.addSource(flags & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection) ? "#define TBN_DIRECTION\n" : "")
#endif
.addSource(rs.get("generic.glsl"))
.addSource(rs.get("MeshVisualizer.frag"));
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
Containers::Optional<GL::Shader> geom;
if(flags & Flag::Wireframe && !(flags & Flag::NoGeometryShader)) {
if(flags & (Flag::Wireframe|Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection) && !(flags & Flag::NoGeometryShader)) {
Int maxVertices = 0;
if(flags & Flag::Wireframe) maxVertices += 3;
if(flags & Flag::TangentDirection) maxVertices += 3*6;
if(flags & (Flag::BitangentDirection|Flag::BitangentFromTangentDirection))
maxVertices += 3*6;
if(flags & Flag::NormalDirection) maxVertices += 3*6;
geom = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Geometry);
geom->addSource(rs.get("MeshVisualizer.geom"));
(*geom)
.addSource(Utility::formatString("#define MAX_VERTICES {}\n", maxVertices))
.addSource(flags & Flag::Wireframe ? "#define WIREFRAME_RENDERING\n" : "")
.addSource(flags & Flag::TangentDirection ? "#define TANGENT_DIRECTION\n" : "")
.addSource(flags & (Flag::BitangentDirection|Flag::BitangentFromTangentDirection) ? "#define BITANGENT_DIRECTION\n" : "")
.addSource(flags & Flag::NormalDirection ? "#define NORMAL_DIRECTION\n" : "")
.addSource(rs.get("MeshVisualizer.geom"));
}
#else
static_cast<void>(version);
@ -260,6 +293,17 @@ MeshVisualizer3D::MeshVisualizer3D(const Flags flags): Implementation::MeshVisua
{
bindAttributeLocation(Position::Location, "position");
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
if(flags & Flag::TangentDirection ||
flags & Flag::BitangentFromTangentDirection)
bindAttributeLocation(Tangent4::Location, "tangent");
if(flags & Flag::BitangentDirection)
bindAttributeLocation(Bitangent::Location, "bitangent");
if(flags & Flag::NormalDirection ||
flags & Flag::BitangentFromTangentDirection)
bindAttributeLocation(Normal::Location, "normal");
#endif
#if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2)
#ifndef MAGNUM_TARGET_GLES
if(!GL::Context::current().isVersionSupported(GL::Version::GL310))
@ -277,32 +321,118 @@ MeshVisualizer3D::MeshVisualizer3D(const Flags flags): Implementation::MeshVisua
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::explicit_uniform_location>(version))
#endif
{
_transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix");
_transformationMatrixUniform = uniformLocation("transformationMatrix");
_projectionMatrixUniform = uniformLocation("projectionMatrix");
_colorUniform = uniformLocation("color");
if(flags & Flag::Wireframe) {
_wireframeColorUniform = uniformLocation("wireframeColor");
_wireframeWidthUniform = uniformLocation("wireframeWidth");
}
if(flags & (Flag::Wireframe
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
|Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection
#endif
)) {
_smoothnessUniform = uniformLocation("smoothness");
if(!(flags & Flag::NoGeometryShader))
_viewportSizeUniform = uniformLocation("viewportSize");
}
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
if(flags & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection)) {
_normalMatrixUniform = uniformLocation("normalMatrix");
_lineWidthUniform = uniformLocation("lineWidth");
_lineLengthUniform = uniformLocation("lineLength");
}
#endif
}
/* Set defaults in OpenGL ES (for desktop they are set in shader code itself) */
#ifdef MAGNUM_TARGET_GLES
setTransformationProjectionMatrix({});
setTransformationMatrix({});
setProjectionMatrix({});
setColor(Color3(1.0f));
if(flags & Flag::Wireframe) {
/* Viewport size is zero by default */
setWireframeColor(Color3{0.0f});
setWireframeWidth(1.0f);
}
if(flags & (Flag::Wireframe
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
|Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection
#endif
)) {
setSmoothness(2.0f);
}
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
if(flags & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection)) {
setNormalMatrix({});
setLineWidth(1.0f);
setLineLength(1.0f);
}
#endif
#endif
}
MeshVisualizer3D& MeshVisualizer3D::setTransformationProjectionMatrix(const Matrix4& matrix) {
setUniform(_transformationProjectionMatrixUniform, matrix);
MeshVisualizer3D& MeshVisualizer3D::setTransformationMatrix(const Matrix4& matrix) {
setUniform(_transformationMatrixUniform, matrix);
return *this;
}
MeshVisualizer3D& MeshVisualizer3D::setProjectionMatrix(const Matrix4& matrix) {
setUniform(_projectionMatrixUniform, matrix);
return *this;
}
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
MeshVisualizer3D& MeshVisualizer3D::setNormalMatrix(const Matrix3x3& matrix) {
CORRADE_ASSERT(flags() & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection),
"Shaders::MeshVisualizer3D::setNormalMatrix(): the shader was not created with TBN direction enabled", *this);
setUniform(_normalMatrixUniform, matrix);
return *this;
}
#endif
MeshVisualizer3D& MeshVisualizer3D::setViewportSize(const Vector2& size) {
/* Not asserting here, since the relation to wireframe is a bit vague.
Also it's an ugly hack that should be removed, ideally. */
if((flags() & Flag::Wireframe && !(flags() & Flag::NoGeometryShader))
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
|| flags() & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection)
#endif
)
setUniform(_viewportSizeUniform, size);
return *this;
}
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
MeshVisualizer3D& MeshVisualizer3D::setLineWidth(const Float width) {
CORRADE_ASSERT(flags() & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection),
"Shaders::MeshVisualizer3D::setLineWidth(): the shader was not created with TBN direction enabled", *this);
setUniform(_lineWidthUniform, width);
return *this;
}
MeshVisualizer3D& MeshVisualizer3D::setLineLength(const Float length) {
CORRADE_ASSERT(flags() & (Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection),
"Shaders::MeshVisualizer3D::setLineLength(): the shader was not created with TBN direction enabled", *this);
setUniform(_lineLengthUniform, length);
return *this;
}
#endif
MeshVisualizer3D& MeshVisualizer3D::setSmoothness(const Float smoothness) {
#ifndef CORRADE_NO_ASSERT
/* This is a bit vaguely related but less vague than setViewportSize() so
asserting in this case. */
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
constexpr Flags allowed = Flag::Wireframe|Flag::TangentDirection|Flag::BitangentFromTangentDirection|Flag::BitangentDirection|Flag::NormalDirection;
#else
constexpr Flags allowed = Flag::Wireframe;
#endif
CORRADE_ASSERT(flags() & allowed,
"Shaders::MeshVisualizer3D::setSmoothness(): the shader was not created with wireframe or TBN direction enabled", *this);
#endif
setUniform(_smoothnessUniform, smoothness);
return *this;
}
@ -329,6 +459,12 @@ Debug& operator<<(Debug& debug, const MeshVisualizer3D::Flag value) {
#define _c(v) case MeshVisualizer3D::Flag::v: return debug << "::" #v;
_c(NoGeometryShader)
_c(Wireframe)
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
_c(TangentDirection)
_c(BitangentFromTangentDirection)
_c(BitangentDirection)
_c(NormalDirection)
#endif
#undef _c
/* LCOV_EXCL_STOP */
}

81
src/Magnum/Shaders/MeshVisualizer.frag

@ -44,6 +44,7 @@
#extension GL_NV_shader_noperspective_interpolation: require
#endif
#ifndef TBN_DIRECTION
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 1)
#endif
@ -53,7 +54,7 @@ uniform lowp vec4 color
#endif
;
#ifdef WIREFRAME_RENDERING
#if defined(WIREFRAME_RENDERING) && !defined(TBN_DIRECTION)
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 2)
#endif
@ -62,7 +63,10 @@ uniform lowp vec4 wireframeColor
= vec4(0.0, 0.0, 0.0, 1.0)
#endif
;
#endif
#endif
#ifdef WIREFRAME_RENDERING
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 3)
#endif
@ -71,7 +75,18 @@ uniform lowp float wireframeWidth
= 1.0
#endif
;
#elif defined(TBN_DIRECTION)
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 8)
#endif
uniform lowp float lineWidth
#ifndef GL_ES
= 1.0
#endif
;
#endif
#if defined(WIREFRAME_RENDERING) || defined(TBN_DIRECTION)
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 4)
#endif
@ -91,6 +106,11 @@ in lowp vec3 barycentric;
#endif
#endif
#ifdef TBN_DIRECTION
in lowp vec4 backgroundColor;
in lowp vec4 lineColor;
#endif
#ifdef NEW_GLSL
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = COLOR_OUTPUT_ATTRIBUTE_LOCATION)
@ -99,13 +119,63 @@ out lowp vec4 fragmentColor;
#endif
void main() {
#ifdef WIREFRAME_RENDERING
/* 1. For wireframe the line is on the triangle edges, thus dist = 0 at
vertices and dist = w (= wireframeWidth) at center of smoothed edge.
2. For antialiased TBN (drawn alone) the line is in the center of the
bar, thus dist = 0 at the center, dist = ±w (= lineWidth) at center
of the smoothed edge, and dist = ±S = w + s (= smoothness) at edge of
the primitive.
3. For non-antialised TBN (drawn together with wireframe) the bar width
is without the extra padding for smoothness.
0 _____________ 0 -S_-w_0__w_S -w_0__w
\ _______ / | | | | | |
\ w w / | | /| | | /|
\ \ / / | | / | | | / |
\ \w/ / | | / | | | / |
\ v / | | / | | | / |
\ / | |/ | | |/ |
v |_|_____|_| |_____|
0 -S -w 0 w S -w 0 w
*/
#if defined(WIREFRAME_RENDERING) || defined(TBN_DIRECTION)
#ifndef NO_GEOMETRY_SHADER
/* Distance to nearest side */
/* Distance to nearest side. If signed distance is involved when rendering
TBN alone, we need to find an absolute distance */
#if defined(TBN_DIRECTION) && !defined(WIREFRAME_RENDERING)
lowp const float nearest = min(min(abs(dist.x), abs(dist.y)), abs(dist.z));
#else
lowp const float nearest = min(min(dist.x, dist.y), dist.z);
#endif
/* Smooth step between face color and wireframe color based on distance */
fragmentColor = mix(wireframeColor, color, smoothstep(wireframeWidth-smoothness, wireframeWidth+smoothness, nearest));
/* Smooth step between two colors based on distance.
1. For wireframe alone the width and colors are supplied directly from
wireframeWidth, wireframeColor, color uniforms
2. For TBN alone the width is supplied directly from the lineWidth
uniform and colors from the GS
3. For TBN and wireframe together wireframeWidth is used to draw the
wireframe, and since `nearest` is always 0 for the TBN bars, there it
doesn't matter.
*/
fragmentColor = mix(
#if defined(WIREFRAME_RENDERING) && !defined(TBN_DIRECTION)
wireframeColor, color,
#else
lineColor, backgroundColor,
#endif
#ifdef WIREFRAME_RENDERING
smoothstep(wireframeWidth - smoothness,
wireframeWidth + smoothness, nearest)
#elif defined(TBN_DIRECTION)
smoothstep(lineWidth - smoothness,
lineWidth + smoothness, nearest)
#else
#error
#endif
);
/* Wireframe rendering without a GS. No TBN in this case */
#else
const lowp vec3 d = fwidth(barycentric);
const lowp vec3 factor = smoothstep(vec3(0.0), d*1.5, barycentric);
@ -113,6 +183,7 @@ void main() {
fragmentColor = mix(wireframeColor, color, nearest);
#endif
/* Plain color rendering */
#else
fragmentColor = color;
#endif

154
src/Magnum/Shaders/MeshVisualizer.geom

@ -41,7 +41,55 @@ uniform lowp vec2 viewportSize; /* defaults to zero */
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
#if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION)
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 1)
#endif
uniform lowp vec4 color
#ifndef GL_ES
= vec4(1.0, 1.0, 1.0, 1.0)
#endif
;
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 2)
#endif
uniform lowp vec4 wireframeColor
#ifndef GL_ES
= vec4(0.0, 0.0, 0.0, 1.0)
#endif
;
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 8)
#endif
uniform lowp float lineWidth
#ifndef GL_ES
= 1.0
#endif
;
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 4)
#endif
uniform lowp float smoothness
#ifndef GL_ES
= 2.0
#endif
;
#endif
#ifdef TANGENT_DIRECTION
in highp vec4 tangentEndpoint[];
#endif
#ifdef BITANGENT_DIRECTION
in highp vec4 bitangentEndpoint[];
#endif
#ifdef NORMAL_DIRECTION
in highp vec4 normalEndpoint[];
#endif
layout(triangle_strip, max_vertices = MAX_VERTICES) out;
/* Interpolate in screen space (if possible) */
#if !defined(GL_ES) || defined(GL_NV_shader_noperspective_interpolation)
@ -49,12 +97,89 @@ noperspective
#endif
out lowp vec3 dist;
#if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION)
out lowp vec4 backgroundColor;
out lowp vec4 lineColor;
void emitQuad(vec4 position, vec2 positionScreen, vec4 endpoint, vec2 endpointScreen) {
/* Calculate screen-space locations for the bar vertices and form two
triangles out of them. In case TBN is rendered alone, half bar width is
lineWidth + smoothness to allow for antialiasing, in case it's rendered
together with wireframe, it's just lineWidth, as antialiasing would
cause depth order issues.
3 - e - 1 -S_-w_0__w_S -w_0__w
| /| | | | | | |
| / | | | /| | | /|
| / | | | / | | | / |
| / | | | / | | | / |
| / | | | / | | | / |
| / | | |/ | | |/ |
|/ | |_|_____|_| |_____|
2 - p - 0 -S -w 0 w S -w 0 w
The edgeDistance is set to a signed half-width for the antialiased case
and to 0 otherwise. See the fragment shader for details.
*/
vec2 direction = normalize(endpointScreen - positionScreen);
#ifdef WIREFRAME_RENDERING
float edgeDistance = 0.0;
vec2 halfSide = lineWidth*vec2(-direction.y, direction.x);
#else
float edgeDistance = lineWidth + smoothness;
vec2 halfSide = edgeDistance*vec2(-direction.y, direction.x);
#endif
/* 0 */
dist = vec3(-edgeDistance);
gl_Position = vec4((positionScreen - halfSide)*position.w/viewportSize, position.zw);
EmitVertex();
/* 1 */
dist = vec3(-edgeDistance);
gl_Position = vec4((endpointScreen - halfSide)*endpoint.w/viewportSize, endpoint.zw);
EmitVertex();
/* 2 */
dist = vec3(edgeDistance);
gl_Position = vec4((positionScreen + halfSide)*position.w/viewportSize, position.zw);
EmitVertex();
/* 3 */
dist = vec3(edgeDistance);
gl_Position = vec4((endpointScreen + halfSide)*endpoint.w/viewportSize, endpoint.zw);
EmitVertex();
EndPrimitive();
}
#endif
void main() {
/* Screen position of each vertex */
vec2 p[3];
for(int i = 0; i != 3; ++i)
#ifdef TANGENT_DIRECTION
vec2 t[3];
#endif
#ifdef BITANGENT_DIRECTION
vec2 b[3];
#endif
#ifdef NORMAL_DIRECTION
vec2 n[3];
#endif
for(int i = 0; i != 3; ++i) {
p[i] = viewportSize*gl_in[i].gl_Position.xy/gl_in[i].gl_Position.w;
#ifdef TANGENT_DIRECTION
t[i] = viewportSize*tangentEndpoint[i].xy/tangentEndpoint[i].w;
#endif
#ifdef BITANGENT_DIRECTION
b[i] = viewportSize*bitangentEndpoint[i].xy/bitangentEndpoint[i].w;
#endif
#ifdef NORMAL_DIRECTION
n[i] = viewportSize*normalEndpoint[i].xy/normalEndpoint[i].w;
#endif
}
#ifdef WIREFRAME_RENDERING
/* Vector of each triangle side */
const vec2 v[3] = vec2[3](
p[2]-p[1],
@ -69,9 +194,34 @@ void main() {
for(int i = 0; i != 3; ++i) {
dist = vec3(0.0, 0.0, 0.0);
dist[i] = area/length(v[i]);
#if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION)
backgroundColor = color;
lineColor = wireframeColor;
#endif
gl_Position = gl_in[i].gl_Position;
EmitVertex();
}
EndPrimitive();
#endif
#if defined(TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION)
backgroundColor = vec4(0.0, 0.0, 0.0, 0.0);
for(int i = 0; i != 3; ++i) {
#ifdef TANGENT_DIRECTION
lineColor = vec4(1.0, 0.0, 0.0, 1.0);
emitQuad(gl_in[i].gl_Position, p[i], tangentEndpoint[i], t[i]);
#endif
#ifdef BITANGENT_DIRECTION
lineColor = vec4(0.0, 1.0, 0.0, 1.0);
emitQuad(gl_in[i].gl_Position, p[i], bitangentEndpoint[i], b[i]);
#endif
#ifdef NORMAL_DIRECTION
lineColor = vec4(0.0, 0.0, 1.0, 1.0);
emitQuad(gl_in[i].gl_Position, p[i], normalEndpoint[i], n[i]);
#endif
}
#endif
}

280
src/Magnum/Shaders/MeshVisualizer.h

@ -59,11 +59,9 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizerBase: public GL::AbstractShaderProgram
MAGNUM_SHADERS_LOCAL GL::Version setupShaders(GL::Shader& vert, GL::Shader& frag, const Utility::Resource& rs) const;
MeshVisualizerBase& setViewportSize(const Vector2& size);
MeshVisualizerBase& setColor(const Color4& color);
MeshVisualizerBase& setWireframeColor(const Color4& color);
MeshVisualizerBase& setWireframeWidth(Float width);
MeshVisualizerBase& setSmoothness(Float smoothness);
/* Prevent accidentally calling irrelevant functions */
#ifndef MAGNUM_TARGET_GLES
@ -205,9 +203,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer2D: public Implementation::MeshVisuali
* shaders are used, otherwise it does nothing. Initial value is a zero
* vector.
*/
MeshVisualizer2D& setViewportSize(const Vector2& size) {
return static_cast<MeshVisualizer2D&>(Implementation::MeshVisualizerBase::setViewportSize(size));
}
MeshVisualizer2D& setViewportSize(const Vector2& size);
/**
* @brief Set base object color
@ -250,9 +246,7 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer2D: public Implementation::MeshVisuali
* initial value is @cpp 2.0f @ce. Expects that @ref Flag::Wireframe is
* enabled.
*/
MeshVisualizer2D& setSmoothness(Float smoothness) {
return static_cast<MeshVisualizer2D&>(Implementation::MeshVisualizerBase::setSmoothness(smoothness));
}
MeshVisualizer2D& setSmoothness(Float smoothness);
private:
Int _transformationProjectionMatrixUniform{0};
@ -290,7 +284,34 @@ have OpenGL < 3.1 or OpenGL ES 2.0, you need to provide also the
wireframe rendering without geometry shaders.
If using geometry shaders on OpenGL ES, @gl_extension{NV,shader_noperspective_interpolation}
is optionally used for improving line appearance.
is optionally used for improving line appearance. On desktop OpenGL this is
done implicitly.
@section Shaders-MeshVisualizer-tbn Tangent space visualization
On platforms with geometry shaders (desktop GL, OpenGL ES 3.2), the shader is
able to visualize tangents, bitangent and normal direction via colored lines
coming out of vertices (red, green and blue for tangent, bitangent and normal, respectively). This can be enabled together with wireframe visualization,
however note that when both are enabled, the lines are not antialiased to avoid
depth ordering artifacts.
For tangents and normals, you need to provide the @ref Tangent and @ref Normal
attributes and enable @ref Flag::TangentDirection and
@ref Flag::NormalDirection, respectively. If any of the attributes isn't
present, its data are implicitly zero and thus the direction isn't shown ---
which means you don't need to worry about having two active variants of the
shader and switching between either depending on whether tangents are present
or not.
For bitangents however, there are two possible representations --- the more
efficient one is via a fourth component in the tangent attribute that
indicates tangent space handedness, in which case you'll be using the
@ref Tangent4 attribute instead of @ref Tangent, and enable
@ref Flag::BitangentFromTangentDirection. The other, more obvious but less
efficient representation, is a dedicated @ref Bitangent attribute (in which
case you'll enable @ref Flag::BitangentDirection). Note that these two are
mutually exclusive, so you need to choose either of them based on what given
mesh contains.
@section Shaders-MeshVisualizer-usage Example usage
@ -304,6 +325,18 @@ Common rendering setup:
@snippet MagnumShaders.cpp MeshVisualizer-usage-geom2
@subsection Shaders-MeshVisualizer-usage-wireframe-tbn Tangent space visualization with a geometry shader (desktop GL, OpenGL ES 3.2)
Setup for a mesh that contains four-component tangents
(@ref Shaders-MeshVisualizer-tbn "see above" if you have a dedicated bitangent
attribute instead):
@snippet MagnumShaders.cpp MeshVisualizer-usage-tbn1
Rendering setup:
@snippet MagnumShaders.cpp MeshVisualizer-usage-tbn2
@subsection Shaders-MeshVisualizer-usage-wireframe-no-geom Wireframe visualization of indexed meshes without a geometry shader
The vertices have to be converted to a non-indexed array first. Mesh setup:
@ -336,6 +369,47 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali
*/
typedef typename Generic3D::Position Position;
/**
* @brief Tangent direction
* @m_since_latest
*
* @ref shaders-generic "Generic attribute",
* @ref Magnum::Vector3 "Vector3". Use either this or the @ref Tangent4
* attribute. Used only if @ref Flag::TangentDirection is enabled.
*/
typedef typename Generic3D::Tangent Tangent;
/**
* @brief Tangent direction with a bitangent sign
* @m_since_latest
*
* @ref shaders-generic "Generic attribute",
* @ref Magnum::Vector4 "Vector4". Use either this or the @ref Tangent
* attribute. Used only if @ref Flag::TangentDirection or
* @ref Flag::BitangentFromTangentDirection is enabled.
*/
typedef typename Generic3D::Tangent4 Tangent4;
/**
* @brief Bitangent direction
* @m_since_latest
*
* @ref shaders-generic "Generic attribute",
* @ref Magnum::Vector4 "Vector4". Use either this or the @ref Tangent4
* attribute. Used only if @ref Flag::BitangentDirection is enabled.
*/
typedef typename Generic3D::Bitangent Bitangent;
/**
* @brief Normal direction
* @m_since_latest
*
* @ref shaders-generic "Generic attribute",
* @ref Magnum::Vector3 "Vector3". Used only if
* @ref Flag::NormalDirection is enabled.
*/
typedef typename Generic3D::Normal Normal;
/**
* @brief Vertex index
*
@ -384,8 +458,78 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali
* enabled, you might need to provide also the @ref VertexIndex
* attribute in the mesh. In OpenGL ES 2.0 enabled alongside
* @ref Flag::Wireframe.
*
* Mutually exclusive with @ref Flag::TangentDirection,
* @ref Flag::BitangentFromTangentDirection,
* @ref Flag::BitangentDirection and @ref Flag::NormalDirection ---
* those need a geometry shader always.
*/
NoGeometryShader = 1 << 1
NoGeometryShader = 1 << 1,
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
/**
* Visualize tangent direction with red lines pointing out of
* vertices. You need to provide the @ref Tangent or @ref Tangent4
* attribute in the mesh. Mutually exclusive with
* @ref Flag::NoGeometryShader (as this needs a geometry shader
* always).
* @requires_gl32 Extension @gl_extension{ARB,geometry_shader4}
* @requires_gles30 Not defined in OpenGL ES 2.0.
* @requires_gles32 Extension @gl_extension{ANDROID,extension_pack_es31a} /
* @gl_extension{EXT,geometry_shader}
* @requires_gles Geometry shaders are not available in WebGL.
* @m_since_latest
*/
TangentDirection = 1 << 2,
/**
* Visualize bitangent direction with green lines pointing out of
* vertices. You need to provide both @ref Normal and @ref Tangent4
* attributes in the mesh, alternatively you can provide the
* @ref Bitangent attribute and enable
* @ref Flag::BitangentDirection instead. Mutually exclusive with
* @ref Flag::NoGeometryShader (as this needs a geometry shader
* always).
* @requires_gl32 Extension @gl_extension{ARB,geometry_shader4}
* @requires_gles30 Not defined in OpenGL ES 2.0.
* @requires_gles32 Extension @gl_extension{ANDROID,extension_pack_es31a} /
* @gl_extension{EXT,geometry_shader}
* @requires_gles Geometry shaders are not available in WebGL.
* @m_since_latest
*/
BitangentFromTangentDirection = 1 << 3,
/**
* Visualize bitangent direction with green lines pointing out of
* vertices. You need to provide the @ref Bitangent attribute in
* the mesh, alternatively you can provide both @ref Normal and
* @ref Tangent4 attributes and enable
* @ref Flag::BitangentFromTangentDirection instead. Mutually
* exclusive with @ref Flag::NoGeometryShader (as this needs a
* geometry shader always).
* @requires_gl32 Extension @gl_extension{ARB,geometry_shader4}
* @requires_gles30 Not defined in OpenGL ES 2.0.
* @requires_gles32 Extension @gl_extension{ANDROID,extension_pack_es31a} /
* @gl_extension{EXT,geometry_shader}
* @requires_gles Geometry shaders are not available in WebGL.
* @m_since_latest
*/
BitangentDirection = 1 << 4,
/**
* Visualize normal direction with blue lines pointing out of
* vertices. You need to provide the @ref Normal attribute in the
* mesh. Mutually exclusive with @ref Flag::NoGeometryShader (as
* this needs a geometry shader always).
* @requires_gl32 Extension @gl_extension{ARB,geometry_shader4}
* @requires_gles30 Not defined in OpenGL ES 2.0.
* @requires_gles32 Extension @gl_extension{ANDROID,extension_pack_es31a} /
* @gl_extension{EXT,geometry_shader}
* @requires_gles Geometry shaders are not available in WebGL.
* @m_since_latest
*/
NormalDirection = 1 << 5
#endif
};
/** @brief Flags */
@ -428,24 +572,67 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali
return reinterpret_cast<const Flags&>(Implementation::MeshVisualizerBase::_flags);
}
#ifdef MAGNUM_BUILD_DEPRECATED
/**
* @brief Set transformation and projection matrix
* @m_deprecated_since_latest Use @ref setTransformationMatrix() and
* @ref setProjectionMatrix() instead.
*/
CORRADE_DEPRECATED("use setTransformationMatrix() and setProjectionMatrix() instead") MeshVisualizer3D& setTransformationProjectionMatrix(const Matrix4& matrix) {
/* Keep projection at identity, which should still work for
wireframe (but of course not for TBN visualization) */
return setTransformationMatrix(matrix);
}
#endif
/**
* @brief Set transformation matrix
* @return Reference to self (for method chaining)
*
* Initial value is an identity matrix.
*/
MeshVisualizer3D& setTransformationProjectionMatrix(const Matrix4& matrix);
MeshVisualizer3D& setTransformationMatrix(const Matrix4& matrix);
/**
* @brief Set projection matrix
* @return Reference to self (for method chaining)
*
* Initial value is an identity matrix. (i.e., an orthographic
* projection of the default @f$ [ -\boldsymbol{1} ; \boldsymbol{1} ] @f$
* cube).
*/
MeshVisualizer3D& setProjectionMatrix(const Matrix4& matrix);
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
/**
* @brief Set transformation matrix
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Expects that @ref Flag::TangentDirection,
* @ref Flag::BitangentDirection or @ref Flag::NormalDirection is
* enabled. The matrix doesn't need to be normalized, as
* renormalization is done per-fragment anyway.
* Initial value is an identity matrix.
* @requires_gl32 Extension @gl_extension{ARB,geometry_shader4}
* @requires_gles30 Not defined in OpenGL ES 2.0.
* @requires_gles32 Extension @gl_extension{ANDROID,extension_pack_es31a} /
* @gl_extension{EXT,geometry_shader}
* @requires_gles Geometry shaders are not available in WebGL.
*/
MeshVisualizer3D& setNormalMatrix(const Matrix3x3& matrix);
#endif
/**
* @brief Set viewport size
* @return Reference to self (for method chaining)
*
* Has effect only if @ref Flag::Wireframe is enabled and geometry
* shaders are used. Initial value is a zero vector.
* shaders are used; or if @ref Flag::TangentDirection,
* @ref Flag::BitangentDirection or @ref Flag::NormalDirection is
* enabled, otherwise it does nothing. Initial value is a zero vector.
*/
MeshVisualizer3D& setViewportSize(const Vector2& size) {
return static_cast<MeshVisualizer3D&>(Implementation::MeshVisualizerBase::setViewportSize(size));
}
MeshVisualizer3D& setViewportSize(const Vector2& size);
/**
* @brief Set base object color
@ -472,27 +659,74 @@ class MAGNUM_SHADERS_EXPORT MeshVisualizer3D: public Implementation::MeshVisuali
* @brief Set wireframe width
* @return Reference to self (for method chaining)
*
* Initial value is @cpp 1.0f @ce. Has effect only if
* @ref Flag::Wireframe is enabled.
* Value is in screen space (depending on @ref setViewportSize()),
* initial value is @cpp 1.0f @ce. Expects that @ref Flag::Wireframe is
* enabled.
*/
MeshVisualizer3D& setWireframeWidth(Float width) {
return static_cast<MeshVisualizer3D&>(Implementation::MeshVisualizerBase::setWireframeWidth(width));
}
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
/**
* @brief Set line width
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Value is in screen space (depending on @ref setViewportSize()),
* initial value is @cpp 1.0f @ce. Expects that
* @ref Flag::TangentDirection,
* @ref Flag::BitangentFromTangentDirection,
* @ref Flag::BitangentDirection or @ref Flag::NormalDirection is
* enabled.
* @requires_gl32 Extension @gl_extension{ARB,geometry_shader4}
* @requires_gles30 Not defined in OpenGL ES 2.0.
* @requires_gles32 Extension @gl_extension{ANDROID,extension_pack_es31a} /
* @gl_extension{EXT,geometry_shader}
* @requires_gles Geometry shaders are not available in WebGL.
*/
MeshVisualizer3D& setLineWidth(Float width);
/**
* @brief Set line length
* @return Reference to self (for method chaining)
* @m_since_latest
*
* Value is in object space, initial value is @cpp 1.0f @ce. Expects
* that @ref Flag::TangentDirection,
* @ref Flag::BitangentFromTangentDirection,
* @ref Flag::BitangentDirection or @ref Flag::NormalDirection is
* enabled.
* @requires_gl32 Extension @gl_extension{ARB,geometry_shader4}
* @requires_gles30 Not defined in OpenGL ES 2.0.
* @requires_gles32 Extension @gl_extension{ANDROID,extension_pack_es31a} /
* @gl_extension{EXT,geometry_shader}
* @requires_gles Geometry shaders are not available in WebGL.
*/
MeshVisualizer3D& setLineLength(Float length);
#endif
/**
* @brief Set line smoothness
* @return Reference to self (for method chaining)
*
* Value is in screen space (depending on @ref setViewportSize()),
* initial value is @cpp 2.0f @ce. Expects that @ref Flag::Wireframe
* is enabled.
* initial value is @cpp 2.0f @ce. Expects that @ref Flag::Wireframe,
* @ref Flag::TangentDirection,
* @ref Flag::BitangentFromTangentDirection,
* @ref Flag::BitangentDirection or @ref Flag::NormalDirection is
* enabled.
*/
MeshVisualizer3D& setSmoothness(Float smoothness) {
return static_cast<MeshVisualizer3D&>(Implementation::MeshVisualizerBase::setSmoothness(smoothness));
}
MeshVisualizer3D& setSmoothness(Float smoothness);
private:
Int _transformationProjectionMatrixUniform{0};
Int _transformationMatrixUniform{0},
_projectionMatrixUniform{6};
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
Int _normalMatrixUniform{7},
_lineWidthUniform{8},
_lineLengthUniform{9};
#endif
};
#ifdef MAGNUM_BUILD_DEPRECATED

81
src/Magnum/Shaders/MeshVisualizer.vert

@ -28,17 +28,28 @@
#define out varying
#endif
#ifdef TWO_DIMENSIONS
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 0)
#endif
#ifdef TWO_DIMENSIONS
uniform highp mat3 transformationProjectionMatrix
#ifndef GL_ES
= mat3(1.0)
#endif
;
#elif defined(THREE_DIMENSIONS)
uniform highp mat4 transformationProjectionMatrix
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 0)
#endif
uniform highp mat4 transformationMatrix
#ifndef GL_ES
= mat4(1.0)
#endif
;
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 6)
#endif
uniform highp mat4 projectionMatrix
#ifndef GL_ES
= mat4(1.0)
#endif
@ -47,6 +58,26 @@ uniform highp mat4 transformationProjectionMatrix
#error
#endif
#if defined(TANGENT_DIRECTION) || defined(BITANGENT_FROM_TANGENT_DIRECTION) || defined(BITANGENT_DIRECTION) || defined(NORMAL_DIRECTION)
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 7)
#endif
uniform highp mat3 normalMatrix
#ifndef GL_ES
= mat3(1.0)
#endif
;
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 9)
#endif
uniform highp float lineLength
#ifndef GL_ES
= 1.0
#endif
;
#endif
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = POSITION_ATTRIBUTE_LOCATION)
#endif
@ -58,6 +89,27 @@ in highp vec4 position;
#error
#endif
#if defined(TANGENT_DIRECTION) || defined(BITANGENT_FROM_TANGENT_DIRECTION)
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = TANGENT_ATTRIBUTE_LOCATION)
#endif
in highp vec4 tangent;
#endif
#ifdef BITANGENT_DIRECTION
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = BITANGENT_ATTRIBUTE_LOCATION)
#endif
in highp vec3 bitangent;
#endif
#if defined(NORMAL_DIRECTION) || defined(BITANGENT_FROM_TANGENT_DIRECTION)
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = NORMAL_ATTRIBUTE_LOCATION)
#endif
in highp vec3 normal;
#endif
#if defined(WIREFRAME_RENDERING) && defined(NO_GEOMETRY_SHADER)
#if (!defined(GL_ES) && __VERSION__ < 140) || (defined(GL_ES) && __VERSION__ < 300)
#ifdef EXPLICIT_ATTRIB_LOCATION
@ -70,15 +122,38 @@ in lowp float vertexIndex;
out vec3 barycentric;
#endif
#ifdef TANGENT_DIRECTION
out highp vec4 tangentEndpoint;
#endif
#if defined(BITANGENT_DIRECTION) || defined(BITANGENT_FROM_TANGENT_DIRECTION)
out highp vec4 bitangentEndpoint;
#endif
#ifdef NORMAL_DIRECTION
out highp vec4 normalEndpoint;
#endif
void main() {
#ifdef TWO_DIMENSIONS
gl_Position.xywz = vec4(transformationProjectionMatrix*vec3(position, 1.0), 0.0);
#elif defined(THREE_DIMENSIONS)
gl_Position = transformationProjectionMatrix*position;
gl_Position = projectionMatrix*transformationMatrix*position;
#else
#error
#endif
#ifdef TANGENT_DIRECTION
tangentEndpoint = projectionMatrix*(transformationMatrix*position + vec4(normalize(normalMatrix*tangent.xyz)*lineLength, 0.0));
#endif
#ifdef BITANGENT_FROM_TANGENT_DIRECTION
vec3 bitangent = cross(normal, tangent.xyz)*tangent.w;
#endif
#if defined(BITANGENT_DIRECTION) || defined(BITANGENT_FROM_TANGENT_DIRECTION)
bitangentEndpoint = projectionMatrix*(transformationMatrix*position + vec4(normalize(normalMatrix*bitangent)*lineLength, 0.0));
#endif
#ifdef NORMAL_DIRECTION
normalEndpoint = projectionMatrix*(transformationMatrix*position + vec4(normalize(normalMatrix*normal)*lineLength, 0.0));
#endif
#if defined(WIREFRAME_RENDERING) && defined(NO_GEOMETRY_SHADER)
barycentric = vec3(0.0);

349
src/Magnum/Shaders/Test/MeshVisualizerGLTest.cpp

@ -68,7 +68,10 @@ struct MeshVisualizerGLTest: GL::OpenGLTester {
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
void constructWireframeGeometryShader2D();
void constructWireframeGeometryShader3D();
void constructGeometryShader3D();
void construct3DGeometryShaderDisabledButNeeded();
void construct3DConflictingBitangentInput();
#endif
void constructMove2D();
@ -76,6 +79,9 @@ struct MeshVisualizerGLTest: GL::OpenGLTester {
void setWireframeNotEnabled2D();
void setWireframeNotEnabled3D();
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
void setTangentBitangentNormalNotEnabled3D();
#endif
void renderSetup();
void renderTeardown();
@ -85,6 +91,7 @@ struct MeshVisualizerGLTest: GL::OpenGLTester {
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
void renderDefaultsWireframe2D();
void renderDefaultsWireframe3D();
void renderDefaultsTangentBitangentNormal();
#endif
void render2D();
void render3D();
@ -92,13 +99,15 @@ struct MeshVisualizerGLTest: GL::OpenGLTester {
void renderWireframe3D();
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
void renderWireframe3DPerspective();
void renderTangentBitangentNormal();
#endif
private:
PluginManager::Manager<Trade::AbstractImporter> _manager{"nonexistent"};
std::string _testDir;
GL::Renderbuffer _color{NoCreate};
GL::Renderbuffer _color{NoCreate},
_depth{NoCreate};
#ifndef MAGNUM_TARGET_GLES2
GL::Renderbuffer _objectId{NoCreate};
#endif
@ -111,7 +120,7 @@ struct MeshVisualizerGLTest: GL::OpenGLTester {
- Mesa Intel
- Mesa AMD
- SwiftShader ES2/ES3
- ARM Mali (Huawei P10) ES2/ES3
- ARM Mali (Huawei P10) ES2/ES3 (except TBN visualization)
- WebGL 1 / 2 (on Mesa Intel)
- iPhone 6 w/ iOS 12.4
*/
@ -134,6 +143,22 @@ constexpr struct {
{"wireframe w/o GS", MeshVisualizer3D::Flag::Wireframe|MeshVisualizer3D::Flag::NoGeometryShader}
};
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
constexpr struct {
const char* name;
MeshVisualizer3D::Flags flags;
} ConstructGeometryShaderData3D[] {
{"wireframe", MeshVisualizer3D::Flag::Wireframe},
{"tangent direction", MeshVisualizer3D::Flag::TangentDirection},
{"bitangent direction from tangent", MeshVisualizer3D::Flag::BitangentFromTangentDirection},
{"bitangent direction", MeshVisualizer3D::Flag::BitangentDirection},
{"normal direction", MeshVisualizer3D::Flag::NormalDirection},
{"tbn direction", MeshVisualizer3D::Flag::TangentDirection|MeshVisualizer3D::Flag::BitangentDirection|MeshVisualizer3D::Flag::NormalDirection},
{"tbn direction with bitangent from tangent", MeshVisualizer3D::Flag::TangentDirection|MeshVisualizer3D::Flag::BitangentFromTangentDirection|MeshVisualizer3D::Flag::NormalDirection},
{"wireframe + t/n direction", MeshVisualizer3D::Flag::Wireframe|MeshVisualizer3D::Flag::TangentDirection|MeshVisualizer3D::Flag::NormalDirection}
};
#endif
constexpr struct {
const char* name;
MeshVisualizer2D::Flags flags;
@ -174,6 +199,60 @@ constexpr struct {
3.0f, 1.0f, "wireframe-wide3D.tga", "wireframe-nogeo3D.tga"}
};
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
constexpr struct {
const char* name;
MeshVisualizer3D::Flags flags;
MeshVisualizer3D::Flags secondPassFlags;
bool skipBitagnentEvenIfEnabledInFlags;
Float smoothness;
Float lineWidth;
Float lineLength;
Float multiply;
const char* file;
} TangentBitangentNormalData[] {
{"",
MeshVisualizer3D::Flag::TangentDirection|
MeshVisualizer3D::Flag::BitangentDirection|
MeshVisualizer3D::Flag::NormalDirection, {},
false, 2.0f, 1.0f, 0.6f, 1.0f, "tbn.tga"},
{"bitangents from tangents",
MeshVisualizer3D::Flag::TangentDirection|
MeshVisualizer3D::Flag::BitangentFromTangentDirection|
MeshVisualizer3D::Flag::NormalDirection, {},
false, 2.0f, 1.0f, 0.6f, 1.0f, "tbn.tga"},
{"scaled data",
MeshVisualizer3D::Flag::TangentDirection|
MeshVisualizer3D::Flag::BitangentDirection|
MeshVisualizer3D::Flag::NormalDirection, {},
false, 2.0f, 1.0f, 0.6f, 5.0f, "tbn.tga"},
{"wide blurry lines",
MeshVisualizer3D::Flag::TangentDirection|
MeshVisualizer3D::Flag::BitangentDirection|
MeshVisualizer3D::Flag::NormalDirection, {},
false, 5.0f, 5.0f, 0.8f, 1.0f, "tbn-wide.tga"},
{"only bitangent from tangent",
MeshVisualizer3D::Flag::BitangentFromTangentDirection, {},
false, 2.0f, 1.0f, 0.6f, 1.0f, "bitangents-from-tangents.tga"},
{"wireframe + tangents + normals, single pass",
MeshVisualizer3D::Flag::Wireframe|
MeshVisualizer3D::Flag::TangentDirection|
MeshVisualizer3D::Flag::NormalDirection, {},
false, 2.0f, 1.0f, 0.6f, 1.0f, "wireframe-tn.tga"},
{"wireframe, rendering all, but only tangents + normals present",
MeshVisualizer3D::Flag::Wireframe|
MeshVisualizer3D::Flag::TangentDirection|
MeshVisualizer3D::Flag::BitangentDirection|
MeshVisualizer3D::Flag::NormalDirection, {},
true, 2.0f, 1.0f, 0.6f, 1.0f, "wireframe-tn.tga"},
{"wireframe + tangents + normals, two passes",
MeshVisualizer3D::Flag::TangentDirection|
MeshVisualizer3D::Flag::NormalDirection,
MeshVisualizer3D::Flag::Wireframe,
false, 2.0f, 1.0f, 0.6f, 1.0f, "wireframe-tn-smooth.tga"},
};
#endif
MeshVisualizerGLTest::MeshVisualizerGLTest() {
addInstancedTests({&MeshVisualizerGLTest::construct2D},
Containers::arraySize(ConstructData2D));
@ -181,23 +260,35 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() {
addInstancedTests({&MeshVisualizerGLTest::construct3D},
Containers::arraySize(ConstructData3D));
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
addTests({&MeshVisualizerGLTest::constructWireframeGeometryShader2D});
addInstancedTests({&MeshVisualizerGLTest::constructGeometryShader3D},
Containers::arraySize(ConstructGeometryShaderData3D));
#endif
addTests({
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
&MeshVisualizerGLTest::constructWireframeGeometryShader2D,
&MeshVisualizerGLTest::constructWireframeGeometryShader3D,
&MeshVisualizerGLTest::construct3DGeometryShaderDisabledButNeeded,
&MeshVisualizerGLTest::construct3DConflictingBitangentInput,
#endif
&MeshVisualizerGLTest::constructMove2D,
&MeshVisualizerGLTest::constructMove3D,
&MeshVisualizerGLTest::setWireframeNotEnabled2D,
&MeshVisualizerGLTest::setWireframeNotEnabled3D});
&MeshVisualizerGLTest::setWireframeNotEnabled3D,
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
&MeshVisualizerGLTest::setTangentBitangentNormalNotEnabled3D,
#endif
});
addTests({&MeshVisualizerGLTest::renderDefaults2D,
&MeshVisualizerGLTest::renderDefaults3D,
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
&MeshVisualizerGLTest::renderDefaultsWireframe2D,
&MeshVisualizerGLTest::renderDefaultsWireframe3D,
&MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal,
#endif
&MeshVisualizerGLTest::render2D,
&MeshVisualizerGLTest::render3D},
@ -218,6 +309,11 @@ MeshVisualizerGLTest::MeshVisualizerGLTest() {
addTests({&MeshVisualizerGLTest::renderWireframe3DPerspective},
&MeshVisualizerGLTest::renderSetup,
&MeshVisualizerGLTest::renderTeardown);
addInstancedTests({&MeshVisualizerGLTest::renderTangentBitangentNormal},
Containers::arraySize(TangentBitangentNormalData),
&MeshVisualizerGLTest::renderSetup,
&MeshVisualizerGLTest::renderTeardown);
#endif
/* Load the plugins directly from the build tree. Otherwise they're either
@ -304,7 +400,10 @@ void MeshVisualizerGLTest::constructWireframeGeometryShader2D() {
}
}
void MeshVisualizerGLTest::constructWireframeGeometryShader3D() {
void MeshVisualizerGLTest::constructGeometryShader3D() {
auto&& data = ConstructGeometryShaderData3D[testCaseInstanceId()];
setTestCaseDescription(data.name);
#ifndef MAGNUM_TARGET_GLES
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::geometry_shader4>())
CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported"));
@ -318,8 +417,8 @@ void MeshVisualizerGLTest::constructWireframeGeometryShader3D() {
Debug() << "Using" << GL::Extensions::NV::shader_noperspective_interpolation::string();
#endif
MeshVisualizer3D shader{MeshVisualizer3D::Flag::Wireframe};
CORRADE_COMPARE(shader.flags(), MeshVisualizer3D::Flag::Wireframe);
MeshVisualizer3D shader{data.flags};
CORRADE_COMPARE(shader.flags(), data.flags);
{
#ifdef CORRADE_TARGET_APPLE
CORRADE_EXPECT_FAIL("macOS drivers need insane amount of state to validate properly.");
@ -330,6 +429,40 @@ void MeshVisualizerGLTest::constructWireframeGeometryShader3D() {
}
#endif
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
void MeshVisualizerGLTest::construct3DGeometryShaderDisabledButNeeded() {
#ifndef MAGNUM_TARGET_GLES
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::geometry_shader4>())
CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported"));
#else
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::geometry_shader>())
CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() + std::string(" is not supported"));
#endif
std::ostringstream out;
Error redirectError{&out};
MeshVisualizer3D{MeshVisualizer3D::Flag::NoGeometryShader|MeshVisualizer3D::Flag::NormalDirection};
CORRADE_COMPARE(out.str(),
"Shaders::MeshVisualizer3D: geometry shader has to be enabled when rendering TBN direction\n");
}
void MeshVisualizerGLTest::construct3DConflictingBitangentInput() {
#ifndef MAGNUM_TARGET_GLES
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::geometry_shader4>())
CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported"));
#else
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::geometry_shader>())
CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() + std::string(" is not supported"));
#endif
std::ostringstream out;
Error redirectError{&out};
MeshVisualizer3D{MeshVisualizer3D::Flag::BitangentFromTangentDirection|MeshVisualizer3D::Flag::BitangentDirection};
CORRADE_COMPARE(out.str(),
"Shaders::MeshVisualizer3D: Flag::BitangentDirection and Flag::BitangentFromTangentDirection are mutually exclusive\n");
}
#endif
void MeshVisualizerGLTest::constructMove2D() {
MeshVisualizer2D a{MeshVisualizer2D::Flag::Wireframe|MeshVisualizer2D::Flag::NoGeometryShader};
const GLuint id = a.id();
@ -380,7 +513,7 @@ void MeshVisualizerGLTest::setWireframeNotEnabled2D() {
CORRADE_COMPARE(out.str(),
"Shaders::MeshVisualizer::setWireframeColor(): the shader was not created with wireframe enabled\n"
"Shaders::MeshVisualizer::setWireframeWidth(): the shader was not created with wireframe enabled\n"
"Shaders::MeshVisualizer::setSmoothness(): the shader was not created with wireframe enabled\n");
"Shaders::MeshVisualizer2D::setSmoothness(): the shader was not created with wireframe enabled\n");
}
void MeshVisualizerGLTest::setWireframeNotEnabled3D() {
@ -395,9 +528,28 @@ void MeshVisualizerGLTest::setWireframeNotEnabled3D() {
CORRADE_COMPARE(out.str(),
"Shaders::MeshVisualizer::setWireframeColor(): the shader was not created with wireframe enabled\n"
"Shaders::MeshVisualizer::setWireframeWidth(): the shader was not created with wireframe enabled\n"
"Shaders::MeshVisualizer::setSmoothness(): the shader was not created with wireframe enabled\n");
"Shaders::MeshVisualizer3D::setSmoothness(): the shader was not created with wireframe or TBN direction enabled\n");
}
#if !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
void MeshVisualizerGLTest::setTangentBitangentNormalNotEnabled3D() {
std::ostringstream out;
Error redirectError{&out};
MeshVisualizer3D shader;
shader.setNormalMatrix({})
.setLineWidth({})
.setLineLength({})
.setSmoothness({});
CORRADE_COMPARE(out.str(),
"Shaders::MeshVisualizer3D::setNormalMatrix(): the shader was not created with TBN direction enabled\n"
"Shaders::MeshVisualizer3D::setLineWidth(): the shader was not created with TBN direction enabled\n"
"Shaders::MeshVisualizer3D::setLineLength(): the shader was not created with TBN direction enabled\n"
"Shaders::MeshVisualizer3D::setSmoothness(): the shader was not created with wireframe or TBN direction enabled\n");
}
#endif
constexpr Vector2i RenderSize{80, 80};
void MeshVisualizerGLTest::renderSetup() {
@ -414,10 +566,19 @@ void MeshVisualizerGLTest::renderSetup() {
GL::RenderbufferFormat::RGBA4,
#endif
RenderSize);
_depth = GL::Renderbuffer{};
_depth.setStorage(GL::RenderbufferFormat::DepthComponent16, RenderSize);
_framebuffer = GL::Framebuffer{{{}, RenderSize}};
_framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color)
.clear(GL::FramebufferClear::Color)
_framebuffer
.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color)
.attachRenderbuffer(GL::Framebuffer::BufferAttachment::Depth, _depth)
.clear(GL::FramebufferClear::Color|GL::FramebufferClear::Depth)
.bind();
/* Disable depth test & blending by default, particular tests enable it if
needed */
GL::Renderer::disable(GL::Renderer::Feature::DepthTest);
GL::Renderer::disable(GL::Renderer::Feature::Blending);
}
void MeshVisualizerGLTest::renderTeardown() {
@ -575,6 +736,18 @@ void MeshVisualizerGLTest::renderDefaultsWireframe3D() {
/* AMD has off-by-one errors on edges compared to Intel */
(DebugTools::CompareImageToFile{_manager, 1.0f, 0.06f}));
}
void MeshVisualizerGLTest::renderDefaultsTangentBitangentNormal() {
#ifndef MAGNUM_TARGET_GLES
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::geometry_shader4>())
CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported"));
#else
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::geometry_shader>())
CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() + std::string(" is not supported"));
#endif
CORRADE_SKIP("Needs compile() and Primitives supporting TBN, which is not done yet.");
}
#endif
void MeshVisualizerGLTest::render2D() {
@ -610,11 +783,12 @@ void MeshVisualizerGLTest::render3D() {
MeshVisualizer3D{}
.setColor(0x9999ff_rgbf)
.setTransformationProjectionMatrix(
Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)*
.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))
.draw(sphere);
MAGNUM_VERIFY_NO_GL_ERROR();
@ -775,11 +949,11 @@ void MeshVisualizerGLTest::renderWireframe3D() {
.setWireframeWidth(data.width)
.setSmoothness(data.smoothness)
.setViewportSize({80, 80})
.setTransformationProjectionMatrix(
Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)*
.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))
.draw(sphere);
MAGNUM_VERIFY_NO_GL_ERROR();
@ -845,11 +1019,11 @@ void MeshVisualizerGLTest::renderWireframe3DPerspective() {
.setWireframeWidth(8.0f)
.setWireframeColor(0xff0000_rgbf)
.setViewportSize({80, 80})
.setTransformationProjectionMatrix(
Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)*
.setTransformationMatrix(
Matrix4::translation({0.0f, 0.5f, -3.5f})*
Matrix4::rotationX(-60.0_degf)*
Matrix4::scaling(Vector3::yScale(2.0f)))
.setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f))
.draw(plane);
MAGNUM_VERIFY_NO_GL_ERROR();
@ -870,6 +1044,143 @@ void MeshVisualizerGLTest::renderWireframe3DPerspective() {
Utility::Directory::join(_testDir, "MeshVisualizerTestFiles/wireframe-perspective.tga"),
(DebugTools::CompareImageToFile{_manager, 0.667f, 0.002f}));
}
void MeshVisualizerGLTest::renderTangentBitangentNormal() {
#ifndef MAGNUM_TARGET_GLES
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::geometry_shader4>())
CORRADE_SKIP(GL::Extensions::ARB::geometry_shader4::string() + std::string(" is not supported"));
#else
if(!GL::Context::current().isExtensionSupported<GL::Extensions::EXT::geometry_shader>())
CORRADE_SKIP(GL::Extensions::EXT::geometry_shader::string() + std::string(" is not supported"));
#endif
auto&& data = TangentBitangentNormalData[testCaseInstanceId()];
setTestCaseDescription(data.name);
GL::Renderer::enable(GL::Renderer::Feature::DepthTest);
GL::Renderer::enable(GL::Renderer::Feature::Blending);
GL::Renderer::setBlendFunction(GL::Renderer::BlendFunction::One, GL::Renderer::BlendFunction::OneMinusSourceAlpha);
/* Creating a primitive from scratch because Primitives::planeSolid() is
too regular to test everything properly */
struct Vertex {
Vector3 position;
Vector4 tangent;
Vector3 bitangent;
Vector3 normal;
} vertexData[] {
{{ 1.0f, -1.0f, 0.0f},
Vector4{Vector3{1.25f, 0.0f, 0.25f}.normalized(), -1.0f}, {},
Vector3{0.25f, 0.0f, -1.25f}.normalized()},
{{ 1.0f, 1.0f, 0.0f},
Vector4{Vector3{-1.0f, 0.25f, 0.0f}.normalized(), -1.0f}, {},
Vector3{-0.25f, -1.0f, 0.0f}.normalized()},
{{-1.0f, -1.0f, 0.0f},
{1.0f, 0.0f, 0.0f, -1.0f}, {},
{0.0f, 0.0f, 1.0f}},
{{-1.0f, 1.0f, 0.0f},
Vector4{Vector3{0.75f, 0.0f, -0.25f}.normalized(), 1.0f}, {},
Vector3{0.25f, 0.0f, 0.75f}.normalized()}
};
/* Calculate bitangents from normal+tangent */
for(Vertex& i: vertexData)
i.bitangent = Math::cross(i.normal, i.tangent.xyz())*i.tangent.w();
/* Verify the TBN is orthogonal */
for(Vertex& i: vertexData) {
CORRADE_ITERATION(i.position);
CORRADE_VERIFY(i.tangent.xyz().isNormalized());
CORRADE_VERIFY(i.bitangent.isNormalized());
CORRADE_VERIFY(i.normal.isNormalized());
CORRADE_COMPARE(dot(i.normal, i.tangent.xyz()), 0.0f);
CORRADE_COMPARE(dot(i.normal, i.bitangent), 0.0f);
CORRADE_COMPARE(dot(i.tangent.xyz(), i.bitangent), 0.0f);
}
/* Apply scale to all */
for(Vertex& i: vertexData) {
i.tangent *= data.multiply;
i.bitangent *= data.multiply;
i.normal *= data.multiply;
}
GL::Buffer vertices{vertexData};
GL::Mesh mesh{MeshPrimitive::TriangleStrip};
mesh.setCount(4)
.addVertexBuffer(vertices, 0,
Shaders::MeshVisualizer3D::Position{},
sizeof(Vector4), /* conditionally added below */
sizeof(Vector3), /* conditionally added below */
Shaders::MeshVisualizer3D::Normal{});
if(data.flags & MeshVisualizer3D::Flag::BitangentFromTangentDirection && !data.skipBitagnentEvenIfEnabledInFlags)
mesh.addVertexBuffer(vertices, 0,
sizeof(Vector3),
Shaders::MeshVisualizer3D::Tangent4{},
sizeof(Vector3),
sizeof(Vector3));
else if(data.flags & MeshVisualizer3D::Flag::TangentDirection)
mesh.addVertexBuffer(vertices, 0,
sizeof(Vector3),
Shaders::MeshVisualizer3D::Tangent{}, sizeof(Float), sizeof(Vector3),
sizeof(Vector3));
if(data.flags & MeshVisualizer3D::Flag::BitangentDirection && !data.skipBitagnentEvenIfEnabledInFlags)
mesh.addVertexBuffer(vertices, 0,
sizeof(Vector3),
sizeof(Vector4),
Shaders::MeshVisualizer3D::Bitangent{},
sizeof(Vector3));
Matrix4 transformation = Matrix4::translation({0.0f, 0.5f, -3.5f})*
Matrix4::rotationX(-60.0_degf)*
Matrix4::scaling(Vector3::yScale(1.5f));
if(data.secondPassFlags) {
MeshVisualizer3D{data.secondPassFlags}
/** @todo make this unnecessary */
.setViewportSize({80, 80})
.setTransformationMatrix(transformation)
.setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f))
.setColor(0xffff99_rgbf)
.setWireframeColor(0x9999ff_rgbf)
.draw(mesh);
}
MeshVisualizer3D shader{data.flags};
shader
/** @todo make this unnecessary */
.setViewportSize({80, 80})
.setTransformationMatrix(transformation)
.setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f))
.setNormalMatrix(transformation.normalMatrix()*data.multiply)
.setSmoothness(data.smoothness)
.setLineLength(data.lineLength)
.setLineWidth(data.lineWidth);
if(data.flags & MeshVisualizer3D::Flag::Wireframe) shader
.setColor(0xffff99_rgbf)
.setWireframeColor(0x9999ff_rgbf);
shader.draw(mesh);
MAGNUM_VERIFY_NO_GL_ERROR();
/* Slight rasterization differences on AMD. If
GL_NV_shader_noperspective_interpolation is not supported, the artifacts
are bigger. */
Float maxThreshold = 1.334f, meanThreshold = 0.008f;
#ifdef MAGNUM_TARGET_GLES
if(!(data.flags & MeshVisualizer3D::Flag::NoGeometryShader) && !GL::Context::current().isExtensionSupported<GL::Extensions::NV::shader_noperspective_interpolation>()) {
maxThreshold = 39.0f;
meanThreshold = 1.207f;
}
#endif
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::Directory::join({_testDir, "MeshVisualizerTestFiles", data.file}),
(DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold}));
}
#endif
}}}}

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/bitangents-from-tangents.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/tbn-wide.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/tbn.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-tn-smooth.tga

Binary file not shown.

BIN
src/Magnum/Shaders/Test/MeshVisualizerTestFiles/wireframe-tn.tga

Binary file not shown.
Loading…
Cancel
Save