[2.1] Multiple Geometry Shaders (for different primitives) Topic is solved

Problems building or running the engine, queries about how to use features etc.
Post Reply
gabbsson
Halfling
Posts: 65
Joined: Wed Aug 08, 2018 9:03 am
x 13

[2.1] Multiple Geometry Shaders (for different primitives)

Post by gabbsson »

Render System: GL3Plus

Hello!

Realized I have a second question regarding geometry shaders and figured I may as well make a new post.

As mentioned in my previous post I am using a geometry shader to implement line thickness for lines, but turning them into quads in screenspace.
This works fine but when I render something other than lines using that specific Hlms (my own version of Unlit in this case) it renders nothing.

I'm assuming that because the geometry shader specifices what type of primitive it handles it discards all others.
Ideally it would just pass-through other primitives that do not match the layout qualifer (layout(lines) in;)
After searching the internet it seems I should create different geometry shaders for the different types of primitives I want to use.

So for triangles I basically just want to pass-through and for points I want to do similar thickness stuff.

My question is: How do I tell Ogre to use the different geometry shaders (in a single Hlms)?

To get the one I have working I simply created a GeometryShader_gs.glsl file and it compiled and used it automatically. So I'm guessing there is something telling Ogre to look for that file. As for the other shader types this is done for each Hlms type, but I would prefer not adding a new Hlms just to be able to render other types or primitives, that seems overkill.

Edit: I suppose it could be done by having datablocks define what shader they need, but again that would mean specific datablocks only work for specific primitives, which doesn't seem like a relation that should be required. It would also mean I need to create many new datablocks. The best would be if it could just automatically switch geometry shader based on the type of primitive.

If there is a different/better solution let me know!
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: [2.1] Multiple Geometry Shaders (for different primitives)

Post by dark_sylinc »

The simplest way I see is to store a magic value in Renderable::setCustomParameter to identify what GS to use, and evaluate that in calculateHashForPreCreate (you'll have to create a class "MyHlmsPbs" that derives from HlmsPbs just to overload this function):

Code: Select all

const size_t c_geometryShaderMagicValue = 123456;

renderableA->setCustomParameter( c_geometryShaderMagicValue, Vector4( 0.0f ) ); // Use GS Method A
renderableB->setCustomParameter( c_geometryShaderMagicValue, Vector4( 1.0f ) ); // Use GS Method B

void MyHlmsPbs::calculateHashForPreCreate( Renderable *renderable, PiecesMap *inOutPieces )
{
    HlmsPbs::calculateHashForPreCreate( renderable, inOutPieces );
    
    const Renderable::CustomParameterMap &customParams = renderable->getCustomParameters();
    Renderable::CustomParameterMap::const_iterator it =
            customParams.find( c_geometryShaderMagicValue );
    
    if( it != customParams.end() )
    {
        //Geometry Shader will be used. Set which method
        setProperty( "geometry_shader_method", (int32)it->second );
    }
}
Then in the template:

Code: Select all

@property( geometry_shader_method == 0 )
... your method A geometry shader ...
@end
@property( geometry_shader_method == 1 )
... your method B geometry shader ...
@end
Just make sure the setCustomParameter gets called before assigning the datablock, otherwise you'll have to do this to refresh the Hlms hashes:

Code: Select all

HlmsDatablock *datablock = renderableA->getDatablock();
Hlms *hlms = datablock->getCreator();
if( hlms->getType() == Ogre::HLMS_PBS )
{
    assert( dynamic_cast<MyHlmsPbs*>( hlms ) );
    MyHlmsPbs *myHlmsPbs = static_cast<MyHlmsPbs*>( hlms );
    uint32 hash, casterHash;
    myHlmsPbs->calculateHashFor( renderableA, hash, casterHash );
    renderableA->_setHlmsHashes( hash, casterHash );
}
Cheers


Edit: If you want to identify your methods by name, you can do so:

Code: Select all

const size_t c_geometryShaderMagicValue = 123456;
const IdString c_methodNames[2] =
{
    IdString( "my_method_A" ),
    IdString( "my_method_B" ),
};

void MyHlmsPbs::calculateHashForPreCreate( Renderable *renderable, PiecesMap *inOutPieces )
{
    HlmsPbs::calculateHashForPreCreate( renderable, inOutPieces );

    //Always set these, because otherwise when unset "geometry_shader_method == my_method_A" will evaluate to true since 0 == 0
    for( int i=0; i<2; ++i )
            setProperty( c_MethodNames[u], (int32)c_methodNames[i].mHash );
    
    const Renderable::CustomParameterMap &customParams = renderable->getCustomParameters();
    Renderable::CustomParameterMap::const_iterator it =
            customParams.find( c_geometryShaderMagicValue );
    
    if( it != customParams.end() )
    {
        //Geometry Shader will be used. Set which method
        //Remember to perform bounds checkings else c_MethodNames[(size_t)it->second] may be out of bounds!
        setProperty( "geometry_shader_method", (int32)c_MethodNames[(size_t)it->second].mHash );
    }
}
Then in the template:

Code: Select all

@property( geometry_shader_method == my_method_A )
... your method A geometry shader ...
@end
@property( geometry_shader_method == my_method_B )
... your method B geometry shader ...
@end
gabbsson
Halfling
Posts: 65
Joined: Wed Aug 08, 2018 9:03 am
x 13

Re: [2.1] Multiple Geometry Shaders (for different primitives)

Post by gabbsson »

Alright! That seems simple enough.

So if I understand correctly; just like with the vertex and pixel shaders I basically create a template geometry shader which (by property) can take different shapes based on the magic number being sent.

Thanks for the answer!
gabbsson
Halfling
Posts: 65
Joined: Wed Aug 08, 2018 9:03 am
x 13

Re: [2.1] Multiple Geometry Shaders (for different primitives)

Post by gabbsson »

Is there no way to read the operation type of the mesh connected to the renderable?
It would be nice to "automate" the magic number part.

Something like:

Code: Select all

void MyHlmsPbs::calculateHashForPreCreate( Renderable *renderable, PiecesMap *inOutPieces )
{
    HlmsPbs::calculateHashForPreCreate( renderable, inOutPieces );
    
   //
   int opType = renderable->getOperationType() // <- This currently does not exist, but you get what i mean
    
    if( opType == OT_LINE_LIST || opType == OT_LINE_STRIP )
    {
        //Geometry Shader will be used. Set which method
        setProperty( "geometry_shader_method", (int32)"line_primitive");
    }
    ...
}
EDIT: Seems like this was a thing in V1, not sure how to do it in 2.1 though.

How i found it could be done in V1 (items dont implement getRenderOperation):

Code: Select all

RenderOperation rOp;
renderable->getRenderOperation(rOp);
OperationType opType = rOp.operationType;
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: [2.1] Multiple Geometry Shaders (for different primitives)

Post by dark_sylinc »

For v1, you found the way.

For v2:

Code: Select all

const VertexArrayObjectArray &vao = queuedRenderable.renderable->getVaos( isShadowPass ? VpNormal : VpShadow );
OperationType opType = vao[0]->getOperationType();
gabbsson
Halfling
Posts: 65
Joined: Wed Aug 08, 2018 9:03 am
x 13

Re: [2.1] Multiple Geometry Shaders (for different primitives)

Post by gabbsson »

Great! I was close but couldn't quite figure it out.

Thanks again, thats perfect!

Now I just have to figure out the problem in my other post.
Post Reply