Hi David,
On 14 February 2015 at 14:24, David Callu <[email protected]> wrote:
> 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.
>
Is there a chance you could create an example that illustrates how you are
using the old osg::ShaderComposition framework? This will be give me a
better idea of what could be done to ease the transition/what parts of the
existing framework will need to be maintained.
I am keen to avoid the OSG littering up with lots of dead end bits of code.
>
>
>
> 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 ?
>
Right now I would prefer to avoid adding the osg_ prefix as it's a bit
clumsy, and the approach needed be OSG specific.
I am consider other additions as well - such as having shaders be able to
request the linking of other shaders - so the app could link a single main
shader that pulls in the rest of the shaders to be linked to the program.
Another possible addition would be to have osg::Program settings
controllable via #pragmas. Right now I want to keep things simple though.
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.
>
The new shader composition framework doesn't add complexity where none
exists. If there are many different combinations that need to be supported
then this complexity will be reflected in the shaders, but at least the
developer will be able to see it directly and account for the possibility
of different combinations.
One isn't tied to one uber osg::Program that manages all the possible
combinations. One also can use the define's to inject code, more info below.
> 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.
>
The way to you implement code injection right now was illustrated in a
small way in the example I gave in my previous email, and provided in the
svn/trunk version of the osgshadercomposition example. This example
injects a marco function that modifies a vertex. You don't have to use it
this way instead you can do the following, in the GLSL shader you'd have:
#pragma import_defines( INJECTION_POINT_1, INJECTION_POINT_2 )
...
INJECTION_POINT_1
void main(void)
{
...
INEJECTION_POINT_2
..
}
Then in the StateSet you'd pass in the code to inject via:
stateset->setDefine("INJECTION_POINT_1", "varying vec4 position;")
stateset->setDefine("INJECTION_POINT_2", "position = gl_Vertex;")
The strings you pass in can be multi-line as long as the driver supports
multi-line #define as C and C++ do.
Right now if you nest a setDefine(defStr, valueStr) in the scene graph
it'll replace entirely any previous value but perhaps we could generalize
things further to allow one to prepend or append the valueStr to any
already defined in StateSet above that point the scene graph. This would
add complexity though, so unless it's really shown to be needed I'll wait
till it is.
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 Shader
> Attribute. With modern OpenGL and depreciated data like fog, light, ...
> we need a way to introduce our custom attribute.
>
Unifoms should be able to take over at least part of the attribute role.
In the case of fallbacks for things like osg::Light/glLight we'd need these
atttributes to provides one or more Uniforms when they are needed. It
could be that ShaderAttribute might still have a roll to play in this. The
code injection side is something that would need to be looked at if we are
removing the old osg::ShaderComposition framework.
Something I have used in the osgTerrain::DispalcementMappingTechnique to
have a custom Drawable check the value of a Define by querying osg::State
and then having the Drawable choose between different data to send to
OpenGL, in this case the actual implementation chooses between using
GL_QUADS or GL_LINES_ADJACENCY for the glDrawElements call depending upon
whether the "COMPUTE_DIAGONALS" define exists. This "COMPUTE_DEFINES"
toggles on/off a geometry shader.
> 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 !!!
>
>
Have you published your osgLighting so that others can review it?
>
> 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.
>
The new #pragma(tic) shader composition support has to manage multiple GL
program and shader objects, one for each combination of define settings, as
the define settings change then a new object has to be selected. The
current implementation is written to get it working rather than to optimize
it 100% so there are still opportunities to make the selection of objects
more efficient.
How it compares to the old osg::ShaderComposition I can't say yet as I'd
need to have a decent sized scene with two opposing approaches
implemented. Without these shader composition features the alternative is
to use lots of separate osg::Program of push lots of uniforms into the
shaders to do the switching, so these will inevitably have an overhead
too. Which comes out on top will depend a great deal on how well each path
is optimized in the driver and on the scene graph side.
> 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.
>
As I explained above code injection is possible right now. It's not
possible to know what you mean by import_module, it's such an open ended
thing. import_define currently has a very specific GLSL based
implementation, it literly is all about provide #define implementations for
the GLSL compiler to utilize.
Having a link_shader() or import_shader() etc. would be possible addition
to sit alongside import_define but wouldn't replace it. There are various
things we could consider adding to the #pragma approach. If you have an
idea could be specific about how it'd be used and how it'd be implemented.
Cheers,
Robert.
>
>
>
>
> 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 <[email protected]>:
>
>> 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
>> [email protected]
>> http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org
>>
>>
>
> _______________________________________________
> osg-users mailing list
> [email protected]
> http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org
>
>
_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org