Hi David,
I don't see where you see problems. The old ShaderComposition is still there, and basically nothing has changed, as the new feature is orthogonal to the old one. I'm using some adapted version of the ShaderComposition framework, and it still works with the current trunk, so could you please provide some example what is failing?

Cheers
Sebastian
Hi Robert, Hi all


I also implement a shader composition framework for a client, based on osg shader composition, so remove it will be a big loss. My implementation handle OpenGL version, some pragma (optimize and debug), generate a main function for vertex and fragment shader based on what is found in shaderComponent list. So change from previous shader composition to new one will not be straight forward.



some thought about new shader composition

1)
OpenGL Shading Language specification
3.3 Preprocessor :
#pragma allows implementation dependent compiler control

so pragma "requires" and "import_defines" should be use by shader compiler, replace them by "osg_requires" and "osg_import_defines" could be prevent futur pragma collisions. Thought ?

but pragma is an elegant way to insert information directly in shader code. well spot !!!




2)
Define a main function with all possibility and use define to choose the ones to enable and the ones to not will make
a really complex shader, imagine, you optionally use
- instancing
- bump/deplacement mapping
- vertex blending
- material/texture/multi-texture
- lighting/shadow
- fog
- physic
- one/multi fragment output
You will be quickly crazy. The one that work with SpeedTree Shader knows what I am talking about. This is why I prefer your previous approach that inject code found in StateAttribute in final program. It is also less intrusive. If i need to insert a new shader component in the program, no need to change main shader. More over, I introduce some new ShaderAttribute like "camera" to define where fragment shader output have to go, or "drawable" to define which vertex shader input are available, "Instance" to optionally use draw instance, ... so don't remove ShaderAttribute. With modern OpenGL and depreciated data like fog, light, ... we need a way to introduce our custom attribute. I create a nodekit osgLighting just to manage different light source. I have 5 different light source (sun, car, streetlight, ...) that all use shaderAttribute to define data that compose the light and shader that use this data. So don't remove shaderAttribute this is really a good idea you have here !!!


3)
On a performance point of view, it will be a good thing to use subroutine to enable/disable feature. Many different path in shader composition result in many different program. And so many change of program in one frame, perhaps use the program A, then the program B, then the program A again... This is the main problem I currently have in my shader composition implementation.

4)
I always think that lack of define injection in shader is a drawback in osg. With your function and name convention, this will be difficult to add this feature. Replace import_define by import_module will fix this. Because we talk about shader module, lighting, fog, bump mapping, shadow ... all of this could be define as shader module.




I really think that shader composition is a big piece of software, really usefull, and really complex. I think we need a debate about this with all user that have already try to implement a shader composition. Clearly define our needs (have reusable module, keep code in shader files, use OpenGL feature to improve performance like subroutine, pipeline object, ...), debate about solution, propose implementation...


This problem is really new and so few research has be done an the subject. I did my first implementation in octobre 2010 I have used my shader composition in many way. Found some cool stuff and other bad about my design. I look for existing solution on internet, I found some one like libsh <http://libsh.org/> but not easy and clean way to solve it. Recently i found CosMo <http://www.vrvis.at/publications/pdfs/PB-VRVis-2014-001.pdf> that did the job in many way like my implementation, but more deeply in abstraction of the code. They don't think in term of shader but in term of pipeline graphics, shader composition will decide which code put in vertex/tessellation/geometry/fragment shader. I also do this
but it's clearly not a good idea.

You propose a clean and (perhaps too) easy way to do the job. This is fresh. This is nice to have a new view on this complex problem. So, Robert, Other, we need to talk about this to create a robust, clean and easy way to do shader composition.

Thought ?

My 2 cents
David



2015-02-13 19:01 GMT+01:00 Robert Osfield <robert.osfi...@gmail.com <mailto:robert.osfi...@gmail.com>>:

    Hi All,

    Attached is a screenshot of running (clock.osgt is in
    OpenSceneGraph-Data):

      osgshadercomposition clock.osgt

    A very simple example that provides a single osg::Program that
    have five different shader combinations used on the same subgraph
    to produce different results.  From left to right they are:

       1) White unlit box - is the default implementation with no
    define's supplied from osg::StateSet's so no texturing or lighting

       2) White lit box - "GL_LIGHTING" define is provided to the
    shaders via the stateset->setDefine("GL_LIGHTING");

       3) Textured unlit box, "GL_TEXTURE_2D" define is provide to the
    shader via stateset->setDefine("GL_TEXTURE_2D");

       4) Textured lit box, both "GL_LIGHTING"  and "GL_TEXTURE_2D"
    defines are provide via stateset->setDefine("GL_TEXTURE_2D"); etc.

       5) As for 4 but a macro function is passed to the shader via :

                   stateset->setDefine("VERTEX_FUNC(v)" , "vec4(v.x,
    v.y, v.z * sin(osg_SimulationTime), v.w)");

    In each of these instances the define you provide via
    StateSet::setDefine(DefineString) is mapped to a set of:

       #define DefineString

    The is passed to the shader compilation. When
    StateSet::setDefine(DefineString, DefineValue) is used it's mapped to:

      #define DefineString DefineValue

    So for the case 5 the Define's passed to the StateSet map to a set
    of shader lines that are inserted before the body of the shader thus:

       #define GL_LIGHTING
       #define GL_TEXTURE_2D
       #define VERTEX_FUNC(v) vec4(v.x, v.y, v.z *
    sin(osg_SimulationTime), v.w)

    These #defines are then used by the three shaders attached to the
    osg::Program in the osgshadercomposition example, the first shader
    is OpenSceneGraph-Data/shader/osgshadercomposition.vert which
    looks like:

    -- start of osgshadercomposition.vert --

    #pragma import_defines ( GL_LIGHTING, GL_TEXTURE_2D, VERTEX_FUNC(v) )

    #ifdef GL_LIGHTING
    // forward declare lighting computation, provided by lighting.vert
    shader
    void directionalLight( int lightNum, vec3 normal, inout vec4 color );
    #endif

    #ifdef GL_TEXTURE_2D
    varying vec2 texcoord;
    #endif

    #ifdef VERTEX_FUNC
    uniform float osg_SimulationTime;
    #endif

    varying vec4 basecolor;

    void main(void)
    {
        basecolor = gl_Color;

    #ifdef GL_LIGHTING
        directionalLight( 0, gl_Normal.xyz, basecolor);
    #endif

    #ifdef GL_TEXTURE_2D
        // if we want texturing we need to pass on texture coords
        texcoord = gl_MultiTexCoord0.xy;
    #endif

    #ifdef VERTEX_FUNC
        gl_Position   = gl_ModelViewProjectionMatrix *
    VERTEX_FUNC(gl_Vertex);
    #else
        gl_Position   = gl_ModelViewProjectionMatrix * gl_Vertex;
    #endif

    }

    -- end of osgshadercomposition.vert --

    First thing of note is the first line:

       #pragma import_defines(GL_LIGHTING, GL_TEXTURE_2D, VERTEX_FUNC(v))

    This #pragma is read and used by the OSG to tell it what Define's
    to look up and apply if they are provided in the parental chain of
    StateSet's, note the matching is case sensitive and in the case of
    macro VERTEX_FUNC(v) you must use exactly the same parameters
    including name as the matching is doing straight std::string match
    internally. When using import_defines() the defines are all
    treated as optional, so if they aren't supplied by the StateSet's
    then nothing is passed along.  Also if you switch off a define via
    setDefine("GL_LIGHTING", osg::StateAttribute::OFF); then this
    value will not be passed on.

    It is intended that the shaders themselves will use #ifdef blocks
    to enable/disable various features so that all the various paths
    make sense.  This approach make it possible to implement optional
    varying and uniform usage as well as operations within the main or
    functions.  It also ensures that the shader compilation done by
    the drive just compiles what is required, it doesn't have extra
    uniform variables that might be constant or varyings and uniforms
    that might not be used in all code paths.

    --

    The second shader is OpenSceneGraph-Data/shaders/lighting.vert
    that provides the directionalLight() function that is used by the
    osgshadercomposition.vert main when GL_LIGHTING is enabled via
    StateSet::setDefine("GL_LIGHTING").  Since this shader is not
    required when GL_LIGHTING is not defined it's not appropriate to
    link the shader to Program, so here we provide a #pragma
    requres(GL_LIGHTING) as a directive to the OSG so it know when to
    link it.  The shader looks :

    -- start of lighting.vert

    #pragma requires(GL_LIGHTING)

    void directionalLight( int lightNum, vec3 normal, inout vec4 color )
    {
        vec3 n = normalize(gl_NormalMatrix * normal);

        float NdotL = dot( n,
    normalize(gl_LightSource[lightNum].position.xyz) );
        NdotL = max( 0.0, NdotL );

        float NdotHV = dot( n, gl_LightSource[lightNum].halfVector.xyz );
        NdotHV = max( 0.0, NdotHV );
    #if 1
        color *= gl_LightSource[lightNum].ambient +
                 gl_LightSource[lightNum].diffuse * NdotL;
    #else
        color *= gl_FrontLightModelProduct.sceneColor +
    gl_FrontLightProduct[lightNum].ambient +
    gl_FrontLightProduct[lightNum].diffuse * NdotL;
    #endif
    #if 0
        if ( NdotL * NdotHV > 0.0 )
            color += gl_FrontLightProduct[lightNum].specular * pow(
    NdotHV, gl_FrontMaterial.shininess );
    #endif
    }

    -- end of lighting.vert

    Since this shader requires GL_LIGHTING it doesn't make any sense
    to had an optional code paths using this define so the shader
    doesn't have any.  If you did want to add in use of defines as in
    the osgshadercomposition.vert shader you'd need to specify them
    with an additional #pragma import_defines(...) line as per the
    osgshadercomposition.vert shader.

    The optional linking facility provided by #pragma requires(..)
    enables on to optionally pull in whole chunks of the shader, so if
    you wanted you could optional link in a geometry shader, or
    disable vertex shaders and fallback to use fixed function pipeline
    if you so wished.  It also allows you to attach multiple shaders
    to the main osg::Program and have these shaders provide the same
    functions but each with a different implementation, but then use
    different define's to pull in the implementation you want and no
    link the unneeded ones without any conflicts.

    --

    Finally we have the osgshadercomposition.frag shader that does the
    optional texture mapping, the shader looks like:

    -- start of osgshadercomposition.frag
    #pragma import_defines ( GL_TEXTURE_2D )

    #ifdef GL_TEXTURE_2D
    uniform sampler2D texture0;

    varying vec2 texcoord;
    #endif

    varying vec4 basecolor;

    void main(void)
    {
    #ifdef GL_TEXTURE_2D
        gl_FragColor = texture2D( texture0, texcoord) * basecolor;
    #else
        gl_FragColor = basecolor;
    #endif
    }
    -- end of osgshadercomposition.frag

    Note the #pragma import_defines pulls in the optional
    GL_TEXTURE_2D, and when it's provided both the uniform sample2D
    texture0; and the varying texcoords are compiled in, along with
    the texture2D(..) fetch in the main().

    --

    The use of OSG specific #pragma enables the shaders themselves to
    be passed as-is to the driver to compile and have these custom
    #pragma's simply ignored by the driver.  This means one can also
    use the same shaders in non OSG applications, such as developing
    the shaders in a 3rd party tool.  In this tool you'd need to
    manually add in the #define's to test the different path ways out
    and then comment them out/remove them for the final shaders you
    pass to the OSG so the StateSet::setDefine() magic can provide all
    the different paths you need.

    This ability to have shaders that are readable and usable as-is
    without code insertion/mucking about by the application is a real
    bonus.  When I was modifying the old osgshadercomposition code
    paths (now placed in
    examples/osgshadercomposition/oldshadercomposition.cpp) I was
    struck by how hard is was to work on what on earth was going on
    with how the C++ code passing strings for code injection during
    shader composition to the final GLSL. Basically the old way is
    complicated and so obfuscated it's now seems an obviously bad
    approach - a case of hard problems needing complex solutions.

    My hope is that the new "#pragma(tic) shader composition" approach
    is simple to understand and use, I've certainly found it fun to
    start using in - it's now used in the new
    osgTerrain::DisplacementTechnique to enable toggling between
    different features.  It's actually so simple and powerful I can't
    believe I never thought of this approach years ago.  I guess that
    is the way sometimes with hard problems, you spend too close to a
    problem that the you can't see through the complexity to find a
    simple solution, but once the simple and elegant solution is
    presented to you is like it's so OBVIOUS to hard to fathom why it
    was overlooked for so long.

    Given how much user friendly the new approach is over the previous
    attempts at shader composition like the old osg::ShaderComposition
    framework and the VirtualProgram approach I believe it's
    appropriate to deprecate the ShaderComposition, in fact my
    preference would be to remove it from the OSG as it's just adding
    weight and complexity to the OSG that offers less power than the
    new approach.  I know there are some users out there who've
    worked/modified osg::ShaderComposition framework to work better,
    so removing it completely will be an initial loss, but I feel
    keeping the API simpler is worth that bit of pain.  Porting to
    #pragm(tic) shader composition should be hopefully be relatively
    straight forward.  The osgvirtualprogram provides another previous
    attempt that now feel is eclipsed by the new approach so again I'm
    keen to simple remove it so user don't got learning the wrong things.

    For osgEarth, it has it's own VirtualProgram infrastructure that
    works across a range of OSG versions, but I don't expect any
    problems so once OSG-3.4 with the new shader composition
    functionality both will happily work together.  In fact the
    #pragma's and StateSet::setDefine(..) functionality should
    hopefully work out of the box with VirtualProgram and make it more
    flexible and powerful.  Longer term it could be mean that
    osgEarth::VirtualProgram could be deprecated and new shaders
    composition used instead, but I don't see any hurry to do this.

    Now I've written this baby, it's over to you community, see what
    fun things you can dream up to do with it ;-)





    _______________________________________________
    osg-users mailing list
    osg-users@lists.openscenegraph.org
    <mailto:osg-users@lists.openscenegraph.org>
    http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org




_______________________________________________
osg-users mailing list
osg-users@lists.openscenegraph.org
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

_______________________________________________
osg-users mailing list
osg-users@lists.openscenegraph.org
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Reply via email to