All tests are also listed below, roughly in the order basic -> advanced.
Click on the image to view reference rendering
(matches view3dscene from snapshots result, as our view3dscene implements all proposed specification
fixes). Download X3D in classic or XML version and open with X3D browser of your
choice. Files in X3D classic (VRML) encoding contain many comments,
read them to know what the test is about!

FreeWRL: Incorrect, various problems. It seems FreeWRL doesn't honor the multi-texture transformation properly, it also makes warnings "not enough textures in MultiTextureTransform...." instead of following the spec that says when identity matrices are assumed for transformation. Possibly caused by spec problem 6. below.

Test MultiTexture together with IndexedQuadSet from CAD component.
Very similar to transform_and_coordinates_faces.x3dv
(in fact the result should look exactly the same) but now uses
IndexedQuadSet instead of IndexedFaceSet.

Test results:

FreeWRL: FreeWRL doesn't support CADGeometry component. Results are incorrect (you see nothing), but that's somewhat acceptable since the console warns that CADGeometry level support is 0 (none) in FreeWRL.

Instant Player (Screen) Incorrect. Result equal to 6. That's good, this means that CADGeometry quads correctly work with multi-texturing. But, since results of test 6. were not correct, results for test 7. show exactly the same problems.

Octaga Player: (Screen) Incorrect. Equal to 6. Which is good, it means CADGeometry quads work with multi-texturing. But, since result 6. was incorrect, this is incorrect too.

FreeWRL: Incorrect. FreeWRL doesn't seem to support MovieTexture (although it doesn't complain when we request Texturing component at level 3, so it should support MovieTexture). Also makes warnings "not enough textures in MultiTextureTransform....", so probably would also exhibit problems from test 6.

BS Contact (Screen) Incorrect. MovieTexture support is weird (movie seems played in a separate window instead of as a texture). Also, transformation of squirrel texture is wrong.

Instant Player (Screen) Incorrect. MovieTexture does not seem supported, at least for multi-texturing.

The reference of this test (and view3dscene result) follows our
proposition to always modulate by default. This contradicts
the specification, although we argue (see link above) that in this case
the specification 1. proposes a behavior that is not very useful and
2. is already implemented inconsistently.

Instant Player (Screen) 2.1.0: Equal to BS Contact result for this test, which means incorrect (but at least, this time, consistent with BS Contact). 2.2.0: it seems it changed to be better (but still not exactly spec-complaing): InstantPlayer 2.2.0 doesn't mix texture color with Material.diffuseColor for RGB textures (correct) and does mix with grayscale textures (correct). However, it always mixes texture color with Color (for both RGB (incorrect) and grayscale (correct) textures).

One more test of MultiTexture with separate modes and sources for RGB/alpha.
Similar to "subtract" column of modes_and_sources,
but showing what happens when we subtract only RGB.

Test results:

Testing this is not fair. See previous test for more comments.

License: For the widest possible use, consider these files
public domain, you're welcome to copy them to other examples
repositories etc. Yes, the sample textures/movies inside are in public
domain too (see data/AUTHORS.txt inside for details).

How these files were created: All the X3D test files were written
manually in X3D classic encoding. XML encoding versions were automatically
generated from classic encoding by tovrmlx3d (a tool
distributed with view3dscene).
The reference images were also generated by view3dscene
(using --screenshot option to make screenshots in batch mode).

The mode field may contain an additional blending mode
for the alpha channel. This is the most troublesome
sentence of the MultiTexture specification. It contradicts most of the remaining
specification for MultiTexture node. Other spec parts clearly
suggest that exactly one mode string corresponds to one texture unit,
for example 1. it's mentioned explicitly that
if the mode.length is less than texture.length,
remaining modes should be assumed as "modulate" 2. many modes
are clearly used over both RGB and Alpha channels, and they
specify results for both RGB and Alpha channels.

This means that the meaning of mode=["MODULATE","REPLACE"]
is not clear.

What did the authors meant by the word may
in the sentence "may contain an additional blending mode"?

Expecting two mode strings for one texture unit
clearly contradicts the spec.

Expecting a single mode string for one texture unit means that
no mode specific for alpha channel is available.

Smart detection when to expect the next mode to be
for alpha channel (for example expect the additional mode for alpha channel
only when texture image has alpha channel) is also a bad idea.
First, because the specification
says absolutely nothing about it.
Second, because operating on alpha channel
makes sense even if the image in the current texture unit
doesn't have alpha channel (because alpha may come from previous
texture unit, or from a constant).

Also, some modes are clearly not possible (or sensible) for
the alpha channel alone. For example, it doesn't make much sense to apply
modes like DOTPRODUCT3 or BLEND* only to
the alpha channel.

Proposed clarification: a single string inside mode field
always corresponds to exactly one texture unit.
This string may be a simple name of the mode (like "MODULATE"),
in which case it describes behavior for both RGB and alpha channel.
This string may also contain two mode names,
separated by a comma or slash (like "MODULATE / REPLACE"),
in which case a separate behavior is specified for RGB channels and
for alpha channel.

In Table 18.3 - Multitexture modes, "REPLACE" mode
is specified as "Arg2", which makes no sense. Arg2 comes
by default from previous unit (or material color),
this is implicated by the sentence "The source field
determines the colour source for the second argument".
So mode "REPLACE" interpreted as "Arg2"
would then 1. completely ignore current
texture unit 2. contradict the normal meaning of "REPLACE",
which is explicitly mentioned in specification at paragraph
before this table ("REPLACE for unlit appearance").
An example with alpha (although ambiguous on it's own,
more about this in previous point) clearly shows that
"REPLACE" takes from 1st argument.

To make it absolutely clear, it would also help if the spec
would clearly say something along
the lines "Arg1 is the current texture unit, Arg2 is what is determined
by the source field (by default, it's previous texture unit (or material color
for 1st texture unit))". This would also make it clear what is the
order of calculation for texture units
(and would clarify that Octaga "reversed order"
is incorrect — everyone else does it correctly).

The meaning of ADDSIGNED and ADDSIGNED2X modes is not clear.
Spec doesn't give the exact equation, and from the wording description
it's not clear whether the -0.5 bias is applied to the sum
(Arg1 + Arg2 - 0.5),
or each component
(Arg1 - 0.5 + Arg2 - 0.5 = Arg1 + Arg2 - 1.0).
The first interpretation seems more reasonable,
and it follows OpenGL GL_ADD_SIGNED behavior.

Neither interpretation results in the output
range of values in -0.5 ... 0.5.
The claim making the effective range of values from −0.5 through 0.5
(at the ADDSIGNED value in table 18.3) doesn't seem to make
any sense, regardless how you try to interpret it.

Proposed clarification: I interpret it
as "-0.5 bias is added to the sum",
this follows OpenGL GL_ADD_SIGNED constant, so I guess this
was the intention of the spec.

Some modes say explicitly what happens with
alpha channel, but some do not. This is especially troublesome
in case of the "subtract" mode, that will subtract alphas making resulting
alpha = 0 (invisible) for the most common situation when both textures
have alpha = 1 (opaque).

Proposed clarification: See point 1.
If you specify a simple mode name,
then it applies to both RGB and alpha channels.
Comparing with Octaga, our results
for "subtract" are equal this way: with default alphas = 1,
result gets alpha = 0.

This interpretation is consistent.
In most cases, it also matches "what the author expects".
The one exception is the "subtract" operation,
when you usually do not want to subtract alphas —
authors should just remember that usually
they want subtract only RGB, using mode like
"SUBTRACT / MODULATE".

The table in section above
(Precise and corrected MultiTexture.mode specification)
makes it clear how to use each mode for only RGB, or only alpha, or both.

It's not specified what channels are inverted by
the function="COMPLEMENT" value. Well, obviously RGB are inverted,
but is alpha channel inverted too?

Tests show that view3dscene, Instant Player, BS Contact do it on RGB
(not alpha).
Octaga does it on RGBA (it negates alpha channel too).
Other tested browsers do not support this operation.

Proposed clarification:function="COMPLEMENT"
works only on RGB, does not touch alpha channel.
This seems more suitable for usual cases, and follows the majority
of implementations.

The paragraphs for MultiTextureTransform
(texture coordinates for channel 0 are replicated...)
and MultiTextureCoordinate
(identity matrices are assumed...) should be swapped in
the spec.

The definition of source="DIFFUSE"
and source="SPECULAR" doesn't play nicely with lighting.

Reading the definitions of
source="DIFFUSE"
and source="SPECULAR"
it would seem that X3D specification
forces the Gouraud shading (calculate lighting at each vertex,
not pixel). Which is unacceptable, and I'm absolutely sure that all X3D
browsers ignore it. Most browsers, including ours,
allow to choose Gouraud shading or Phong shading.
The default shading depends on hardware capabilities, user settings,
and maybe other X3D features (like our extensions to
force Phong shading or use bump mapping).
In any case, the shading definitely should not depend on whether
we use multi-texturing or not.

Also, reading their descriptions it would seem that texture is applied
after performing lighting calculation. Which contradicts
the lighting equations in
X3D spec "17.2.2.4 Lighting equations",
that clearly say that textures affect the diffuse color which is then
used for lighting.

Proposed solution:

At the very least, just change description of these source values
to not talk about Gouraud shading.
Just say for source="DIFFUSE",
"The texture argument is the interpolated material diffuse color.".
And analogously for specular.
Do not talk about Gouraud shading here, because
1. you do not want to force Gouraud shading and
2. according to X3D lighting spec, the texture color calculation
should happen before the shading.

Moreover, speaking about diffuse or specular colors
at this point doesn't really make much sense.
According to
lighting equations from X3D spec "17.2.2.4 Lighting equations",
the texture color only changes (replaces or modulates) the material diffuse
color, which is then used inside lighting equations.

This means that source="SPECULAR" doesn't make much
practical sense. Why use the specular color inside diffuse factor
calculation?

It would be best to remove source="DIFFUSE"
and source="SPECULAR" and add source="MATERIAL",
with the meaning This is the Material diffuse color
(eventually replaced with the Color or ColorRGBA
node). The result of multi-texturing is the calculated diffuse color
used as Irgb and A parameters inside
lighting equations. This color is then used for subsequent
lighting calculations (is multiplied by diffuse factor,
summed with specular, multiplied by light color and summed for all lights,
and so on)."

In all of this, there is also a recurring problem
of X3D lighting specification.

The implementations that use Gouraud shading (for example,
OpenGL fixed-function implementations) do not really
implement the X3D lighting equations.
It's not possible, really.
Textures have to be mixed after lighting calculation
in case of Gouraud shading.
Which means that we have to calculate non-textured source color,
with lighting (with all diffuse and specular and all lights already summed),
and only then it can be used for textures.

This is actually a problem of X3D lighting specification,
for which we have no simple solution (it would require changing
the lighting equations). Anyway, it makes the
source="MATERIAL" sound more sensible than
source="DIFFUSE" and source="SPECULAR":
in cases of these implementations, the source must already
include both diffuse and specular.

Our current implementation: Currently our implementation
always mixes the textures after lighting calculation.
Just like described above for Gouraud shading.
We do it also in case of Phong shading for now (for consistency),
although the Phong shading may be fixed one day.

We treat both source="DIFFUSE"
and source="SPECULAR" as equal, and actually they just
mean "the result of lighting equations (for non-textured appearance)".

The default mode is always modulate, for both RGB and grayscale textures.
This is inconsistent with single-texturing (using normal
ImageTexture instead of MultiImageTexture),
when the default mode is to modulate for grayscale textures,
but replace for RGB textures. This means that you cannot blindly
change ImageTexture node into a MultiImageTexture node
(with a single ImageTexture inside): because the default mode
(possibly) changed.

Proposed solution: In this case, I propose to change the
specification parts related to single-texturing (ImageTexture),
and leave existing multi-texturing spec unchanged.
That is, always modulate by default (regardless if texture
is RGB or grayscale).

It would be useful to clarify what happens with grayscale texture
images and images without alpha channel. Following the GPU behaviors
(and common sense), we propose to add such statement to X3D specification:

For the purpose of multitexturing calculations,

Grayscale texture is equivalent to an RGB texture
with all color components (red, green, blue) equal.

Texture without an alpha channel is equivalent to a texture with
alpha channel filled with value 1.0 (completely opaque).

3. Proposed improved MultiTexture.mode specification

To allow different texture modes for RGB and for alpha channel,
you should just write two mode names inside one string, and separate
them by a comma or slash (additional whitespace around is allowed).
For example, mode [ "MODULATE / REPLACE" ]
means that on the 1st texture unit, RGB is modulated and alpha is replaced.
Contrast this with mode [ "MODULATE" "REPLACE" ], that means
to modulate (both RGB and alpha) on the 1st texture unit,
and then to replace (both RGB and alpha) on the 2nd texture unit.

This way we keep the interpretation that "one string on the mode field
always describes full behavior of exactly one texture unit".
Of course, some modes are not available for alpha channel (these are
the OpenGL constraints).

Table below describes precise behavior and disallowed
situations for all mode names. Treat this as a corrected and precise version
of the similar table in X3D spec of MultiTexture
(see text down for details where and why it's corrected,
short version: specification is simply poor and inconsistent).
In table below,

Arg1 is the current texture unit,

Arg2 is determined by the source field. By default,
it's the result of previous texture stage, or (for the 1st stage)
it's interpolated material*lighting.

4. Proposed MultiTexture.source extension

In the same spirit, you can specify separate sources for RGB and alpha
channels, just separate them by comma or slash within a single string.
For example, source string "DIFFUSE / FACTOR" says to take diffuse
color as a source for Arg2.RGB and constant factor (MultiTexture.alpha field)
for Arg2.Alpha.

Note that the empty string is also a source name (it means to take
color from previous texture stage). So source string like "/ FACTOR"
is also Ok (takes RGB from previous stage, and alpha from constant factor),
and "FACTOR /" is Ok (takes RGB from constant factor
MultiTexture.color, and alpha from previous stage).

An example: suppose you have two textures that you want to subtract
on RGB (tex2 - tex1) channel, and you want to set resulting alpha channel
to 1.0 (regardless of any texture value). This will work:

VRML 2 / X3D specifications
say that RGB textures should by default REPLACE the material color (as opposed
to MODULATE).
This is when no multi-texturing is used.
This is said by the specification at tables
"Table 17.2 — Unlit colour and alpha mapping" and
"Table 17.3 — Lit colour and alpha mapping": note that RGB and RGBA texture
colors are not multiplied by color from Material.diffuseColor
or Color nodes.
Also spec about Color nodes (11.4.2 Color, 11.4.3 ColorRGBA) says explicitly
that "RGB or RGBA textures take precedence over colours; specifying both
an RGB or RGBA texture and a Color* node for geometric shape will
result in the Color* node being ignored.".

Problems with the specification text:

It makes Material.diffuseColor and Color
useless with RGB textures, which is a shame. GPUs do not have such limitations.

It is inconsistent with MultiTexture behavior,
when the modulate mode is the default — regardless if we have
RGB or grayscale texture.

In case of our current implementation,
the texture color is mixed with the whole resulting lighting calculation.
Using the "replace" mode by default would mean that shapes are unlit
when you use RGB textures.

A separate problem is that browsers are already
inconsistent in the implementation of this rule, see test results.
That's understandable, because the spec behavior is a little useless.