diff --git a/src/AbstractShaderProgram.h b/src/AbstractShaderProgram.h index 2a4a875ff..7b28acdc8 100644 --- a/src/AbstractShaderProgram.h +++ b/src/AbstractShaderProgram.h @@ -22,8 +22,6 @@ #include #include "Shader.h" -#include "AbstractTexture.h" -#include "BufferedTexture.h" namespace Magnum { @@ -34,34 +32,45 @@ namespace Magnum { This class is designed to be used via subclassing. Subclasses define these functions and properties: - - **Attribute location** typedefs defining locations and types for attribute - binding with Mesh::bindAttribute(), for example: + - %Attribute location typedefs defining locations and types + for attribute binding with Mesh::bindAttribute(), for example: @code typedef Attribute<0, Vector4> Vertex; typedef Attribute<1, Vector3> Normal; typedef Attribute<2, Vector2> TextureCoords; +@endcode + - **Layers for texture uniforms** to which the textures will be bound before + rendering, for example: +@code +static const GLint DiffuseTextureLayer = 0; +static const GLint SpecularTextureLayer = 1; @endcode - **Constructor**, which attaches particular shaders, links the program and gets uniform locations, for example: @code -// Load shaders from file and attach them to the program -attachShader(Shader::fromFile(Shader::Vertex, "PhongShader.vert")); -attachShader(Shader::fromFile(Shader::Fragment, "PhongShader.frag")); - -// Link -link(); - -// Get locations of uniforms -transformationMatrixUniform = uniformLocation("transformationMatrix"); -projectionMatrixUniform = uniformLocation("projectionMatrix"); -// more uniforms like light location, colors etc. +MyShader() { + // Load shaders from file and attach them to the program + attachShader(Shader::fromFile(Shader::Vertex, "PhongShader.vert")); + attachShader(Shader::fromFile(Shader::Fragment, "PhongShader.frag")); + + // Link + link(); + + // Get locations of uniforms + transformationMatrixUniform = uniformLocation("transformationMatrix"); + projectionMatrixUniform = uniformLocation("projectionMatrix"); + // more uniforms like light location, colors etc. +} @endcode - - **Uniform binding functions**, which set shader uniforms using - setUniform() functions. Example: + - **Uniform setting functions**, which will provide public interface for + protected setUniform() functions. Example: @code void setTransformationMatrixUniform(const Matrix4& matrix) { setUniform(transformationMatrixUniform, matrix); } +void setProjectionMatrixUniform(const Matrix4& matrix) { + setUniform(projectionMatrixUniform, matrix); +} @endcode @subsection AbstractShaderProgramAttributeLocation Binding attribute location @@ -89,11 +98,40 @@ bindAttributeLocation(TextureCoords::Location, "textureCoords"); // Link... @endcode +@subsection AbstractShaderProgramTextureLayer Binding texture layer uniforms +The preferred workflow is to specify texture layers directly in the shader +code, e.g.: +@code +layout(binding = 0) uniform sampler2D diffuseTexture; +layout(binding = 1) uniform sampler2D specularTexture; +@endcode +@requires_gl42 Extension @extension{ARB,shading_language_420pack} (for explicit + texture layer binding instead of using setUniform(GLint, GLint)) + +If you don't have the required extension (or if you want to change the layer +later), you can set the texture layer uniform using setUniform(GLint, GLint): +@code +use(); +setUniform(uniformLocation("diffuseTexture"), DiffuseTextureLayer); +setUniform(uniformLocation("specularTexture"), SpecularTextureLayer); +@endcode + @section AbstractShaderProgramRenderingWorkflow Rendering workflow -Basic workflow with AbstractShaderProgram subclasses is: instancing the class -(once at the beginning), then in every frame calling use(), setting uniforms -and calling Mesh::draw() (see its documentation for more). +Basic workflow with %AbstractShaderProgram subclasses is: instancing the class +(once at the beginning), then in Object::draw() reimplementation calling +use(), setting uniforms, binding required textures to their respective layers +using AbstractTexture::bind(GLint) and calling Mesh::draw(). For example: +@code +void draw(const Magnum::Matrix4& transformationMatrix, Magnum::Camera* camera) { + shader.use(); + shader.setTransformationMatrixUniform(transformationMatrix); + shader.setProjectionMatrixUniform(camera->projectionMatrix()); + diffuseTexture.bind(MyShader::DiffuseTextureLayer); + specularTexture.bind(MyShader::SpecularTextureLayer); + mesh.draw(); +} +@endcode @todo Uniform arrays support @@ -297,16 +335,6 @@ class MAGNUM_EXPORT AbstractShaderProgram { glUniformMatrix4fv(location, 1, GL_FALSE, value.data()); } - /** @copydoc setUniform(GLint, GLint) */ - inline void setUniform(GLint location, const AbstractTexture* value) { - setUniform(location, value->layer()); - } - - /** @copydoc setUniform(GLint, GLint) */ - inline void setUniform(GLint location, const BufferedTexture* value) { - setUniform(location, value->layer()); - } - private: enum State { Initialized, diff --git a/src/AbstractTexture.cpp b/src/AbstractTexture.cpp index 3135dd4ac..a0ba88f96 100644 --- a/src/AbstractTexture.cpp +++ b/src/AbstractTexture.cpp @@ -18,18 +18,6 @@ namespace Magnum { #ifndef DOXYGEN_GENERATING_OUTPUT -/* Check corretness of GL_TEXTURE0 + N == GL_TEXTUREN */ -#define texture_assert(num) static_assert(GL_TEXTURE0 + num == GL_TEXTURE##num,\ - "Unsupported constants for GL texture layers") -texture_assert(0); texture_assert(8); texture_assert(16); texture_assert(24); -texture_assert(1); texture_assert(9); texture_assert(17); texture_assert(25); -texture_assert(2); texture_assert(10); texture_assert(18); texture_assert(26); -texture_assert(3); texture_assert(11); texture_assert(19); texture_assert(27); -texture_assert(4); texture_assert(12); texture_assert(20); texture_assert(28); -texture_assert(5); texture_assert(13); texture_assert(21); texture_assert(29); -texture_assert(6); texture_assert(14); texture_assert(22); texture_assert(30); -texture_assert(7); texture_assert(15); texture_assert(23); texture_assert(31); -#undef texture_assert /* Check correctness of binary OR in setMinificationFilter(). If nobody fucks anything up, this assert should produce the same results on all dimensions, @@ -46,6 +34,12 @@ static_assert((filter_or(NearestNeighbor, BaseLevel) == GL_NEAREST) && #undef filter_or #endif +GLint AbstractTexture::maxSupportedLayerCount() { + GLint value; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &value); + return value; +} + GLfloat AbstractTexture::maxSupportedAnisotropy() { GLfloat value; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &value); diff --git a/src/AbstractTexture.h b/src/AbstractTexture.h index 1942ee780..27ae65fa5 100644 --- a/src/AbstractTexture.h +++ b/src/AbstractTexture.h @@ -32,8 +32,9 @@ texture, otherwise the texture will be incomplete. If you specified mipmap filtering in setMinificationFilter(), be sure to also either explicitly set all mip levels or call generateMipmap(). -The texture is bound via bind() and setting texture uniform on the shader to the -texture (see AbstractShaderProgram::setUniform(GLint, const AbstractTexture*)). +The texture is bound to shader via bind(). Texture uniform on the shader must +also be set to particular texture layer using +AbstractShaderProgram::setUniform(GLint, GLint). See Texture, CubeMapTexture and CubeMapTextureArray documentation for more information. @@ -468,6 +469,14 @@ class MAGNUM_EXPORT AbstractTexture { /*@}*/ + /** + * @brief Max supported layer count + * + * At least 48. + * @see bind(GLint) + */ + static GLint maxSupportedLayerCount(); + /** * @brief Max supported anisotropy * @@ -478,13 +487,11 @@ class MAGNUM_EXPORT AbstractTexture { /** * @brief Constructor - * @param layer %Texture layer (number between 0 and 31) * @param target Target, e.g. `GL_TEXTURE_2D`. * * Creates one OpenGL texture. */ - inline AbstractTexture(GLint layer, GLenum target): _target(target), _layer(layer) { - glActiveTexture(GL_TEXTURE0 + layer); + inline AbstractTexture(GLenum target): _target(target) { glGenTextures(1, &texture); } @@ -495,23 +502,19 @@ class MAGNUM_EXPORT AbstractTexture { */ virtual ~AbstractTexture() = 0; - /** @brief %Texture layer */ - inline GLint layer() const { return _layer; } - /** @brief OpenGL internal texture ID */ inline GLuint id() const { return texture; } /** - * @brief Bind texture for usage / rendering - * - * Sets current texture as active in its texture layer. Note that - * only one texture can be bound to given layer. + * @brief Bind texture for rendering * - * @see layer() + * Sets current texture as active in given layer. The layer must be + * between 0 and maxSupportedLayerCount(). Note that only one texture + * can be bound to given layer. */ - inline void bind() { - glActiveTexture(GL_TEXTURE0 + _layer); - glBindTexture(_target, texture); + inline void bind(GLint layer) { + glActiveTexture(GL_TEXTURE0 + layer); + bind(); } /** @@ -582,8 +585,17 @@ class MAGNUM_EXPORT AbstractTexture { const GLenum _target; /**< @brief Target */ + /** + * @brief Bind texture for parameter modification + * + * Unlike bind(GLint) doesn't bind the texture to any particular + * layer, thus unusable for binding for rendering. + */ + inline void bind() { + glBindTexture(_target, texture); + } + private: - const GLint _layer; GLuint texture; }; diff --git a/src/BufferedTexture.h b/src/BufferedTexture.h index 5710e8399..5d69f7b8d 100644 --- a/src/BufferedTexture.h +++ b/src/BufferedTexture.h @@ -108,10 +108,10 @@ class BufferedTexture { /** * @brief Constructor - * @param layer %Texture layer (number between 0 and 31) + * + * Creates one OpenGL texture. */ - inline BufferedTexture(GLint layer = 0): _layer(layer) { - glActiveTexture(GL_TEXTURE0 + layer); + inline BufferedTexture() { glGenTextures(1, &texture); } @@ -120,13 +120,10 @@ class BufferedTexture { glDeleteTextures(1, &texture); } - /** @copydoc AbstractTexture::layer() */ - inline GLint layer() const { return _layer; } - - /** @copydoc AbstractTexture::bind() */ - inline void bind() const { - glActiveTexture(GL_TEXTURE0 + _layer); - glBindTexture(GL_TEXTURE_BUFFER, texture); + /** @copydoc AbstractTexture::bind(GLint) */ + inline void bind(GLint layer) { + glActiveTexture(GL_TEXTURE0 + layer); + bind(); } /** @@ -144,8 +141,12 @@ class BufferedTexture { } private: - GLint _layer; GLuint texture; + + /** @copydoc AbstractTexture::bind() */ + inline void bind() { + glBindTexture(GL_TEXTURE_BUFFER, texture); + } }; /** @relates BufferedTexture diff --git a/src/CubeMapTexture.h b/src/CubeMapTexture.h index ddb455c14..1ca43134a 100644 --- a/src/CubeMapTexture.h +++ b/src/CubeMapTexture.h @@ -70,11 +70,10 @@ class CubeMapTexture: public AbstractTexture { /** * @brief Constructor - * @param layer Texture layer (number between 0 and 31) * * Creates one cube map OpenGL texture. */ - inline CubeMapTexture(GLint layer = 0): AbstractTexture(layer, GL_TEXTURE_CUBE_MAP) {} + inline CubeMapTexture(): AbstractTexture(GL_TEXTURE_CUBE_MAP) {} /** * @copydoc Texture::setWrapping() diff --git a/src/CubeMapTextureArray.h b/src/CubeMapTextureArray.h index af6cdcb23..22d7e1cde 100644 --- a/src/CubeMapTextureArray.h +++ b/src/CubeMapTextureArray.h @@ -58,11 +58,10 @@ class CubeMapTextureArray: public AbstractTexture { /** * @brief Constructor - * @param layer Texture layer (number between 0 and 31) * * Creates one cube map OpenGL texture. */ - inline CubeMapTextureArray(GLint layer = 0): AbstractTexture(layer, GL_TEXTURE_CUBE_MAP_ARRAY) {} + inline CubeMapTextureArray(): AbstractTexture(GL_TEXTURE_CUBE_MAP_ARRAY) {} /** * @copydoc Texture::setWrapping() diff --git a/src/Texture.h b/src/Texture.h index 60d9c6ade..b3447976c 100644 --- a/src/Texture.h +++ b/src/Texture.h @@ -93,14 +93,13 @@ template class Texture: public AbstractTexture { /** * @brief Constructor - * @param layer %Texture layer (number between 0 and 31) * @param target %Texture target. If not set, default value * is `Target::Texture1D`, `Target::Texture2D` or * `Target::Texture3D` based on dimension count. * * Creates one OpenGL texture. */ - inline Texture(GLint layer = 0, Target target = DataHelper::target()): AbstractTexture(layer, static_cast(target)) {} + inline Texture(Target target = DataHelper::target()): AbstractTexture(static_cast(target)) {} /** @brief %Texture target */ inline constexpr Target target() const { return static_cast(_target); }