The X3D specification has unfortunately
some problems related to it's multi-texturing nodes,
mostly MultiTexture
node.
I have documented these problems below, along with the tests on various
X3D browsers, and with the proposed solutions.
Contents:
See my wiki page "Deprecate some unused and badly specified MultiTexturing specification pieces" about future multi-texturing changes I would like to see in X3D.
In short — I think we should just deprecate (a subset of) MultiTexture
node. It does not have much practical usage, it does not have much testcases, there seems to be little desire in Web3D community to fix it — likely because not many people use it. Deprecating/removing some pieces of X3D spec around MultiTexture
node seems like the most efficient thing to do.
X3D multi-texturing tests are available inside the Castle Game Engine "Demo Models" repository. It's easiest to just download whole demo-models repo to your disk, and then explore multi_texturing
subdirectory. All tests are in X3D classic and XML encodings (equivalent), and with reference screenshots.
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!
Test various values for MultiTexture.mode
and MultiTexture.source
.
Test results:
Test of MultiTexture
special modes "BLENDxxx"
.
Test results:
Test of multitexturing MODULATE
and ADD
modes used together.
Shows that A * B + C <> A * C + B
(compare 3rd and 4th box).
Note that whether the 1st and 2nd cube should be yellowish or not is a separate question, related to the default mode when using single-texturing (see below on this page, and test 9). For this test, we accepted the test as "Ok" regardless if the 1st and 2nd cubes are yellowish or not. Only 3rd and 4th cubes of this test were taken into account when judging if test passed/failed.
Test results:
Test MultiTexture
on primitives (Box
, Sphere
, Cone
, Cylinder
).
Test results:
Test MultiTexture.function
.
Test results:
Test various MultiTextureTransform
and MultiTextureCoordinate
values.
Test results:
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:
Test ImageTexture
and MovieTexture
mixing using MultiTexture
.
Test results:
This is not a MultiTexture
test, but it tests a feature related
to some multi-texturing problems: how various X3D browsers mix (single)
texture with Material.diffuseColor
and Color
node.
See lower on this page for details
why this is tested.
The reference of this test (and view3dscene result) follows X3D 4.0 and our idea to always modulate by default. This was different in X3D 3 specification. See Make RGB and grayscale textures treatment consistent.
Test results:
Version: 1.22.13: Seems to never mix texture color with Material.diffuseColor (for both RGB (correct) and grayscale (incorrect) textures), and always mixes texture color with Color node (for both RGB (incorrect) and grayscale (correct) textures).
Version: 4.0.0: RGB texture is multiplied with Material.diffuseColor (incorrect, although this is how we propose to change X3D spec), grayscale texture is multiplied with Material.diffuseColor (correct), RGB texture overrides Color node (correct) and grayscale texture multiplies Color node (correct).
Version: 8.101: RGB texture replaces Material.diffiseColor (correct). Grayscale texture is replaced by Material.diffuseColor (incorrect and weird). RGB texture modulates with Color node (incorrect according to spec). Grayscale texture modulates Color node (correct).
Version: 8.300: RGB texture overrides Material.diffuseColor (correct), grayscale texture is multiplied with Material.diffuseColor (correct), RGB texture multiplies Color node (incorrect) and grayscale texture multiplies Color node (correct).
Version: 2.1.0: Equal to BS Contact 8.101 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).
Version: 2.8.0: Equal to BS Contact 8.300 result for this test (so almost correct, only RGB texture incorrectly multiplies Color node).
Version: 4.0.3: RGB texture overrides Material.diffuseColor (correct). Grayscale texture is overridden by Material.diffuseColor (incorrect and weird, seems to match BS Contact 8.101). RGB texture overrides Color node (correct). Grayscale texture modules with Color node (correct).
Version: 5.0.0: All correct! Congrats, this is the only browser that handles all 4 cases following the specification: RGB texture overrides Material.diffuseColor (correct), grayscale texture is multiplied with Material.diffuseColor (correct), RGB texture overrides Color node (correct) and grayscale texture multiplies Color node (correct).
Version 4.1.4-200: In all cases, texture color is multiplied by Material.diffuseColor or Color. This contradicts the X3D specification, but matches view3dscene, and is exactly as we propose to require in future X3D versions.
Version 1.7.2: In all cases, texture color overrides Material.diffuseColor or Color, even when the texture is grayscale. This is incorrect.
Test MultiTexture
with separate modes and sources for RGB/alpha,
see below for our proposal to allow separate RGB/alpha specification
for modes and sources (problem 1.), and proposed extended MultiTexture.mode table.
Test results:
Testing this is not fair. All VRML/X3D browsers except view3dscene fail on this, because this tests a proposed (not yet part of X3D spec) extension to specify separate modes and sources for RGB/Alpha (and clear some of the confusion around modes spec along the way). See lower on this page about proposed separate MultiTexture.mode and lower on this page about proposed separate MultiTexture.source
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.
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).
All the X3D test files were written
manually in X3D classic encoding. XML encoding versions were automatically
generated from classic encoding by Castle Model Converter.
The reference images were generated by
Castle Model Viewer
(using --screenshot
option to make screenshots in batch mode).
Unless otherwise specified above, we tested on:
Screenshots obtained from BS Contact, Instant Player, Octaga Player are available as a zip file or or just browse/clone the GIT repository. Many, many thanks to Cecile Muller for testing!
X3D specification about multi-texturing has a couple of problems. Below is a list of spotted problems, and an explanation how we handle it in our engine (Castle Game Engine and view3dscene) and how we propose to fix X3D specification. You probably want to read this along with MultiTexture specification in X3D 3.2 (or MultiTexture specification in X3D 3.3, there weren't any important changes since X3D 3.2).
Please report any comments, preferably to x3d-public mailing list.
Specification problems and our solutions:
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"?
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.
The table in section Proposed improved MultiTexture.mode specification contains the exact equations for all the modes, when used for both RGB and alpha or when used for only RGB or only alpha.
Relevant section of latest X3D specification: MultiTexture.
Status: not fixed.
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.
Proposed clarification: "REPLACE" copies the "Arg1" (that is, current texture unit values). IOW, it's equivalent to "SELECTARG1".
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).
Relevant section of latest X3D specification: MultiTexture.
Status: not fixed.
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.
Relevant section of latest X3D specification: MultiTexture.
Status: not fixed. My current opinion: fix this by just deprecating ADDSIGNED, ADDSIGNED2X in X3D > 4.
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.
Relevant section of latest X3D specification: MultiTexture.
Status: not fixed.
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.
Relevant section of latest X3D specification: MultiTexture.
Status: not fixed. My current opinion: fix this by just deprecating function="COMPLEMENT" in X3D > 4.
The paragraphs for MultiTextureTransform
(texture coordinates for channel 0 are replicated...)
and MultiTextureCoordinate
(identity matrices are assumed...) should be swapped in
the spec.
Relevant section of latest X3D specification: MultiTextureTransform.
Status: fixed in X3D 4.0.
MODULATEINVCOLOR_ADDALPHA
refers
to non-existing mode
MODULATECOLOR_ADDALPHA
(that doesn't invert the color).
Relevant section of latest X3D specification: MultiTexture.
Status: not fixed.
Specification has 2 source
values that refer to Phong lighting model ("DIFFUSE", "SPECULAR") and Gouraud shading:
"DIFFUSE" | The texture argument is the diffuse color interpolated from vertex components during Gouraud shading. "SPECULAR" | The texture argument is the specular color interpolated from vertex components during Gouraud shading.
But
Browsers don't have to do Gouraud shading (Most browsers now allow both Phong and Gouraud shading).
In X3D 4 we have new lighting models (physical, unlit) that don't even have diffuse/specular factors.
Relevant section of latest X3D specification: MultiTexture.
Status: not fixed. My current opinion: fix this by just deprecating source="DIFFUSE/SPECULAR" in X3D > 4.
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).
See RGB texture color by default modulates material color for a more detailed description of this problem. Existing browsers already disagree on this. Changing the spec to say "we always modulate by default" would greatly simplify the situation.
Status: fixed in X3D 4.0.
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,
Status: fixed in X3D 4.0.
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,
Mode name | Behavior when used alone (like "REPLACE") |
Behavior when used for only RGB channel (like "REPLACE / ...") |
Behavior when used for only alpha channel (like "... / REPLACE") |
---|---|---|---|
MODULATE | Output.RGBA := Arg1.RGBA * Arg2.RGBA | Output.RGB := Arg1.RGB * Arg2.RGB | Output.A := Arg1.A * Arg2.A |
MODULATE2X | Output.RGBA := Arg1.RGBA * Arg2.RGBA * 2 | Output.RGB := Arg1.RGB * Arg2.RGB * 2 | Output.A := Arg1.A * Arg2.A * 2 |
MODULATE4X | Output.RGBA := Arg1.RGBA * Arg2.RGBA * 4 | Output.RGB := Arg1.RGB * Arg2.RGB * 4 | Output.A := Arg1.A * Arg2.A * 4 |
REPLACE or SELECTARG1 | Output.RGBA := Arg1.RGBA | Output.RGB := Arg1.RGB | Output.A := Arg1.A |
SELECTARG2 | Output.RGBA := Arg2.RGBA | Output.RGB := Arg2.RGB | Output.A := Arg2.A |
ADD | Output.RGBA := Arg1.RGBA + Arg2.RGBA | Output.RGB := Arg1.RGB + Arg2.RGB | Output.A := Arg1.A + Arg2.A |
ADDSIGNED | Output.RGBA := Arg1.RGBA + Arg2.RGBA - 0.5 | Output.RGB := Arg1.RGB + Arg2.RGB - 0.5 | Output.A := Arg1.A + Arg2.A - 0.5 |
ADDSIGNED2X | Output.RGBA := (Arg1.RGBA + Arg2.RGBA - 0.5) * 2 | Output.RGB := (Arg1.RGB + Arg2.RGB - 0.5) * 2 | Output.A := (Arg1.A + Arg2.A - 0.5) * 2 |
SUBTRACT | Output.RGBA := Arg1.RGBA - Arg2.RGBA | Output.RGB := Arg1.RGB - Arg2.RGB | Output.A := Arg1.A - Arg2.A |
OFF | Texture stage is simply turned off. | Not allowed. | |
DOTPRODUCT3 | NewArg1.RGB := (Arg1.RGB - 0.5) * 2; NewArg2.RGB := (Arg2.RGB - 0.5) * 2; Output.RGBA := dot(NewArg1.RGB, NewArg2.RGB) |
... (calculate NewArg* same as on the left)... Output.RGB := dot(NewArg1.RGB, NewArg2.RGB) |
Not allowed. |
BLENDDIFFUSEALPHA | Output.RGBA := Arg1 * PRIMARY_COLOR.Alpha + Arg2 * (1 - PRIMARY_COLOR.Alpha) |
Output.RGB := Arg1.RGB * PRIMARY_COLOR.Alpha + Arg2.RGB * (1 - PRIMARY_COLOR.Alpha) |
Not allowed. |
BLENDTEXTUREALPHA | Output.RGBA := Arg1 * Arg1.A + Arg2 * (1 - Arg1.A) |
Output.RGB := Arg1.RGB * Arg1.A + Arg2.RGB * (1 - Arg1.A) |
Not allowed. |
BLENDFACTORALPHA | Output.RGBA := Arg1 * MULTI_TEXTURE_CONSTANT.Alpha + Arg2 * (1 - MULTI_TEXTURE_CONSTANT.Alpha) |
Output.RGB := Arg1.RGB * MULTI_TEXTURE_CONSTANT.Alpha + Arg2.RGB * (1 - MULTI_TEXTURE_CONSTANT.Alpha) |
Not allowed. |
BLENDCURRENTALPHA | Output.RGBA := Arg1 * PREVIOUS_STAGE.Alpha + Arg2 * (1 - PREVIOUS_STAGE.Alpha) |
Output.RGB := Arg1.RGB * PREVIOUS_STAGE.Alpha + Arg2.RGB * (1 - PREVIOUS_STAGE.Alpha) |
Not allowed. |
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:
MultiTexture { texture [ ImageTexture { url "tex1.png" } ImageTexture { url "tex2.png" } ] mode [ "REPLACE" "SUBTRACT / SELECTARG2" ] source [ "" " / FACTOR" ] alpha 1.0 } # Calculations on texture unit 1: # Stage1Output.RGBA := Tex1.RGBA; # Calculations on texture unit 2: # Output.RGB := Tex2.RGB - Stage1Output.RGB; # Output.A := Arg2.A := 1.0;
This section documents a problem related to single-texturing behavior, that is connected with some multi-texturing troubles. Note that (at the time of X3D 3) this was the one and only place where our engine deliberately did something different than X3D specification, because we felt that the X3D specification behavior is really not useful. In our engine, the texture color is by default multiplied by the material color. This issue is fixed in X3D 4.0, where I introduced prose/equations that match my recommended behavior (and match what CGE is doing).
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:
Material.diffuseColor
and Color
useless with RGB textures, which is a shame. GPUs do not have such limitations.
MultiTexture
behavior,
when the modulate mode is the default — regardless if we have
RGB or grayscale texture.
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.
That's why we propose to change the specification:
simply always MODULATE
(component-wise multiply on RGBA channels).
In other words, treat a grayscale texture exactly like an RGB texture
with all color components (red, green, blue) equal.
Our engine and view3dscene already implement this behavior.
Copyright Michalis Kamburelis and Castle Game Engine Contributors.
This webpage is also open-source and we welcome pull requests to improve it.
We use cookies for analytics. See our privacy policy.