Browse Source

Shaders: support object ID output in Flat and Phong.

pull/364/head
Vladimír Vondruš 7 years ago
parent
commit
87ac8a8cd2
  1. 1
      doc/changelog.dox
  2. 28
      doc/shaders.dox
  3. 39
      doc/snippets/MagnumShaders.cpp
  4. 35
      src/Magnum/Shaders/Flat.cpp
  5. 22
      src/Magnum/Shaders/Flat.frag
  6. 73
      src/Magnum/Shaders/Flat.h
  7. 30
      src/Magnum/Shaders/Generic.h
  8. 37
      src/Magnum/Shaders/Phong.cpp
  9. 40
      src/Magnum/Shaders/Phong.frag
  10. 73
      src/Magnum/Shaders/Phong.h
  11. 4
      src/Magnum/Shaders/Phong.vert
  12. 177
      src/Magnum/Shaders/Test/FlatGLTest.cpp
  13. 123
      src/Magnum/Shaders/Test/PhongGLTest.cpp
  14. 4
      src/Magnum/Shaders/generic.glsl

1
doc/changelog.dox

@ -226,6 +226,7 @@ See also:
- Normal texture support in @ref Shaders::Phong
- Added @ref Shaders::Generic3D::Tangent generic vertex attribute definition
- Object ID output in @ref Shaders::Flat and @ref Shaders::Phong
@subsubsection changelog-latest-new-text Text library

28
doc/shaders.dox

@ -73,27 +73,35 @@ Example configuration and rendering using @link Shaders::Phong @endlink:
@snippet MagnumShaders.cpp shaders-rendering
@section shaders-generic Generic vertex attributes
@section shaders-generic Generic vertex attributes and framebuffer attachments
Many shaders share the same vertex attribute definitions, such as positions,
normals, texture coordinates etc. It's thus possible to configure the mesh
for *generic* shader and then render it with any compatible shader. Definition
of generic attributes is available in @ref Shaders::Generic class.
Configuration of the above mesh using generic attributes could then look like
this:
for a *generic* shader and then render it with any compatible shader.
Definition of all generic attributes is available in the @ref Shaders::Generic
class. Configuration of the above mesh using generic attributes could then look
like this:
@snippet MagnumShaders.cpp shaders-generic
Note that in this particular case both configurations are equivalent, because
@ref Shaders::Phong also uses generic vertex attribute definitions.
Then you can render the mesh using @ref Shaders::Phong shader like above, or
use for example @ref Shaders::Flat3D or even @ref Shaders::MeshVisualizer with
the same mesh reconfiguration. The unused attributes will be simply ignored.
@ref Shaders::Phong also uses generic vertex attribute definitions. Then you
can render the mesh using @ref Shaders::Phong shader like above, or use for
example @ref Shaders::Flat3D or even @ref Shaders::MeshVisualizer with the same
mesh reconfiguration. The unused attributes will be simply ignored.
@snippet MagnumShaders.cpp shaders-meshvisualizer
The @ref MeshTools::compile() utility configures meshes using generic vertex
attribute definitions to make them usable with any shader.
Besides vertex attributes, the @ref Shaders::Generic contains generic
definitions for framebuffer outputs as well --- in many cases a shader has just
one (color) output, but some shaders such as @ref Shaders::Flat or
@ref Shaders::Phong offer an object ID output as well. A setup equivalent to
what's done in Flat shader's @ref Shaders-Flat-usage-object-id but using the
generic definitions would look like this:
@snippet MagnumShaders.cpp shaders-generic-object-id
*/
}

39
doc/snippets/MagnumShaders.cpp

@ -29,7 +29,10 @@
#include "Magnum/GL/Buffer.h"
#include "Magnum/GL/DefaultFramebuffer.h"
#include "Magnum/GL/Framebuffer.h"
#include "Magnum/GL/Mesh.h"
#include "Magnum/GL/Renderbuffer.h"
#include "Magnum/GL/RenderbufferFormat.h"
#include "Magnum/GL/Texture.h"
#include "Magnum/Math/Color.h"
#include "Magnum/MeshTools/Duplicate.h"
@ -204,6 +207,42 @@ mesh.draw(shader);
/* [Flat-usage-textured2] */
}
#ifndef MAGNUM_TARGET_GLES2
{
GL::Framebuffer framebuffer{{}};
GL::Mesh mesh;
Vector2i size;
UnsignedInt meshId{};
/* [Flat-usage-object-id] */
GL::Renderbuffer color, objectId;
color.setStorage(GL::RenderbufferFormat::RGBA8, size);
objectId.setStorage(GL::RenderbufferFormat::R16UI, size); // large as needed
framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, color)
.attachRenderbuffer(GL::Framebuffer::ColorAttachment{1}, objectId);
Shaders::Flat3D shader{Shaders::Flat3D::Flag::ObjectId};
// ...
framebuffer.mapForDraw({
{Shaders::Flat3D::ColorOutput, GL::Framebuffer::ColorAttachment{0}},
{Shaders::Flat3D::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}}})
.clearColor(0, 0x1f1f1f_rgbf)
.clearColor(1, Vector4ui{0})
.bind();
shader.setObjectId(meshId);
mesh.draw(shader);
/* [Flat-usage-object-id] */
/* [shaders-generic-object-id] */
framebuffer.mapForDraw({
{Shaders::Generic3D::ColorOutput, GL::Framebuffer::ColorAttachment{0}},
{Shaders::Generic3D::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}}});
/* [shaders-generic-object-id] */
}
#endif
{
/* [MeshVisualizer-usage-geom1] */
struct Vertex {

35
src/Magnum/Shaders/Flat.cpp

@ -68,13 +68,18 @@ template<UnsignedInt dimensions> Flat<dimensions>::Flat(const Flags flags): _fla
.addSource(rs.get(vertexShaderName<dimensions>()));
frag.addSource(flags & Flag::Textured ? "#define TEXTURED\n" : "")
.addSource(flags & Flag::AlphaMask ? "#define ALPHA_MASK\n" : "")
#ifndef MAGNUM_TARGET_GLES2
.addSource(flags & Flag::ObjectId ? "#define OBJECT_ID\n" : "")
#endif
.addSource(rs.get("generic.glsl"))
.addSource(rs.get("Flat.frag"));
CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag}));
attachShaders({vert, frag});
/* ES3 has this done in the shader directly */
/* ES3 has this done in the shader directly and doesn't even provide
bindFragmentDataLocation() */
#if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2)
#ifndef MAGNUM_TARGET_GLES
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::explicit_attrib_location>(version))
@ -82,6 +87,12 @@ template<UnsignedInt dimensions> Flat<dimensions>::Flat(const Flags flags): _fla
{
bindAttributeLocation(Position::Location, "position");
if(flags & Flag::Textured) bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates");
#ifndef MAGNUM_TARGET_GLES2
if(flags & Flag::ObjectId) {
bindFragmentDataLocation(ColorOutput, "color");
bindFragmentDataLocation(ObjectIdOutput, "objectId");
}
#endif
}
#endif
@ -94,6 +105,9 @@ template<UnsignedInt dimensions> Flat<dimensions>::Flat(const Flags flags): _fla
_transformationProjectionMatrixUniform = uniformLocation("transformationProjectionMatrix");
_colorUniform = uniformLocation("color");
if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask");
#ifndef MAGNUM_TARGET_GLES2
if(flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId");
#endif
}
#ifndef MAGNUM_TARGET_GLES
@ -108,6 +122,7 @@ template<UnsignedInt dimensions> Flat<dimensions>::Flat(const Flags flags): _fla
setTransformationProjectionMatrix({});
setColor(Color4{1.0f});
if(flags & Flag::AlphaMask) setAlphaMask(0.5f);
/* Object ID is zero by default */
#endif
}
@ -125,6 +140,15 @@ template<UnsignedInt dimensions> Flat<dimensions>& Flat<dimensions>::setAlphaMas
return *this;
}
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> Flat<dimensions>& Flat<dimensions>::setObjectId(UnsignedInt id) {
CORRADE_ASSERT(_flags & Flag::ObjectId,
"Shaders::Flat::setObjectId(): the shader was not created with object ID enabled", *this);
setUniform(_objectIdUniform, id);
return *this;
}
#endif
template class Flat<2>;
template class Flat<3>;
@ -136,6 +160,9 @@ Debug& operator<<(Debug& debug, const FlatFlag value) {
#define _c(v) case FlatFlag::v: return debug << "Shaders::Flat::Flag::" #v;
_c(Textured)
_c(AlphaMask)
#ifndef MAGNUM_TARGET_GLES2
_c(ObjectId)
#endif
#undef _c
/* LCOV_EXCL_STOP */
}
@ -146,7 +173,11 @@ Debug& operator<<(Debug& debug, const FlatFlag value) {
Debug& operator<<(Debug& debug, const FlatFlags value) {
return Containers::enumSetDebugOutput(debug, value, "Shaders::Flat::Flags{}", {
FlatFlag::Textured,
FlatFlag::AlphaMask});
FlatFlag::AlphaMask,
#ifndef MAGNUM_TARGET_GLES2
FlatFlag::ObjectId
#endif
});
}
}

22
src/Magnum/Shaders/Flat.frag

@ -56,13 +56,31 @@ uniform lowp float alphaMask
;
#endif
#ifdef OBJECT_ID
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 3)
#endif
/* mediump is just 2^10, which might not be enough, this is 2^16 */
uniform highp uint objectId; /* defaults to zero */
#endif
#ifdef TEXTURED
in mediump vec2 interpolatedTextureCoordinates;
#endif
#ifdef NEW_GLSL
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = COLOR_OUTPUT_ATTRIBUTE_LOCATION)
#endif
out lowp vec4 fragmentColor;
#endif
#ifdef OBJECT_ID
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = OBJECT_ID_OUTPUT_ATTRIBUTE_LOCATION)
#endif
/* mediump is just 2^10, which might not be enough, this is 2^16 */
out highp uint fragmentObjectId;
#endif
void main() {
fragmentColor =
@ -77,4 +95,8 @@ void main() {
anyway. */
if(fragmentColor.a <= alphaMask) discard;
#endif
#ifdef OBJECT_ID
fragmentObjectId = objectId;
#endif
}

73
src/Magnum/Shaders/Flat.h

@ -42,7 +42,10 @@ namespace Magnum { namespace Shaders {
namespace Implementation {
enum class FlatFlag: UnsignedByte {
Textured = 1 << 0,
AlphaMask = 1 << 1
AlphaMask = 1 << 1,
#ifndef MAGNUM_TARGET_GLES2
ObjectId = 1 << 2
#endif
};
typedef Containers::EnumSet<FlatFlag> FlatFlags;
}
@ -96,6 +99,20 @@ operation which is known to have considerable performance impact on some
platforms. With proper depth sorting and blending you'll usually get much
better performance and output quality.
@subsection Shaders-Flat-usage-object-id Object ID output
The shader supports writing object ID to the framebuffer for object picking or
other annotation purposes. Enable it using @ref Flag::ObjectId and set up an
integer buffer attached to the @ref ObjectIdOutput attachment. Note that for
portability you should use @ref GL::Framebuffer::clearColor() instead of
@ref GL::Framebuffer::clear() as the former usually emits GL errors when called
on framebuffers with integer attachments.
@snippet MagnumShaders.cpp Flat-usage-object-id
@requires_gles30 Object ID output requires integer buffer attachments, which
are not available in OpenGL ES 2.0 or WebGL 1.0.
@see @ref shaders, @ref Flat2D, @ref Flat3D
*/
template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT Flat: public GL::AbstractShaderProgram {
@ -118,6 +135,28 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab
*/
typedef typename Generic<dimensions>::TextureCoordinates TextureCoordinates;
enum: UnsignedInt {
/**
* Color shader output. Present always, expects three- or
* four-component floating-point or normalized buffer attachment.
*/
ColorOutput = Generic<dimensions>::ColorOutput,
#ifndef MAGNUM_TARGET_GLES2
/**
* Object ID shader output. @ref shaders-generic "Generic output",
* present only if @ref Flag::ObjectId is set. Expects a
* single-component unsigned integral attachment. Writes the value
* set in @ref setObjectId() there, see
* @ref Shaders-Phong-usage-object-id for more information.
* @requires_gles30 Object ID output requires integer buffer
* attachments, which are not available in OpenGL ES 2.0 or
* WebGL 1.0.
*/
ObjectIdOutput = Generic<dimensions>::ObjectIdOutput
#endif
};
#ifdef DOXYGEN_GENERATING_OUTPUT
/**
* @brief Flag
@ -142,7 +181,18 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab
* with proper depth sorting and blending you'll usually get much
* better performance and output quality.
*/
AlphaMask = 1 << 1
AlphaMask = 1 << 1,
#ifndef MAGNUM_TARGET_GLES2
/**
* Enable object ID output. See @ref Shaders-Flat-usage-object-id
* for more information.
* @requires_gles30 Object ID output requires integer buffer
* attachments, which are not available in OpenGL ES 2.0 or
* WebGL 1.0.
*/
ObjectId = 1 << 2
#endif
};
/**
@ -237,6 +287,22 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab
*/
Flat<dimensions>& setAlphaMask(Float mask);
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Set object ID
* @return Reference to self (for method chaining)
*
* Expects that the shader was created with @ref Flag::ObjectId
* enabled. Value set here is written to the @ref ObjectIdOutput, see
* @ref Shaders-Flat-usage-object-id for more information. Default is
* @cpp 0 @ce.
* @requires_gles30 Object ID output requires integer buffer
* attachments, which are not available in OpenGL ES 2.0 or WebGL
* 1.0.
*/
Flat<dimensions>& setObjectId(UnsignedInt id);
#endif
#ifdef MAGNUM_BUILD_DEPRECATED
/** @brief @copybrief bindTexture()
* @deprecated Use @ref bindTexture() instead.
@ -251,6 +317,9 @@ template<UnsignedInt dimensions> class MAGNUM_SHADERS_EXPORT Flat: public GL::Ab
Int _transformationProjectionMatrixUniform{0},
_colorUniform{1},
_alphaMaskUniform{2};
#ifndef MAGNUM_TARGET_GLES2
Int _objectIdUniform{3};
#endif
};
/** @brief 2D flat shader */

30
src/Magnum/Shaders/Generic.h

@ -37,8 +37,8 @@ namespace Magnum { namespace Shaders {
@brief Generic shader definition
Definitions common for majority of shaders in the @ref Shaders namespace,
allowing mesh configured for a generic shader to be used with any of them.
See @ref shaders-generic for more information.
allowing mesh or a framebuffer configured for a generic shader to be used with
any of them. See @ref shaders-generic for more information.
@see @ref shaders, @ref Generic2D, @ref Generic3D
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
@ -47,6 +47,25 @@ template<UnsignedInt> struct Generic;
template<UnsignedInt dimensions> struct Generic {
/* Keep consistent with generic.glsl and the real definitions below */
enum: UnsignedInt {
/**
* Color shader output. Present always, expects three- or
* four-component floating-point or normalized buffer attachment.
*/
ColorOutput = 0,
#ifndef MAGNUM_TARGET_GLES2
/**
* Object ID shader output. Expects a single-component unsigned
* integral attachment.
* @requires_gles30 Object ID output requires integer buffer
* attachments, which are not available in OpenGL ES 2.0 or
* WebGL 1.0.
*/
ObjectIdOutput = 1
#endif
};
/**
* @brief Vertex position
*
@ -123,6 +142,13 @@ typedef Generic<3> Generic3D;
#ifndef DOXYGEN_GENERATING_OUTPUT
struct BaseGeneric {
enum: UnsignedInt {
ColorOutput = 0,
#ifndef MAGNUM_TARGET_GLES2
ObjectIdOutput = 1
#endif
};
typedef GL::Attribute<1, Vector2> TextureCoordinates;
typedef GL::Attribute<3, Magnum::Color3> Color3;
typedef GL::Attribute<3, Magnum::Color4> Color4;

37
src/Magnum/Shaders/Phong.cpp

@ -98,19 +98,24 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l
.addSource(flags & Flag::SpecularTexture ? "#define SPECULAR_TEXTURE\n" : "")
.addSource(flags & Flag::NormalTexture ? "#define NORMAL_TEXTURE\n" : "")
.addSource(flags & Flag::AlphaMask ? "#define ALPHA_MASK\n" : "")
#ifndef MAGNUM_TARGET_GLES2
.addSource(flags & Flag::ObjectId ? "#define OBJECT_ID\n" : "")
#endif
.addSource(Utility::formatString(
"#define LIGHT_COUNT {}\n"
"#define LIGHT_COLORS_LOCATION {}\n", lightCount, _lightPositionsUniform + lightCount))
#ifndef MAGNUM_TARGET_GLES
.addSource(std::move(lightInitializer))
#endif
.addSource(rs.get("generic.glsl"))
.addSource(rs.get("Phong.frag"));
CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag}));
attachShaders({vert, frag});
/* ES3 has this done in the shader directly */
/* ES3 has this done in the shader directly and doesn't even provide
bindFragmentDataLocation() */
#if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES2)
#ifndef MAGNUM_TARGET_GLES
if(!GL::Context::current().isExtensionSupported<GL::Extensions::ARB::explicit_attrib_location>(version))
@ -122,6 +127,12 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l
bindAttributeLocation(Tangent::Location, "tangent");
if(flags & (Flag::AmbientTexture|Flag::DiffuseTexture|Flag::SpecularTexture))
bindAttributeLocation(TextureCoordinates::Location, "textureCoordinates");
#ifndef MAGNUM_TARGET_GLES2
if(flags & Flag::ObjectId) {
bindFragmentDataLocation(ColorOutput, "color");
bindFragmentDataLocation(ObjectIdOutput, "objectId");
}
#endif
}
#endif
@ -139,6 +150,9 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l
_specularColorUniform = uniformLocation("specularColor");
_shininessUniform = uniformLocation("shininess");
if(flags & Flag::AlphaMask) _alphaMaskUniform = uniformLocation("alphaMask");
#ifndef MAGNUM_TARGET_GLES2
if(flags & Flag::ObjectId) _objectIdUniform = uniformLocation("objectId");
#endif
_lightPositionsUniform = uniformLocation("lightPositions");
_lightColorsUniform = uniformLocation("lightColors");
}
@ -162,12 +176,13 @@ Phong::Phong(const Flags flags, const UnsignedInt lightCount): _flags{flags}, _l
setSpecularColor(Color4{1.0f});
setShininess(80.0f);
if(flags & Flag::AlphaMask) setAlphaMask(0.5f);
/* Object ID is zero by default */
setLightColors(Containers::Array<Color4>{Containers::DirectInit, lightCount, Color4{1.0f}});
/* Light position is zero by default */
setTransformationMatrix({});
setProjectionMatrix({});
setNormalMatrix({});
/* Light position is zero by default */
#endif
}
@ -213,6 +228,15 @@ Phong& Phong::setAlphaMask(Float mask) {
return *this;
}
#ifndef MAGNUM_TARGET_GLES2
Phong& Phong::setObjectId(UnsignedInt id) {
CORRADE_ASSERT(_flags & Flag::ObjectId,
"Shaders::Phong::setObjectId(): the shader was not created with object ID enabled", *this);
setUniform(_objectIdUniform, id);
return *this;
}
#endif
Phong& Phong::setLightPositions(const Containers::ArrayView<const Vector3> positions) {
CORRADE_ASSERT(_lightCount == positions.size(),
"Shaders::Phong::setLightPositions(): expected" << _lightCount << "items but got" << positions.size(), *this);
@ -250,6 +274,9 @@ Debug& operator<<(Debug& debug, const Phong::Flag value) {
_c(SpecularTexture)
_c(NormalTexture)
_c(AlphaMask)
#ifndef MAGNUM_TARGET_GLES2
_c(ObjectId)
#endif
#undef _c
/* LCOV_EXCL_STOP */
}
@ -263,7 +290,11 @@ Debug& operator<<(Debug& debug, const Phong::Flags value) {
Phong::Flag::DiffuseTexture,
Phong::Flag::SpecularTexture,
Phong::Flag::NormalTexture,
Phong::Flag::AlphaMask});
Phong::Flag::AlphaMask,
#ifndef MAGNUM_TARGET_GLES2
Phong::Flag::ObjectId
#endif
});
}
}}

40
src/Magnum/Shaders/Phong.frag

@ -25,7 +25,7 @@
#ifndef NEW_GLSL
#define in varying
#define color gl_FragColor
#define fragmentColor gl_FragColor
#define texture texture2D
#endif
@ -112,9 +112,17 @@ uniform lowp float alphaMask
;
#endif
/* Needs to be last because it uses locations 9 + LIGHT_COUNT to
9 + 2*LIGHT_COUNT - 1. Location 9 is lightPositions. Also it can't be
specified as 9 + LIGHT_COUNT because that requires ARB_enhanced_layouts. */
#ifdef OBJECT_ID
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 9)
#endif
/* mediump is just 2^10, which might not be enough, this is 2^16 */
uniform highp uint objectId; /* defaults to zero */
#endif
/* Needs to be last because it uses locations 10 + LIGHT_COUNT to
10 + 2*LIGHT_COUNT - 1. Location 10 is lightPositions. Also it can't be
specified as 10 + LIGHT_COUNT because that requires ARB_enhanced_layouts. */
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = LIGHT_COLORS_LOCATION) /* I fear this will blow up some drivers */
#endif
@ -136,7 +144,17 @@ in mediump vec2 interpolatedTextureCoords;
#endif
#ifdef NEW_GLSL
out lowp vec4 color;
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = COLOR_OUTPUT_ATTRIBUTE_LOCATION)
#endif
out lowp vec4 fragmentColor;
#endif
#ifdef OBJECT_ID
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = OBJECT_ID_OUTPUT_ATTRIBUTE_LOCATION)
#endif
/* mediump is just 2^10, which might not be enough, this is 2^16 */
out highp uint fragmentObjectId;
#endif
void main() {
@ -157,7 +175,7 @@ void main() {
specularColor;
/* Ambient color */
color = finalAmbientColor;
fragmentColor = finalAmbientColor;
/* Normal */
mediump vec3 normalizedTransformedNormal = normalize(transformedNormal);
@ -176,13 +194,13 @@ void main() {
for(int i = 0; i < LIGHT_COUNT; ++i) {
highp vec3 normalizedLightDirection = normalize(lightDirections[i]);
lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection));
color += vec4(finalDiffuseColor.rgb*lightColors[i].rgb*intensity, lightColors[i].a*finalDiffuseColor.a/float(LIGHT_COUNT));
fragmentColor += vec4(finalDiffuseColor.rgb*lightColors[i].rgb*intensity, lightColors[i].a*finalDiffuseColor.a/float(LIGHT_COUNT));
/* Add specular color, if needed */
if(intensity > 0.001) {
highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal);
mediump float specularity = clamp(pow(max(0.0, dot(normalize(cameraDirection), reflection)), shininess), 0.0, 1.0);
color += vec4(finalSpecularColor.rgb*specularity, finalSpecularColor.a);
fragmentColor += vec4(finalSpecularColor.rgb*specularity, finalSpecularColor.a);
}
}
@ -190,6 +208,10 @@ void main() {
/* Using <= because if mask is set to 1.0, it should discard all, similarly
as when using 0, it should only discard what's already invisible
anyway. */
if(color.a <= alphaMask) discard;
if(fragmentColor.a <= alphaMask) discard;
#endif
#ifdef OBJECT_ID
fragmentObjectId = objectId;
#endif
}

73
src/Magnum/Shaders/Phong.h

@ -95,6 +95,18 @@ diffuse part and then separate the alpha like this:
@snippet MagnumShaders.cpp Phong-usage-alpha
@subsection Shaders-Phong-usage-object-id Object ID output
The shader supports writing object ID to the framebuffer for object picking or
other annotation purposes. Enable it using @ref Flag::ObjectId and set up an
integer buffer attached to the @ref ObjectIdOutput attachment. The
functionality is practically the same as in the @ref Flat shader, see its
@ref Shaders-Flat-usage-object-id documentation for more information and usage
example.
@requires_gles30 Object ID output requires integer buffer attachments, which
are not available in OpenGL ES 2.0 or WebGL 1.0.
@see @ref shaders
*/
class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram {
@ -134,6 +146,29 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram {
*/
typedef Generic3D::TextureCoordinates TextureCoordinates;
enum: UnsignedInt {
/**
* Color shader output. @ref shaders-generic "Generic output",
* present always. Expects three- or four-component floating-point
* or normalized buffer attachment.
*/
ColorOutput = Generic3D::ColorOutput,
#ifndef MAGNUM_TARGET_GLES2
/**
* Object ID shader output. @ref shaders-generic "Generic output",
* present only if @ref Flag::ObjectId is set. Expects a
* single-component unsigned integral attachment. Writes the value
* set in @ref setObjectId() there, see
* @ref Shaders-Phong-usage-object-id for more information.
* @requires_gles30 Object ID output requires integer buffer
* attachments, which are not available in OpenGL ES 2.0 or
* WebGL 1.0.
*/
ObjectIdOutput = Generic3D::ObjectIdOutput
#endif
};
/**
* @brief Flag
*
@ -175,7 +210,18 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram {
* with proper depth sorting and blending you'll usually get much
* better performance and output quality.
*/
AlphaMask = 1 << 3
AlphaMask = 1 << 3,
#ifndef MAGNUM_TARGET_GLES2
/**
* Enable object ID output. See @ref Shaders-Phong-usage-object-id
* for more information.
* @requires_gles30 Object ID output requires integer buffer
* attachments, which are not available in OpenGL ES 2.0 or
* WebGL 1.0.
*/
ObjectId = 1 << 5
#endif
};
/**
@ -381,6 +427,22 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram {
*/
Phong& setAlphaMask(Float mask);
#ifndef MAGNUM_TARGET_GLES2
/**
* @brief Set object ID
* @return Reference to self (for method chaining)
*
* Expects that the shader was created with @ref Flag::ObjectId
* enabled. Value set here is written to the @ref ObjectIdOutput, see
* @ref Shaders-Phong-usage-object-id for more information. Default is
* @cpp 0 @ce.
* @requires_gles30 Object ID output requires integer buffer
* attachments, which are not available in OpenGL ES 2.0 or WebGL
* 1.0.
*/
Phong& setObjectId(UnsignedInt id);
#endif
/**
* @brief Set transformation matrix
* @return Reference to self (for method chaining)
@ -506,9 +568,12 @@ class MAGNUM_SHADERS_EXPORT Phong: public GL::AbstractShaderProgram {
_diffuseColorUniform{5},
_specularColorUniform{6},
_shininessUniform{7},
_alphaMaskUniform{8},
_lightPositionsUniform{9},
_lightColorsUniform; /* 9 + lightCount, set in the constructor */
_alphaMaskUniform{8};
#ifndef MAGNUM_TARGET_GLES2
Int _objectIdUniform{9};
#endif
Int _lightPositionsUniform{10},
_lightColorsUniform; /* 10 + lightCount, set in the constructor */
};
/** @debugoperatorclassenum{Phong,Phong::Flag} */

4
src/Magnum/Shaders/Phong.vert

@ -55,9 +55,9 @@ uniform mediump mat3 normalMatrix
#endif
;
/* Needs to be last because it uses locations 9 to 9 + LIGHT_COUNT - 1 */
/* Needs to be last because it uses locations 10 to 10 + LIGHT_COUNT - 1 */
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 9)
layout(location = 10)
#endif
uniform highp vec3 lightPositions[LIGHT_COUNT]; /* defaults to zero */

177
src/Magnum/Shaders/Test/FlatGLTest.cpp

@ -64,6 +64,9 @@ struct FlatGLTest: GL::OpenGLTester {
template<UnsignedInt dimensions> void bindTextureNotEnabled();
template<UnsignedInt dimensions> void setAlphaMaskNotEnabled();
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void setObjectIdNotEnabled();
#endif
void renderSetup();
void renderTeardown();
@ -83,10 +86,21 @@ struct FlatGLTest: GL::OpenGLTester {
void renderAlpha2D();
void renderAlpha3D();
#ifndef MAGNUM_TARGET_GLES2
void renderObjectIdSetup();
void renderObjectIdTeardown();
void renderObjectId2D();
void renderObjectId3D();
#endif
private:
PluginManager::Manager<Trade::AbstractImporter> _manager{"nonexistent"};
GL::Renderbuffer _color{NoCreate};
#ifndef MAGNUM_TARGET_GLES2
GL::Renderbuffer _objectId{NoCreate};
#endif
GL::Framebuffer _framebuffer{NoCreate};
};
@ -112,7 +126,11 @@ constexpr struct {
{"", {}},
{"textured", Flat2D::Flag::Textured},
{"alpha mask", Flat2D::Flag::AlphaMask},
{"alpha mask + textured", Flat2D::Flag::AlphaMask|Flat2D::Flag::Textured}
{"alpha mask + textured", Flat2D::Flag::AlphaMask|Flat2D::Flag::Textured},
#ifndef MAGNUM_TARGET_GLES2
{"object ID", Flat2D::Flag::ObjectId},
{"object ID + alpha mask + textured", Flat2D::Flag::ObjectId|Flat2D::Flag::AlphaMask|Flat2D::Flag::Textured}
#endif
};
const struct {
@ -150,7 +168,12 @@ FlatGLTest::FlatGLTest() {
&FlatGLTest::bindTextureNotEnabled<2>,
&FlatGLTest::bindTextureNotEnabled<3>,
&FlatGLTest::setAlphaMaskNotEnabled<2>,
&FlatGLTest::setAlphaMaskNotEnabled<3>});
&FlatGLTest::setAlphaMaskNotEnabled<3>,
#ifndef MAGNUM_TARGET_GLES2
&FlatGLTest::setObjectIdNotEnabled<2>,
&FlatGLTest::setObjectIdNotEnabled<3>
#endif
});
addTests({&FlatGLTest::renderDefaults2D,
&FlatGLTest::renderDefaults3D,
@ -169,6 +192,13 @@ FlatGLTest::FlatGLTest() {
&FlatGLTest::renderAlphaSetup,
&FlatGLTest::renderAlphaTeardown);
#ifndef MAGNUM_TARGET_GLES2
addTests({&FlatGLTest::renderObjectId2D,
&FlatGLTest::renderObjectId3D},
&FlatGLTest::renderObjectIdSetup,
&FlatGLTest::renderObjectIdTeardown);
#endif
/* Load the plugins directly from the build tree. Otherwise they're either
static and already loaded or not present in the build tree */
#ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME
@ -243,6 +273,21 @@ template<UnsignedInt dimensions> void FlatGLTest::setAlphaMaskNotEnabled() {
"Shaders::Flat::setAlphaMask(): the shader was not created with alpha mask enabled\n");
}
#ifndef MAGNUM_TARGET_GLES2
template<UnsignedInt dimensions> void FlatGLTest::setObjectIdNotEnabled() {
setTestCaseTemplateName(std::to_string(dimensions));
std::ostringstream out;
Error redirectError{&out};
Flat<dimensions> shader;
shader.setObjectId(33376);
CORRADE_COMPARE(out.str(),
"Shaders::Flat::setObjectId(): the shader was not created with object ID enabled\n");
}
#endif
constexpr Vector2i RenderSize{80, 80};
void FlatGLTest::renderSetup() {
@ -683,6 +728,134 @@ void FlatGLTest::renderAlpha3D() {
(DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold}));
}
#ifndef MAGNUM_TARGET_GLES2
void FlatGLTest::renderObjectIdSetup() {
GL::Renderer::enable(GL::Renderer::Feature::FaceCulling);
_color = GL::Renderbuffer{};
_color.setStorage(GL::RenderbufferFormat::RGBA8, RenderSize);
_objectId = GL::Renderbuffer{};
_objectId.setStorage(GL::RenderbufferFormat::R32UI, RenderSize);
_framebuffer = GL::Framebuffer{{{}, RenderSize}};
_framebuffer
.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color)
.attachRenderbuffer(GL::Framebuffer::ColorAttachment{1}, _objectId)
.mapForDraw({
{Flat3D::ColorOutput, GL::Framebuffer::ColorAttachment{0}},
{Flat3D::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}}
})
/* Pick a color that's directly representable on RGBA4 as well to
reduce artifacts (well, and this needs to be consistent with other
tests that *need* to run on WebGL 1) */
.clearColor(0, 0x111111_rgbf)
.clearColor(1, Vector4ui{27})
.bind();
}
void FlatGLTest::renderObjectIdTeardown() {
_color = GL::Renderbuffer{NoCreate};
_objectId = GL::Renderbuffer{NoCreate};
_framebuffer = GL::Framebuffer{NoCreate};
}
void FlatGLTest::renderObjectId2D() {
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Draw), GL::Framebuffer::Status::Complete);
GL::Mesh circle = MeshTools::compile(Primitives::circle2DSolid(32));
Flat2D shader{Flat3D::Flag::ObjectId};
shader.setColor(0x9999ff_rgbf)
.setTransformationProjectionMatrix(Matrix3::projection({2.1f, 2.1f}))
.setObjectId(47523);
circle.draw(shader);
MAGNUM_VERIFY_NO_GL_ERROR();
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found.");
/* Color output should have no difference -- same as in colored2D() */
#if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL))
const Float maxThreshold = 0.0f, meanThreshold = 0.0f;
#else
/* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */
const Float maxThreshold = 11.34f, meanThreshold = 0.51f;
#endif
CORRADE_COMPARE_WITH(
/* Dropping the alpha channel, as it's always 1.0 */
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
Utility::Directory::join(SHADERS_TEST_DIR, "FlatTestFiles/colored2D.tga"),
(DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold}));
/* Object ID -- no need to verify the whole image, just check that pixels
on known places have expected values. SwiftShader insists that the read
format has to be 32bit, so the renderbuffer format is that too to make
it the same (ES3 Mesa complains if these don't match). */
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1});
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete);
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI});
MAGNUM_VERIFY_NO_GL_ERROR();
/* Outside of the object, cleared to 27 */
CORRADE_COMPARE(image.pixels<UnsignedInt>()[10][10], 27);
/* Inside of the object. Verify that it can hold 16 bits at least. */
CORRADE_COMPARE(image.pixels<UnsignedInt>()[40][46], 47523);
}
void FlatGLTest::renderObjectId3D() {
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Draw), GL::Framebuffer::Status::Complete);
GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32));
Flat3D shader{Flat3D::Flag::ObjectId};
shader.setColor(0x9999ff_rgbf)
.setTransformationProjectionMatrix(
Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f)*
Matrix4::translation(Vector3::zAxis(-2.15f))*
Matrix4::rotationY(-15.0_degf)*
Matrix4::rotationX(15.0_degf))
.setObjectId(48526);
sphere.draw(shader);
MAGNUM_VERIFY_NO_GL_ERROR();
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found.");
/* Color output should have no difference -- same as in colored3D() */
#if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL))
/* SwiftShader has 5 different pixels on the edges */
const Float maxThreshold = 170.0f, meanThreshold = 0.133f;
#else
/* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */
const Float maxThreshold = 170.0f, meanThreshold = 0.456f;
#endif
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{0});
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete);
CORRADE_COMPARE_WITH(
/* Dropping the alpha channel, as it's always 1.0 */
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
Utility::Directory::join(SHADERS_TEST_DIR, "FlatTestFiles/colored3D.tga"),
(DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold}));
/* Object ID -- no need to verify the whole image, just check that pixels
on known places have expected values. SwiftShader insists that the read
format has to be 32bit, so the renderbuffer format is that too to make
it the same (ES3 Mesa complains if these don't match). */
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1});
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete);
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI});
MAGNUM_VERIFY_NO_GL_ERROR();
/* Outside of the object, cleared to 27 */
CORRADE_COMPARE(image.pixels<UnsignedInt>()[10][10], 27);
/* Inside of the object. Verify that it can hold 16 bits at least. */
CORRADE_COMPARE(image.pixels<UnsignedInt>()[40][46], 48526);
}
#endif
}}}}
CORRADE_TEST_MAIN(Magnum::Shaders::Test::FlatGLTest)

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

@ -65,6 +65,9 @@ struct PhongGLTest: GL::OpenGLTester {
void bindTexturesNotEnabled();
void setAlphaMaskNotEnabled();
#ifndef MAGNUM_TARGET_GLES2
void setObjectIdNotEnabled();
#endif
void setWrongLightCount();
void setWrongLightId();
@ -85,10 +88,20 @@ struct PhongGLTest: GL::OpenGLTester {
void renderAlpha();
#ifndef MAGNUM_TARGET_GLES2
void renderObjectIdSetup();
void renderObjectIdTeardown();
void renderObjectId();
#endif
private:
PluginManager::Manager<Trade::AbstractImporter> _manager{"nonexistent"};
GL::Renderbuffer _color{NoCreate};
#ifndef MAGNUM_TARGET_GLES2
GL::Renderbuffer _objectId{NoCreate};
#endif
GL::Framebuffer _framebuffer{NoCreate};
};
@ -122,7 +135,11 @@ constexpr struct {
{"ambient + diffuse + specular + normal texture", Phong::Flag::AmbientTexture|Phong::Flag::DiffuseTexture|Phong::Flag::SpecularTexture|Phong::Flag::NormalTexture, 1},
{"alpha mask", Phong::Flag::AlphaMask, 1},
{"alpha mask + diffuse texture", Phong::Flag::AlphaMask|Phong::Flag::DiffuseTexture, 1},
#ifndef MAGNUM_TARGET_GLES2
{"object ID", Phong::Flag::ObjectId, 1},
{"object ID + alpha mask + specular texture", Phong::Flag::ObjectId|Phong::Flag::AlphaMask|Phong::Flag::SpecularTexture, 1},
{"five lights", {}, 5}
#endif
};
using namespace Math::Literals;
@ -236,6 +253,9 @@ PhongGLTest::PhongGLTest() {
&PhongGLTest::bindTexturesNotEnabled,
&PhongGLTest::setAlphaMaskNotEnabled,
#ifndef MAGNUM_TARGET_GLES2
&PhongGLTest::setObjectIdNotEnabled,
#endif
&PhongGLTest::setWrongLightCount,
&PhongGLTest::setWrongLightId});
@ -273,6 +293,12 @@ PhongGLTest::PhongGLTest() {
&PhongGLTest::renderAlphaSetup,
&PhongGLTest::renderAlphaTeardown);
#ifndef MAGNUM_TARGET_GLES2
addTests({&PhongGLTest::renderObjectId},
&PhongGLTest::renderObjectIdSetup,
&PhongGLTest::renderObjectIdTeardown);
#endif
/* Load the plugins directly from the build tree. Otherwise they're either
static and already loaded or not present in the build tree */
#ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME
@ -351,6 +377,19 @@ void PhongGLTest::setAlphaMaskNotEnabled() {
"Shaders::Phong::setAlphaMask(): the shader was not created with alpha mask enabled\n");
}
#ifndef MAGNUM_TARGET_GLES2
void PhongGLTest::setObjectIdNotEnabled() {
std::ostringstream out;
Error redirectError{&out};
Phong shader;
shader.setObjectId(33376);
CORRADE_COMPARE(out.str(),
"Shaders::Phong::setObjectId(): the shader was not created with object ID enabled\n");
}
#endif
void PhongGLTest::setWrongLightCount() {
std::ostringstream out;
Error redirectError{&out};
@ -942,6 +981,90 @@ void PhongGLTest::renderAlpha() {
(DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold}));
}
#ifndef MAGNUM_TARGET_GLES2
void PhongGLTest::renderObjectIdSetup() {
GL::Renderer::enable(GL::Renderer::Feature::FaceCulling);
_color = GL::Renderbuffer{};
_color.setStorage(GL::RenderbufferFormat::RGBA8, RenderSize);
_objectId = GL::Renderbuffer{};
_objectId.setStorage(GL::RenderbufferFormat::R32UI, RenderSize);
_framebuffer = GL::Framebuffer{{{}, RenderSize}};
_framebuffer
.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color)
.attachRenderbuffer(GL::Framebuffer::ColorAttachment{1}, _objectId)
.mapForDraw({
{Phong::ColorOutput, GL::Framebuffer::ColorAttachment{0}},
{Phong::ObjectIdOutput, GL::Framebuffer::ColorAttachment{1}}
})
/* Pick a color that's directly representable on RGBA4 as well to
reduce artifacts (well, and this needs to be consistent with other
tests that *need* to run on WebGL 1) */
.clearColor(0, 0x111111_rgbf)
.clearColor(1, Vector4ui{27})
.bind();
}
void PhongGLTest::renderObjectIdTeardown() {
_color = GL::Renderbuffer{NoCreate};
_objectId = GL::Renderbuffer{NoCreate};
_framebuffer = GL::Framebuffer{NoCreate};
}
void PhongGLTest::renderObjectId() {
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Draw), GL::Framebuffer::Status::Complete);
GL::Mesh sphere = MeshTools::compile(Primitives::uvSphereSolid(16, 32));
Phong shader{Phong::Flag::ObjectId, 2};
shader.setLightColors({0x993366_rgbf, 0x669933_rgbf})
.setLightPositions({{-3.0f, -3.0f, 0.0f},
{ 3.0f, -3.0f, 0.0f}})
.setAmbientColor(0x330033_rgbf)
.setDiffuseColor(0xccffcc_rgbf)
.setSpecularColor(0x6666ff_rgbf)
.setTransformationMatrix(Matrix4::translation(Vector3::zAxis(-2.15f)))
.setProjectionMatrix(Matrix4::perspectiveProjection(60.0_degf, 1.0f, 0.1f, 10.0f))
.setObjectId(48526);
sphere.draw(shader);
MAGNUM_VERIFY_NO_GL_ERROR();
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / TgaImageImporter plugins not found.");
/* Color output should have no difference -- same as in colored() */
#if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL))
/* SwiftShader has some minor rounding differences (max = 1). ARM Mali G71
has bigger rounding differences. */
const Float maxThreshold = 8.34f, meanThreshold = 0.066f;
#else
/* WebGL 1 doesn't have 8bit renderbuffer storage, so it's way worse */
const Float maxThreshold = 15.34f, meanThreshold = 3.33f;
#endif
CORRADE_COMPARE_WITH(
/* Dropping the alpha channel, as it's always 1.0 */
Containers::arrayCast<Color3ub>(_framebuffer.read(_framebuffer.viewport(), {PixelFormat::RGBA8Unorm}).pixels<Color4ub>()),
Utility::Directory::join(SHADERS_TEST_DIR, "PhongTestFiles/colored.tga"),
(DebugTools::CompareImageToFile{_manager, maxThreshold, meanThreshold}));
/* Object ID -- no need to verify the whole image, just check that pixels
on known places have expected values. SwiftShader insists that the read
format has to be 32bit, so the renderbuffer format is that too to make
it the same (ES3 Mesa complains if these don't match). */
_framebuffer.mapForRead(GL::Framebuffer::ColorAttachment{1});
CORRADE_COMPARE(_framebuffer.checkStatus(GL::FramebufferTarget::Read), GL::Framebuffer::Status::Complete);
Image2D image = _framebuffer.read(_framebuffer.viewport(), {PixelFormat::R32UI});
MAGNUM_VERIFY_NO_GL_ERROR();
/* Outside of the object, cleared to 27 */
CORRADE_COMPARE(image.pixels<UnsignedInt>()[10][10], 27);
/* Inside of the object. Verify that it can hold 16 bits at least. */
CORRADE_COMPARE(image.pixels<UnsignedInt>()[40][46], 48526);
}
#endif
}}}}
CORRADE_TEST_MAIN(Magnum::Shaders::Test::PhongGLTest)

4
src/Magnum/Shaders/generic.glsl

@ -30,3 +30,7 @@
#define NORMAL_ATTRIBUTE_LOCATION 2
#define COLOR_ATTRIBUTE_LOCATION 3
#define TANGENT_ATTRIBUTE_LOCATION 4
/* Outputs */
#define COLOR_OUTPUT_ATTRIBUTE_LOCATION 0
#define OBJECT_ID_OUTPUT_ATTRIBUTE_LOCATION 1

Loading…
Cancel
Save