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.
vectorfields
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 "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:
- <strong>%Attribute location</strong> 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,

18
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);

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
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;
};

23
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

3
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()

3
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()

3
src/Texture.h

@ -93,14 +93,13 @@ template<size_t textureDimensions> 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<Dimensions>::target()): AbstractTexture(layer, static_cast<GLenum>(target)) {}
inline Texture(Target target = DataHelper<Dimensions>::target()): AbstractTexture(static_cast<GLenum>(target)) {}
/** @brief %Texture target */
inline constexpr Target target() const { return static_cast<Target>(_target); }

Loading…
Cancel
Save