We get a lot of questions about extending SilverLining’s shaders to incorporate custom lighting or sensor effects, so here’s some more depth on how that works. Usually we point people at our earlier article on the subject, and I’d still start there to get a high-level overview of how it works. Basically, in OpenGL or Vulkan, our shaders have hooks into the UserFunctions and UserFunctions-frag files in Resources/Shaders, which contain functions that allow you to intercept the shading of the clouds and sky at the vertex and fragment levels. SilverLining provides methods that expose the underlying shader programs, so you can attach your own uniforms to them and pass in whatever data is needed by your own effects. Note that you’ll want to use the .glsl versions of the user functions if you are using the legacy OPENGL renderer, and .glsl15 versions if you are using OPENGL32CORE or VULKAN.

This makes a lot more sense with an example, so please refer to this post with a complete example of adding a spotlight effect to the clouds.

What throws people for a loop lately is that there aren’t a lot of developers coding at the OpenGL or Vulkan level these days. Instead, most are using higher-level frameworks such as OpenSceneGraph (OSG) or even higher-level, such as osgEarth. OSG has its own mechanism for managing shaders and shader uniforms, but SilverLining is not written using OSG, so these mechanisms do not apply to SilverLining’s internal shaders. So, accessing the user shaders and adding your own data to them from OSG may not work they way you expect – you need to go lower than OSG, and do this from the OpenGL level (or Vulkan, in the case of frameworks such as VulkanSceneGraph or Rocky.)

It is possible to call OpenGL code within OSG or osgEarth if you include the necessary headers and library. OSG also provides wrappers for many OpenGL functions via the osg::GL2Extensions class. For example:

const osg::GL2Extensions* extensions = osg::GL2Extensions::Get(contextID, true);
extensions->glUseProgram(0);

If you search the osgEarth source code you will find many examples.

osgEarth also has its own mechanism for interacting with SilverLining’s user shaders via osgEarth::NativeProgramAdapter. I don’t know much about how it works however; that would be a question for osgEarth. You can see where it is used here.

We also get questions about how to pass information from the vertex shaders to the fragment shaders; if you’re not familiar with the lower-level workings of OpenGL and GLSL you might not know about that. In short, you can use the “in/out” or “varying” keywords within UserFunctions and UserFunctions-frag to pass data between them, depending on if you are using SilverLining’s OpenGL 3.2+ mode or not. In legacy mode (if you are using the .glsl shaders), you could for example declare

varying vec4 vertPos;

at the top of both UserFunctions.glsl and UserFunctions-frag.glsl.

Then if you set vertPos = position within the overridePosition function in UserFunctions.glsl, you could use that vertPos within UserFunctions-frag.glsl.

If you are using the .glsl15 shaders, the syntax just changes to out vec4 vertPos at the top of UserFunctions.glsl15, and in vec4 vertPos at the top of UserFunctions-frag.glsl15.