root/OpenSceneGraph/trunk/src/osgUtil/ShaderGen.cpp @ 9940

Revision 9940, 11.8 kB (checked in by robert, 5 years ago)

From Maciej Krol, "As promised to Roland I assembled simple shader generator. ShaderGenVisitor? converts accumulated fixed function pipeline state sets to ones with shader programs. Generated state sets are attached to geometries and stored in ShaderGenCache? for reuse.

Very simple cases of state configuration are supported (all the ones I really need):
- single per pixel not attenuated non spot light source ON/OFF
- exp2 fog ON/OFF
- diffuse texture in rgb + optional specular gloss in alpha (Texture unit 0) ON/OFF
- normal map texture (Texture unit 1 and Tangent in VertexAttribArray? 6) ON/OFF
- blending and alpha testing (not in shader pipeline)

To view fixed function pipeline files and paged databases simply run >osgshadergen myfile.osg"

Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under 
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13
14/**
15 * \brief    Shader generator framework.
16 * \author   Maciej Krol
17 */
18
19#include <osgUtil/ShaderGen>
20#include <osg/Geode>
21#include <osg/Geometry> // for ShaderGenVisitor::update
22#include <sstream>
23
24using namespace osgUtil;
25
26namespace osgUtil
27{
28
29/// State extended by mode/attribute accessors
30class StateEx : public osg::State
31{
32public:
33    StateEx() : State() {}
34
35    osg::StateAttribute::GLModeValue getMode(osg::StateAttribute::GLMode mode,
36        osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const
37    {
38        return getMode(_modeMap, mode, def);
39    }
40
41    osg::StateAttribute *getAttribute(osg::StateAttribute::Type type, unsigned int member = 0) const
42    {
43        return getAttribute(_attributeMap, type, member);
44    }
45
46    osg::StateAttribute::GLModeValue getTextureMode(unsigned int unit,
47        osg::StateAttribute::GLMode mode,
48        osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const
49    {
50        return unit < _textureModeMapList.size() ? getMode(_textureModeMapList[unit], mode, def) : def;
51    }
52
53    osg::StateAttribute *getTextureAttribute(unsigned int unit, osg::StateAttribute::Type type) const
54    {
55        return unit < _textureAttributeMapList.size() ? getAttribute(_textureAttributeMapList[unit], type, 0) : 0;
56    }
57
58    osg::Uniform *getUniform(const std::string& name) const
59    {
60        UniformMap::const_iterator it = _uniformMap.find(name);
61        return it != _uniformMap.end() ?
62            const_cast<osg::Uniform *>(it->second.uniformVec.back().first) : 0;
63    }
64
65protected:
66
67    osg::StateAttribute::GLModeValue getMode(const ModeMap &modeMap,
68        osg::StateAttribute::GLMode mode,
69        osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const
70    {
71        ModeMap::const_iterator it = modeMap.find(mode);
72        return (it != modeMap.end() && it->second.valueVec.size()) ? it->second.valueVec.back() : def;
73    }
74
75    osg::StateAttribute *getAttribute(const AttributeMap &attributeMap,
76        osg::StateAttribute::Type type, unsigned int member = 0) const
77    {
78        AttributeMap::const_iterator it = attributeMap.find(std::make_pair(type, member));
79        return (it != attributeMap.end() && it->second.attributeVec.size()) ?
80            const_cast<osg::StateAttribute*>(it->second.attributeVec.back().first) : 0;
81    }
82};
83
84}
85
86void ShaderGenCache::setStateSet(unsigned int stateMask, osg::StateSet *stateSet)
87{
88    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
89    _stateSetMap[stateMask] = stateSet;
90}
91
92osg::StateSet *ShaderGenCache::getStateSet(unsigned int stateMask) const
93{
94    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
95    StateSetMap::const_iterator it = _stateSetMap.find(stateMask);
96    return (it != _stateSetMap.end()) ? it->second.get() : 0;
97}
98
99osg::StateSet *ShaderGenCache::getOrCreateStateSet(unsigned int stateMask)
100{
101    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
102    StateSetMap::iterator it = _stateSetMap.find(stateMask);
103    if (it == _stateSetMap.end())
104    {
105        osg::StateSet *stateSet = createStateSet(stateMask);
106        _stateSetMap.insert(it, std::make_pair(stateMask, stateSet));
107        return stateSet;
108    }
109    return it->second.get();
110}
111
112osg::StateSet *ShaderGenCache::createStateSet(unsigned int stateMask) const
113{
114    osg::StateSet *stateSet = new osg::StateSet;
115    osg::Program *program = new osg::Program;
116    stateSet->setAttribute(program);
117
118    std::ostringstream vert;
119    std::ostringstream frag;
120
121    // write varyings
122    if ((stateMask & LIGHTING) && !(stateMask & NORMAL_MAP))
123    {
124        vert << "varying vec3 normalDir;\n";
125    }
126
127    if (stateMask & (LIGHTING | NORMAL_MAP))
128    {
129        vert << "varying vec3 lightDir;\n";
130    }
131
132    if (stateMask & (LIGHTING | NORMAL_MAP | FOG))
133    {
134        vert << "varying vec3 viewDir;\n";
135    }
136   
137    // copy varying to fragment shader
138    frag << vert.str();
139
140    // write uniforms and attributes
141    int unit = 0;
142    if (stateMask & DIFFUSE_MAP)
143    {
144        osg::Uniform *diffuseMap = new osg::Uniform("diffuseMap", unit++);
145        stateSet->addUniform(diffuseMap);
146        frag << "uniform sampler2D diffuseMap;\n";
147    }
148
149    if (stateMask & NORMAL_MAP)
150    {
151        osg::Uniform *normalMap = new osg::Uniform("normalMap", unit++);
152        stateSet->addUniform(normalMap);
153        frag << "uniform sampler2D normalMap;\n";
154        program->addBindAttribLocation("tangent", 6);
155        vert << "attribute vec3 tangent;\n";
156    }
157
158    vert << "\n"\
159        "void main()\n"\
160        "{\n"\
161        "  gl_Position = ftransform();\n";
162
163    if (stateMask & (DIFFUSE_MAP | NORMAL_MAP))
164    {
165        vert << "  gl_TexCoord[0] = gl_MultiTexCoord0;\n";
166    }
167
168    if (stateMask & NORMAL_MAP)
169    {
170        vert <<
171            "  vec3 n = gl_NormalMatrix * gl_Normal;\n"\
172            "  vec3 t = gl_NormalMatrix * tangent;\n"\
173            "  vec3 b = cross(n, t);\n"\
174            "  vec3 dir = -vec3(gl_ModelViewMatrix * gl_Vertex);\n"\
175            "  viewDir.x = dot(dir, t);\n"\
176            "  viewDir.y = dot(dir, b);\n"\
177            "  viewDir.z = dot(dir, n);\n"\
178            "  vec4 lpos = gl_LightSource[0].position;\n"\
179            "  if (lpos.w == 0.0)\n"\
180            "    dir = lpos.xyz;\n"\
181            "  else\n"\
182            "    dir += lpos.xyz;\n"\
183            "  lightDir.x = dot(dir, t);\n"\
184            "  lightDir.y = dot(dir, b);\n"\
185            "  lightDir.z = dot(dir, n);\n";
186    }
187    else if (stateMask & LIGHTING)
188    {
189        vert <<
190            "  normalDir = gl_NormalMatrix * gl_Normal;\n"\
191            "  vec3 dir = -vec3(gl_ModelViewMatrix * gl_Vertex);\n"\
192            "  viewDir = dir;\n"\
193            "  vec4 lpos = gl_LightSource[0].position;\n"\
194            "  if (lpos.w == 0.0)\n"\
195            "    lightDir = lpos.xyz;\n"\
196            "  else\n"\
197            "    lightDir = lpos.xyz + dir;\n";
198    }
199    else if (stateMask & FOG)
200    {
201        vert <<
202            "  viewDir = -vec3(gl_ModelViewMatrix * gl_Vertex);\n"\
203            "  gl_FrontColor = gl_Color;\n";
204    }
205    else
206    {
207        vert << "  gl_FrontColor = gl_Color;\n";
208    }
209   
210    vert << "}\n";
211
212    frag << "\n"\
213        "void main()\n"\
214        "{\n";
215
216    if (stateMask & DIFFUSE_MAP)
217    {
218        frag << "  vec4 base = texture2D(diffuseMap, gl_TexCoord[0].st);\n";
219    }
220    else
221    {
222        frag << "  vec4 base = vec4(1.0);\n";
223    }
224
225    if (stateMask & NORMAL_MAP)
226    {
227        frag << "  vec3 normalDir = texture2D(normalMap, gl_TexCoord[0].st).xyz*2.0-1.0;\n";
228    }
229
230    if (stateMask & (LIGHTING | NORMAL_MAP))
231    {
232        frag <<
233            "  vec3 nd = normalize(normalDir);\n"\
234            "  vec3 ld = normalize(lightDir);\n"\
235            "  vec3 vd = normalize(viewDir);\n"\
236            "  vec4 color = gl_FrontLightModelProduct.sceneColor;\n"\
237            "  color += gl_FrontLightProduct[0].ambient;\n"\
238            "  float diff = max(dot(ld, nd), 0.0);\n"\
239            "  color += gl_FrontLightProduct[0].diffuse * diff;\n"\
240            "  color *= base;\n"\
241            "  if (diff > 0.0)\n"\
242            "  {\n"\
243            "    vec3 halfDir = normalize(ld+vd);\n"\
244            "    color.rgb += base.a * gl_FrontLightProduct[0].specular.rgb * \n"\
245            "      pow(max(dot(halfDir, nd), 0.0), gl_FrontMaterial.shininess);\n"\
246            "  }\n";
247    }
248    else
249    {
250        frag << "  vec4 color = base;\n";
251    }
252
253    if (!(stateMask & LIGHTING))
254    {
255        frag << "  color *= gl_Color;\n";
256    }
257
258    if (stateMask & FOG)
259    {
260        frag <<
261            "  float d2 = dot(viewDir, viewDir);//gl_FragCoord.z/gl_FragCoord.w;\n"\
262            "  float f = exp2(-1.442695*gl_Fog.density*gl_Fog.density*d2);\n"\
263            "  color.rgb = mix(gl_Fog.color.rgb, color.rgb, clamp(f, 0.0, 1.0));\n";
264    }
265   
266    frag << "  gl_FragColor = color;\n";
267    frag << "}\n";
268
269    std::string vertstr = vert.str();
270    std::string fragstr = frag.str();
271
272    osg::notify(osg::DEBUG_INFO) << "ShaderGenCache Vertex shader:\n" << vertstr << std::endl;
273    osg::notify(osg::DEBUG_INFO) << "ShaderGenCache Fragment shader:\n" << fragstr << std::endl;
274
275    program->addShader(new osg::Shader(osg::Shader::VERTEX, vertstr));
276    program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragstr));
277
278    return stateSet;
279}
280
281ShaderGenVisitor::ShaderGenVisitor() :
282    NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
283    _stateCache(new ShaderGenCache),
284    _state(new StateEx)
285{
286}
287
288ShaderGenVisitor::ShaderGenVisitor(ShaderGenCache *stateCache) :
289    NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
290    _stateCache(stateCache),
291    _state(new StateEx)
292{
293}
294
295void ShaderGenVisitor::setRootStateSet(osg::StateSet *stateSet)
296{
297    if (_rootStateSet.valid())
298        _state->removeStateSet(0);
299    _rootStateSet = stateSet;
300    if (_rootStateSet.valid())
301        _state->pushStateSet(_rootStateSet.get());
302}
303
304void ShaderGenVisitor::reset()
305{
306    _state->popAllStateSets();
307    if (_rootStateSet.valid())
308        _state->pushStateSet(_rootStateSet.get());
309}
310
311void ShaderGenVisitor::apply(osg::Node &node)
312{
313    osg::StateSet *stateSet = node.getStateSet();
314
315    if (stateSet)
316        _state->pushStateSet(stateSet);
317
318    traverse(node);
319
320    if (stateSet)
321        _state->popStateSet();
322}
323
324void ShaderGenVisitor::apply(osg::Geode &geode)
325{
326    osg::StateSet *stateSet = geode.getStateSet();
327    if (stateSet)
328        _state->pushStateSet(stateSet);
329
330    for (unsigned int i=0; i<geode.getNumDrawables(); ++i)
331    {
332        osg::Drawable *drawable = geode.getDrawable(i);
333        osg::StateSet *ss = drawable->getStateSet();
334        if (ss)
335            _state->pushStateSet(ss);
336
337        update(drawable);
338
339        if (ss)
340            _state->popStateSet();
341    }
342
343    if (stateSet)
344        _state->popStateSet();
345}
346
347void ShaderGenVisitor::update(osg::Drawable *drawable)
348{
349    // update only geometry due to compatibility issues with user defined drawables
350    osg::Geometry *geometry = drawable->asGeometry();
351    if (!geometry)
352        return;
353
354    StateEx *state = static_cast<StateEx *>(_state.get());
355    // skip nodes without state sets
356    if (state->getStateSetStackSize() == (_rootStateSet.valid() ? 1 : 0))
357        return;
358
359    // skip state sets with already attached programs
360    if (state->getAttribute(osg::StateAttribute::PROGRAM))
361        return;
362
363    unsigned int stateMask = 0;
364    //if (state->getMode(GL_BLEND) & osg::StateAttribute::ON)
365    //    stateMask |= ShaderGen::BLEND;
366    if (state->getMode(GL_LIGHTING) & osg::StateAttribute::ON)
367        stateMask |= ShaderGenCache::LIGHTING;
368    if (state->getMode(GL_FOG) & osg::StateAttribute::ON)
369        stateMask |= ShaderGenCache::FOG;
370    if (state->getTextureAttribute(0, osg::StateAttribute::TEXTURE))
371        stateMask |= ShaderGenCache::DIFFUSE_MAP;
372    if (state->getTextureAttribute(1, osg::StateAttribute::TEXTURE) &&
373        geometry->getVertexAttribArray(6)) //tangent
374        stateMask |= ShaderGenCache::NORMAL_MAP;
375
376    // Get program and uniforms for accumulated state.
377    osg::StateSet *progss = _stateCache->getOrCreateStateSet(stateMask);
378    // Set program and uniforms to the last state set.
379    osg::StateSet *ss = const_cast<osg::StateSet *>(state->getStateSetStack().back());
380    ss->setAttribute(progss->getAttribute(osg::StateAttribute::PROGRAM));
381    ss->setUniformList(progss->getUniformList());
382   
383}
Note: See TracBrowser for help on using the browser.