92 6.AFrameworkforGLSLEngineUniforms
Figure 6.1. The relationship among engine uniform interfaces and the model-view matrix
implementation.
class ShaderProgram : public ICleanableObserver
{
public:
static void InitializeEngineUniforms()
{
m_engineUniformFactories["u_modelViewMatrix"] =
new ModelViewMatrixFactory();
// ... Add factories for all engine uniforms.
}
static void DestroyEngineUniforms()
{
std::map<std::string, IEngineUniformFactory *>::iterator i;
for (i = m_engineUniformFactories.begin();
i != m_engineUniformFactories.end(); ++i)
{
delete i->second;
}
m_engineUniformFactories.clear();
}
// ... Other public methods.
IEngineUniform
ModelViewMatrixUniform ModelViewMatrixFactory
IEngineUniformFactory
implementsimplements
creates
6.3Implementation 93
private:
static std::map<std::string, IEngineUniformFactory *>
m_engineUniformFactories;
};
Listing 6.6. Initialization and destruction for engine uniform factories.
access to the map afterward. It is also a best practice to free the memory for the
factories on application shutdown, which can be done by calling
Destroy-
EngineUniforms()
, also shown in Listing 6.6.
There are only two steps left to our implementation: identify and store a pro-
gram’s engine uniforms and set the engine uniforms before a draw call. As stated
earlier, each program keeps a map from a uniform’s name to its uniform imple-
mentation,
m_uniformMap. This map is populated after the program is linked. To
implement engine attributes, a program also needs to keep a list of engine uni-
forms:
std::vector<IEngineUniform *> m_engineUniformList;
The relationship among a program, its various pointers to uniforms, and the
uniform factories is shown in Figure 6.2. Using the uniform map and factory
Figure 6.2. The relationship among a program, its uniforms, and the engine uniform
factories.
ShaderProgram
IEngineUniformIEngineUniformFactory
All programs use the
same map of factories.
ICleanable
Uniform
A program has a Uniform object
for each active uniform.
A program has an IEngineUniform for
each uniform found in the factory map.
A program has an ICleanable for
each currently dirty uniform, as explained
in Chapter 5.
94 6.AFrameworkforGLSLEngineUniforms
std::map<std::string, Uniform *>::iterator i;
for (i = m_uniformMap.begin(); i != m_uniformMap.end(); ++i)
{
std::map<std::string, IEngineUniformFactory *>::iterator j =
m_engineUniformFactories.find(i->first);
if (j != m_engineUniformFactories.end())
{
m_engineUniformList.push_back(j->second->Create(i->second));
}
}
Listing 6.7. Identifying and creating engine uniforms for a program.
map, the list of engine uniforms can be populated by creating an engine uniform
for each uniform that has a factory for the uniform’s name, as shown in List-
ing 6.7. In our implementation, engine uniforms are not reference counted, so the
program’s destructor should delete each engine uniform.
Now that a program has a list of its engine uniforms, it is easy to set them
before a draw call. Assuming we are using the delayed technique for setting uni-
forms introduced in the previous chapter, a shader program has a
Clean() meth-
od that is called before each OpenGL draw call to set each dirty uniform. To set
engine uniforms, this method is modified to take a
State object (which presum-
ably is also passed to the draw method that calls this) and then call the
Set()
method for each automatic uniform, as shown in Listing 6.8.
void Clean(const State& state)
{
std::vector<IEngineUniform *>::iterator i;
for (i = m_engineUniformList.begin();
i != m_engineUniformList.end(); ++i)
{
(*i)->Set(state);
}
6.4BeyondGLSLBuiltinUniforms 95
std::for_each(m_dirtyUniforms.begin(), m_dirtyUniforms.end(),
std::mem_fun(&ICleanable::Clean));
m_dirtyUniforms.clear();
}
Listing 6.8. A modified ShaderProgram::Clean() method that automatically sets engine
uniforms.
6.4BeyondGLSLBuiltinUniforms
Thus far, we’ve focused on using our engine uniform framework to replace the
functionality of the old GLSL built-in uniforms. Our framework extends far be-
yond this, however. Its true usefulness is shown when engine uniforms based on
higher-level engine or application-specific data are added. For example, a planet-
based game may have engine uniforms for the sun position or the player’s alti-
tude. Since engine uniforms are defined using interfaces, new engine uniforms
can be added with very little impact.
These high-level engine uniforms can include things like the current time or
frame number, which are useful in animation. For example, texture animation
may be implemented by translating texture coordinates in a fragment shader
based on a uniform containing the current time. To use the uniform, the shader
author doesn’t have to do any additional work other than define the uniform.
If the
ShaderProgram::InitializeEngineUniforms() method is coded
carefully, applications that have access to headers for
IEngineUniform and
IEngineUniformFactory, but not necessarily access to all of the engine’s source
code, can also add engine attributes.
Engine uniforms can even go beyond setting uniform values. In Insight3D,
1
we use engine uniforms to provide shaders access to the scene’s depth and sil-
houette textures. In these cases, the implementation of the
IEngineUni-
form::Set()
method binds a texture to a texture unit instead of actually setting a
uniform’s value (the value for the
sampler2D uniform is set just once to the tex-
ture unit index in the engine uniform’s constructor). It is also common for en-
gines to provide engine uniforms for noise textures.
6.5ImplementationTips
To reduce the amount of code required for new engine uniforms, it is possible to
use C++ templates to let the compiler write a factory for each engine uniform for
1
See http://www.insight3d.com/.
96 6.AFrameworkforGLSLEngineUniforms
template <typename T>
class EngineUniformFactory : public IEngineUniformFactory
{
public:
virtual IEngineUniform *Create(Uniform *uniform)
{
return (new T(uniform));
}
};
Listing 6.9. C++ templates can reduce the amount of handwritten factories.
you. For example, using the factory in Listing 6.8, the ModelViewMatrixFacto-
ry
class would be replaced with the EngineUniformFactory<ModelView-
MatrixUniform>
class using the template shown in Listing 6.9.
If you are up for parsing GLSL code, you could also eliminate the need for
shader authors to declare engine uniforms by carefully searching the shader’s
source for them. This task is nontrivial considering preprocessor transformations,
multiline comments, strings, and compiler optimizations that could eliminate uni-
forms altogether.
Finally, a careful implementation of the
State class can improve perfor-
mance. Specifically, derived state, like the model-view-projection matrix, can be
cached and only recomputed if one of its dependents change.
6.6ConcludingRemarks
Similar concepts to engine uniforms are in widespread use. For example, Open-
SceneGraph
2
has preset uniforms, such as osg_FrameTime and osg_Delta-
FrameTime
, which are automatically updated once per frame. Likewise,
RenderMonkey
3
contains predefined variables for values such as transformation
matrices and mouse parameters. RenderMonkey allows the names for these vari-
ables to be customized to work with different engines.
Acknowledgements
Thanks to Kevin Ring and Sylvain Dupont from Analytical Graphics, Inc., and Chris-
tophe Riccio from Imagination Technologies for reviewing this chapter.
2
See http://www.openscenegraph.org/projects/osg.
3
See http://developer.amd.com/gpu/rendermonkey/Pages/default.aspx.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset