Browse Source

Reworked binding of textures to shaders.

According to OpenGL specification the sampler uniform takes only texture
layer and not any particular texture. In OpenGL 4.2 it is possible to
explicitly specify desired texture layer in the uniform, so I suppose
it's desired to have shader with fixed texture layers and on the other
hand be able to bind texture to any layer, not only one -- the exact
opposite of how it was in Magnum before.

The shader should now specify in its public API which texture is on
which layer and explicitly set the layer uniforms upon construction. All
setUniform() functions taking textures as argument were thus removed, as
they confuse things a lot.

The textures now have bind() function which takes layer as argument, so
it's now sufficient to only bind the texture before drawing the mesh
without messing with sampler uniform.
pull/279/head
Vladimír Vondruš 14 years ago
parent
commit
4dcb5df21c
  1. 88
      src/AbstractShaderProgram.h
  2. 18
      src/AbstractTexture.cpp
  3. 46
      src/AbstractTexture.h
  4. 23
      src/BufferedTexture.h
  5. 3
      src/CubeMapTexture.h
  6. 3
      src/CubeMapTextureArray.h
  7. 3
      src/Texture.h

88
src/AbstractShaderProgram.h

@ -22,8 +22,6 @@
#include <map> #include <map>
#include "Shader.h" #include "Shader.h"
#include "AbstractTexture.h"
#include "BufferedTexture.h"
namespace Magnum { namespace Magnum {
@ -34,34 +32,45 @@ namespace Magnum {
This class is designed to be used via subclassing. Subclasses define these This class is designed to be used via subclassing. Subclasses define these
functions and properties: functions and properties:
- **Attribute location** typedefs defining locations and types for attribute - <strong>%Attribute location</strong> typedefs defining locations and types
binding with Mesh::bindAttribute(), for example: for attribute binding with Mesh::bindAttribute(), for example:
@code @code
typedef Attribute<0, Vector4> Vertex; typedef Attribute<0, Vector4> Vertex;
typedef Attribute<1, Vector3> Normal; typedef Attribute<1, Vector3> Normal;
typedef Attribute<2, Vector2> TextureCoords; 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 @endcode
- **Constructor**, which attaches particular shaders, links the program and - **Constructor**, which attaches particular shaders, links the program and
gets uniform locations, for example: gets uniform locations, for example:
@code @code
// Load shaders from file and attach them to the program MyShader() {
attachShader(Shader::fromFile(Shader::Vertex, "PhongShader.vert")); // Load shaders from file and attach them to the program
attachShader(Shader::fromFile(Shader::Fragment, "PhongShader.frag")); attachShader(Shader::fromFile(Shader::Vertex, "PhongShader.vert"));
attachShader(Shader::fromFile(Shader::Fragment, "PhongShader.frag"));
// Link
link(); // Link
link();
// Get locations of uniforms
transformationMatrixUniform = uniformLocation("transformationMatrix"); // Get locations of uniforms
projectionMatrixUniform = uniformLocation("projectionMatrix"); transformationMatrixUniform = uniformLocation("transformationMatrix");
// more uniforms like light location, colors etc. projectionMatrixUniform = uniformLocation("projectionMatrix");
// more uniforms like light location, colors etc.
}
@endcode @endcode
- **Uniform binding functions**, which set shader uniforms using - **Uniform setting functions**, which will provide public interface for
setUniform() functions. Example: protected setUniform() functions. Example:
@code @code
void setTransformationMatrixUniform(const Matrix4& matrix) { void setTransformationMatrixUniform(const Matrix4& matrix) {
setUniform(transformationMatrixUniform, matrix); setUniform(transformationMatrixUniform, matrix);
} }
void setProjectionMatrixUniform(const Matrix4& matrix) {
setUniform(projectionMatrixUniform, matrix);
}
@endcode @endcode
@subsection AbstractShaderProgramAttributeLocation Binding attribute location @subsection AbstractShaderProgramAttributeLocation Binding attribute location
@ -89,11 +98,40 @@ bindAttributeLocation(TextureCoords::Location, "textureCoords");
// Link... // Link...
@endcode @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 @section AbstractShaderProgramRenderingWorkflow Rendering workflow
Basic workflow with AbstractShaderProgram subclasses is: instancing the class Basic workflow with %AbstractShaderProgram subclasses is: instancing the class
(once at the beginning), then in every frame calling use(), setting uniforms (once at the beginning), then in Object::draw() reimplementation calling
and calling Mesh::draw() (see its documentation for more). 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 @todo Uniform arrays support
@ -297,16 +335,6 @@ class MAGNUM_EXPORT AbstractShaderProgram {
glUniformMatrix4fv(location, 1, GL_FALSE, value.data()); 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: private:
enum State { enum State {
Initialized, Initialized,

18
src/AbstractTexture.cpp

@ -18,18 +18,6 @@
namespace Magnum { namespace Magnum {
#ifndef DOXYGEN_GENERATING_OUTPUT #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 /* Check correctness of binary OR in setMinificationFilter(). If nobody fucks
anything up, this assert should produce the same results on all dimensions, 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 #undef filter_or
#endif #endif
GLint AbstractTexture::maxSupportedLayerCount() {
GLint value;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &value);
return value;
}
GLfloat AbstractTexture::maxSupportedAnisotropy() { GLfloat AbstractTexture::maxSupportedAnisotropy() {
GLfloat value; GLfloat value;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &value); glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &value);

46
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 filtering in setMinificationFilter(), be sure to also either explicitly set
all mip levels or call generateMipmap(). all mip levels or call generateMipmap().
The texture is bound via bind() and setting texture uniform on the shader to the The texture is bound to shader via bind(). Texture uniform on the shader must
texture (see AbstractShaderProgram::setUniform(GLint, const AbstractTexture*)). also be set to particular texture layer using
AbstractShaderProgram::setUniform(GLint, GLint).
See Texture, CubeMapTexture and CubeMapTextureArray documentation for more See Texture, CubeMapTexture and CubeMapTextureArray documentation for more
information. 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 * @brief Max supported anisotropy
* *
@ -478,13 +487,11 @@ class MAGNUM_EXPORT AbstractTexture {
/** /**
* @brief Constructor * @brief Constructor
* @param layer %Texture layer (number between 0 and 31)
* @param target Target, e.g. `GL_TEXTURE_2D`. * @param target Target, e.g. `GL_TEXTURE_2D`.
* *
* Creates one OpenGL texture. * Creates one OpenGL texture.
*/ */
inline AbstractTexture(GLint layer, GLenum target): _target(target), _layer(layer) { inline AbstractTexture(GLenum target): _target(target) {
glActiveTexture(GL_TEXTURE0 + layer);
glGenTextures(1, &texture); glGenTextures(1, &texture);
} }
@ -495,23 +502,19 @@ class MAGNUM_EXPORT AbstractTexture {
*/ */
virtual ~AbstractTexture() = 0; virtual ~AbstractTexture() = 0;
/** @brief %Texture layer */
inline GLint layer() const { return _layer; }
/** @brief OpenGL internal texture ID */ /** @brief OpenGL internal texture ID */
inline GLuint id() const { return texture; } inline GLuint id() const { return texture; }
/** /**
* @brief Bind texture for usage / rendering * @brief Bind texture for rendering
*
* Sets current texture as active in its texture layer. Note that
* only one texture can be bound to given layer.
* *
* @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() { inline void bind(GLint layer) {
glActiveTexture(GL_TEXTURE0 + _layer); glActiveTexture(GL_TEXTURE0 + layer);
glBindTexture(_target, texture); bind();
} }
/** /**
@ -582,8 +585,17 @@ class MAGNUM_EXPORT AbstractTexture {
const GLenum _target; /**< @brief Target */ 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: private:
const GLint _layer;
GLuint texture; GLuint texture;
}; };

23
src/BufferedTexture.h

@ -108,10 +108,10 @@ class BufferedTexture {
/** /**
* @brief Constructor * @brief Constructor
* @param layer %Texture layer (number between 0 and 31) *
* Creates one OpenGL texture.
*/ */
inline BufferedTexture(GLint layer = 0): _layer(layer) { inline BufferedTexture() {
glActiveTexture(GL_TEXTURE0 + layer);
glGenTextures(1, &texture); glGenTextures(1, &texture);
} }
@ -120,13 +120,10 @@ class BufferedTexture {
glDeleteTextures(1, &texture); glDeleteTextures(1, &texture);
} }
/** @copydoc AbstractTexture::layer() */ /** @copydoc AbstractTexture::bind(GLint) */
inline GLint layer() const { return _layer; } inline void bind(GLint layer) {
glActiveTexture(GL_TEXTURE0 + layer);
/** @copydoc AbstractTexture::bind() */ bind();
inline void bind() const {
glActiveTexture(GL_TEXTURE0 + _layer);
glBindTexture(GL_TEXTURE_BUFFER, texture);
} }
/** /**
@ -144,8 +141,12 @@ class BufferedTexture {
} }
private: private:
GLint _layer;
GLuint texture; GLuint texture;
/** @copydoc AbstractTexture::bind() */
inline void bind() {
glBindTexture(GL_TEXTURE_BUFFER, texture);
}
}; };
/** @relates BufferedTexture /** @relates BufferedTexture

3
src/CubeMapTexture.h

@ -70,11 +70,10 @@ class CubeMapTexture: public AbstractTexture {
/** /**
* @brief Constructor * @brief Constructor
* @param layer Texture layer (number between 0 and 31)
* *
* Creates one cube map OpenGL texture. * 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() * @copydoc Texture::setWrapping()

3
src/CubeMapTextureArray.h

@ -58,11 +58,10 @@ class CubeMapTextureArray: public AbstractTexture {
/** /**
* @brief Constructor * @brief Constructor
* @param layer Texture layer (number between 0 and 31)
* *
* Creates one cube map OpenGL texture. * 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() * @copydoc Texture::setWrapping()

3
src/Texture.h

@ -93,14 +93,13 @@ template<size_t textureDimensions> class Texture: public AbstractTexture {
/** /**
* @brief Constructor * @brief Constructor
* @param layer %Texture layer (number between 0 and 31)
* @param target %Texture target. If not set, default value * @param target %Texture target. If not set, default value
* is `Target::Texture1D`, `Target::Texture2D` or * is `Target::Texture1D`, `Target::Texture2D` or
* `Target::Texture3D` based on dimension count. * `Target::Texture3D` based on dimension count.
* *
* Creates one OpenGL texture. * Creates one OpenGL texture.
*/ */
inline Texture(GLint layer = 0, Target target = DataHelper<Dimensions>::target()): AbstractTexture(layer, static_cast<GLenum>(target)) {} inline Texture(Target target = DataHelper<Dimensions>::target()): AbstractTexture(static_cast<GLenum>(target)) {}
/** @brief %Texture target */ /** @brief %Texture target */
inline constexpr Target target() const { return static_cast<Target>(_target); } inline constexpr Target target() const { return static_cast<Target>(_target); }

Loading…
Cancel
Save