Filter shader plugin

../../_images/filtershaderplugin.jpg

Example of a procedural filter shader plugin

With the SDK, you may create new filter shaders types that will extend the base types included in Ocean.

Class header

To make a new filter shader type, you need to subclass IFilterShaderPlugin defined in the filtershaderplugin.h SDK header, and implement a set of pure virtual functions.

The example below shows the declaration of a simple filter shader type named “filter”:

#include <oceansdk/filtershaderplugin.h>
#include <oceansdk/math/vec3.h>

class TestFilterShader : public Ocean::Sdk::IFilterShaderPlugin
{
public:
     TestFilterShader() { }
     ~TestFilterShader() { }

     void getValues(float * dest, const Ocean::Sdk::IShaderContext * sc) const;
     float decay(const Ocean::Sdk::IShaderContext * sc) const;
     float alpha(const Ocean::Sdk::IShaderContext * sc) const;
     float approxAverage() const;
     bool hasAlpha() const;

     const char * typeName() const { return "filter"; }
     const char * prettyTypeName() const { return "SDK Test Filter"; }
     TestFilterShader * clone() const { return new TestFilterShader(*this); }
     void getParameters(Ocean::Sdk::IParamList * iParamList) { }
     bool setParameter(const char * /*parameterName*/, const Ocean::Sdk::IParamValue & /*value*/) { return false; }
     void addChildren(Ocean::Sdk::IAddChildContext * /*iChildAdd*/) { }
     bool prepare(const Ocean::Sdk::IPrepareContext * iPrepare);

private:
     float wlMin;
     float wlMax;
};

Class implementation

prepare function

Documentation : IFilterShaderPlugin::prepare()

In order to compute wavelength dependent values in IFilterShaderPlugin::getValues(), we need to retrieve the simulation wavelength range when initializing the shader. This is done by overloading the function:

bool TestFilterShader::prepare(const Ocean::Sdk::IPrepareContext * iPrepare)
{
     wlMin = iPrepare->wavelengthMin();
     wlMax = iPrepare->wavelengthMax();
}

getValues function

Documentation : IFilterShaderPlugin::getValues()

This is the main shader function, it evaluates and return the filter shader value as an array of floats for a wavelength packet. The shader may use all information passed by the :cpp:any`IShaderContext`, such as wavelength, world position, uv coordinates, etc…

The function is only passed a float * pointer for storing its output, we need to get the number of wavelengths first:

unsigned int n = sc->numWavelengths();

Then, we iterate ver the n wavelength. We get the actual wavelength in micrometers:

float wl = (float)sc->getWavelengthMeters(i, wlMin, wlMax)*1E6f;

Then we get the position along x axis:

float x = sc->worldPos().toVec3<float>()[0];

And we compute a procedural shader value and store it in dest

float s = std::sin(x*30*wl);
dest[i] = s*s;

The full function is shown below:

void TestFilterShader::getValues(float * dest,
                                 const Ocean::Sdk::IShaderContext * sc) const
{
     unsigned int n = sc->numWavelengths();

     for(unsigned int i=0;i<n;i++)
     {
             float wl = (float)sc->getWavelengthMeters(i, wlMin, wlMax)*1E6f;
             float x = sc->worldPos().toVec3<float>()[0];
             float s = std::sin(x*30*wl);
             dest[i] = s*s*0.5f;
     }
}

decay function

Documentation : IFilterShaderPlugin::decay()

This function returns a probability used by Ocean to stop the ray-tracing recursion when light is multiplied by this shader.

For optimal efficiency, it should return the energy conservation factor of the shader at the given shading context. In our case, this value is not trivial and would require integrating the sine-like spectrum over the simulation wavelength range.

This is feasible, but for simplicity we will return here an upper bound of energy factor instead of the exact value. As the values are bound to 0.5, we return this value. This will cause slight oversampling, thus lower noise in the dark regions of the shader, at the cost of lower samples per second (sps) rate:

float TestFilterShader::decay(const Ocean::Sdk::IShaderContext * /*sc*/) const
{
     return 0.5f;
}

alpha function

Documentation : IFilterShaderPlugin::alpha()

This function returns the shader opacity between 0 and 1.

In our case, the shader is fully opaque, so we return 1:

float TestFilterShader::alpha(const Ocean::Sdk::IShaderContext * /*sc*/) const
{
     return 1.0f;
}

hasAlpha function

Documentation : IFilterShaderPlugin::hasAlpha()

This function returns true if the shader may return opacity below 1.

In our case, the shader is fully opaque, so we return false:

bool TestFilterShader::hasAlpha() const
{
     return false;
}

approxAverage function

Documentation : IFilterShaderPlugin::approxAverage()

This function returns average shader value between 0 and 1, for any shading context.

In our case, the average value is 0.25 as the squared sine has an average of 0.5:

float TestFilterShader::approxAverage() const
{
     return 0.25f;
}

Result

The image at the top of this page is the result of using this shader as the albedo of a Lambertian BSDF as defined in the following material definition:

<material type="generic" name="previewmaterial">
     <bsdf type="lambertian" name="bsdf">
             <filtershader type="sdktest/filter" name="diffuse"/>
     </bsdf>
</material>