Table of Contents
In a shader code, new plug may be defined by a magic comment:
/* PLUG: name (param1, param2, ...) */
This defines a point where calls to user functions declared as
PLUG_name
will be inserted. They will be called with given
parameters.
Many effects may use the same PLUG_name
.
Even within a single effect, the same PLUG_name
may be used
many times. All the PLUG_name
functions
will be uniquely renamed to not collide with each other.
The calls will be added in the order they are specified on the
effects
list. More precisely, the most local effects
(at light sources and textures) are called first, then the effects
at shape appearance, and finally the effects inside the grouping nodes.
Although, preferably, for most effects this order will not matter.
For the effects on lights and textures, we first try to find the plug
specific to the given light or texture node.
This means that using the PLUG_light_scale
inside the X3DLightNode.effects
changes only the given light node contribution.
Contrast this with using the same PLUG_light_scale
inside
Appearance.effects
, in which case
the intensity of all the light sources on the given shape can be changed.
A plug is often defined to allow modifying some parameter repeatedly (like adding or modulating the fragment color), so one or more of the parameters are often allowed to be handled as “inout” values.
The same plug name may be defined many times in the source shader.
That is, the magic comment /* PLUG: name ... */
may be
repeated a couple of times, with the same name
.
This means that the final shader may call every matching PLUG_name
function
many times. This is useful when the algorithm is naturally
expressed as a loop, but it had to be unrolled for shader source
(for example, to slightly tweak some loop iterations).
Currently all the plugs must be procedures, that is their result type
must be declared as void
. We have been considering
a possibility of functions, where part of the calculation may be replaced
by a call to a plugged function. While not difficult to implement,
this idea seems unnecessary after many tests.
Procedural plugs are easier to declare, as the call to the plug
may be simply inserted, while in case of function it will have to replace
some previous code. This also means that using a procedural plug
never replaces or removes some existing code, which is a very nice
concept to keep. We want the effects to cooperate with each other,
not to “hijack” from each other some parts of the functionality.
New plugs can be defined inside the Effect
nodes,
as well as inside the complete shaders (like standard
ComposedShader
nodes).
In the first case, the plugs
are only available for the following effects of the same shape.
The advantage of using magic comments to define plugs is that
they can be ignored and a shader source remains valid.
This means that ComposedShader
nodes can define custom plugs
and still work (although with no extra effects) even in X3D browsers
that do not support our extensions.
Suppose we have an effect X that defines a new plug,
by including a magic /* PLUG: ... */
comment.
When this plug is used by another effect Y,
then an appropriate function call is automatically inserted into the generated shader.
In the middle of the source code of effect X,
a function defined in effect Y has to be called. This is the simplest
implementation of our plugs.
Additionally, a forward or external declaration of the called function may need to be inserted into the effect X. That is because Y may be in a separate compilation unit (in case of GLSL), or just defined lower in the code. In simple cases, such forward or external declarations can be inserted right at the beginning of effect X code.
Some shading language directives are required to be placed before
all normal declarations. For example, in case of the OpenGL shading language,
the #version
as well as some #extension
directives
must occur at the beginning of the shader code.
To handle such cases, another magic comment /* PLUG-DECLARATIONS */
is available.
If present, it signifies a place where forward or external declarations
should be inserted.