Normal shader plugin

../../_images/normalshaderplugin.jpg

Example of a procedural normal shader plugin

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

Class header

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

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

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

class TestNormalShader : public Ocean::Sdk::INormalShaderPlugin
{
public:
     TestNormalShader();
     ~TestNormalShader();

     Vec3<float> valueTS(const Ocean::Sdk::IShaderContext * sc) const;
     Vec3<float> shadeWS(const Ocean::Sdk::IShaderContext * sc,
                         const Vec3<float> & normalWS) const;

     const char * typeName() const { return "normal"; }
     const char * prettyTypeName() const { return "SDK Test Normal"; }
     TestNormalShader * clone() const { return new TestNormalShader(*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*/) { }
};

Class implementation

valueTS function

Documentation : INormalShaderPlugin::valueTS()

In our example, the only non-trivial function we need to implement is IScalarShaderPlugin::value()

This is the main shader function, it evaluates and return the scalar shader value as a float. The shader may use all information passed by the IShaderContext, such as world position, uv coordinates, etc, except the wavelength information.

Warning

A normal shader cannot be wavelength dependent!

We start by getting the U coordinate (from UV mapping coordinates):

float u = sc->uvs()[0];

Then we compute slopes in UV space:

Vec2<float> uvDiff(std::sin(u*400)*0.05f, 0.0f);

Then we compute these slopes in tangent space:

Vec2<float> tsDiff = sc->bumpNormalShiftTS(uvDiff);

And we return the normalized shaded normal in tangent space:

return Vec3<float>(tsDiff[0],
                   tsDiff[1],
                   std::sqrt(1.0f-tsDiff.squareLength()));

The full function is shown below:

Vec3<float> TestNormalShader::valueTS(const Ocean::Sdk::IShaderContext * sc) const
{
     float u = sc->uvs()[0];
     Vec2<float> uvDiff(std::sin(u*400)*0.05f, 0.0f);
     Vec2<float> tsDiff = sc->bumpNormalShiftTS(uvDiff);
     return Vec3<float>(tsDiff[0],
                     tsDiff[1],
                     std::sqrt(1.0f-tsDiff.squareLength()));
}

valueTS function

Documentation : INormalShaderPlugin::shadeWS()

This function allows another layer of normal perturbation, this time in world space.

We will not use it and return the unmodified normal:

Vec3<float> TestNormalShader::shadeWS(const Ocean::Sdk::IShaderContext * /*sc*/,
                                      const Vec3<float> & normalWS) const
{
     return normalWS;
}

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">
     <normalshader type="sdktest/normal" name="bump"/>
     <bsdf type="glossy" name="bsdf" ior="1.7">
             <filtershader type="constant" name="diffuse" value="0.18"/>
             <roughness type="flat" name="roughness"/>
     </bsdf>
</material>