X3D MultiTexturing problems and proposed solutions

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:

1. Future: where do we go with MultiTexture in X3D > 4.0

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.

2. Tests results

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!

2.1. Modes and sources

modes_and_sources

Test various values for MultiTexture.mode and MultiTexture.source.

Test results:

  • FreeWRL: Incorrect. MultiTexture.source seems ignored by FreeWRL. MultiTexture.mode = SUBTRACT is weird (not sure what is does). MultiTexture.mode = SELECTARG2 seems equal to MultiTexture.mode = SELECTARG1. Other MultiTexture.mode values seem Ok.
  • BS Contact (Screen) Incorrect MultiTexture.mode = SUBTRACT column (all the other columns are correct, so BS Contact is closest to reference/view3dscene). Looks like BS Contact subtracts only RGB channel, does not touch alpha. This contradicts our proposed clarifications. The X3D spec is ambigous about this, see problem 4.
  • Instant Player (Screen) Incorrect, many problems. MultiTexture.source does something weird (not sure why it changes the result like that). Various MultiTexture.mode values incorrectly handled. Note MultiTexture.mode = SUBTRACT is correct (subtract alpha 1-1 makes invisible surface).
  • Octaga Player: (Screen) Incorrect, many problems.

2.2. Modes blend

modes_blend

Test of MultiTexture special modes "BLENDxxx".

Test results:

  • FreeWRL: All incorrect. Not sure how FreeWRL interprets MultiTexture.mode = BLENDxxx, it doesn't seem to follow spec or match reference images.
  • BS Contact (Screen) Ok.
  • Instant Player (Screen) All incorrect.
  • Octaga Player: (Screen) Incorrect 2nd, 3rd and 4th quads. So only BLENDDIFFUSEALPHA is correct.

2.3. Modes modulate add order

modes_modulate_add_order

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:

  • FreeWRL: Ok.
  • BS Contact (Screen) Possibly incorrect? 3rd cube should look a little different than 4th, but it seems exactly the same (unless it is just a lighting playing tricks).
  • Instant Player (Screen) Incorrect (3rd and 4th cube). Not really sure what is happening there, why the effet is like it is.
  • Octaga Player: (Screen) Ok.

2.4. Primitives

primitives

Test MultiTexture on primitives (Box, Sphere, Cone, Cylinder).

Test results:

  • FreeWRL: Ok.
  • BS Contact (Screen) Incorrect. Texture transformation is not applied, but at least two textures are mixed Ok.
  • Instant Player (Screen) Incorrect. Multi-texturing is not used (squirrel textue is not mixed with brick texture). But texture transformation for squirrel is applied.
  • Octaga Player: (Screen) Ok.

2.5. Functions

functions

Test MultiTexture.function.

Test results:

  • FreeWRL: All incorrect. It seems FreeWRL doesn't handle MultiTexture.function.
  • BS Contact (Screen) Incorrect: 2nd and 3rd cube invalid. It looks like BS Contact supports MultiTexture.function (COMPLEMENT and ALPHAREPLICATE), but the COMPLEMENT support is buggy.
  • Instant Player (Screen) Incorrect 2nd and 3rd, it doesn't seem to apply MultiTexture.function at the right place. This is actually similar to BS Contact, but definitely contradicts the spec.
  • Octaga Player: (Screen) Incorrect. Looks like COMPLEMENT negates the alpha as well, which may be caused by specification problem 5.

2.6. Transform and coordinates faces

transform_and_coordinates_faces

Test various MultiTextureTransform and MultiTextureCoordinate values.

Test results:

  • 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.
  • BS Contact (Screen) Incorrect, various problems (but *different* than e.g. FreeWRL problems).
  • Instant Player (Screen) Incorrect, various problems, but *different* than BS Contact and FreeWRL.
  • Octaga Player: (Screen) Incorrect, various problems.

2.7. Transform and coordinates quads

transform_and_coordinates_quads

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.
  • BS Contact (Screen) BS Contact doesn't seem to support CADGeometry component.
  • 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.

2.8. Image with movie multi texture

image_with_movie_multi_texture

Test ImageTexture and MovieTexture mixing using MultiTexture.

Test results:

  • 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.
  • Octaga Player: (Screen) Incorrect. MovieTexture not supported? At least for multi-texturing.

2.9. Material color mixed with texture color

material_color_mixed_with_texture_color

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:

  • FreeWRL:

    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).

  • BS Contact (Screen)

    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).

  • Instant Player (Screen)

    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).

  • Octaga Player: (Screen)

    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).

  • X_ITE:

    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.

  • X3DOM:

    Version 1.7.2: In all cases, texture color overrides Material.diffuseColor or Color, even when the texture is grayscale. This is incorrect.

2.10. Subtract and force alpha

subtract_and_force_alpha

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

2.11. Subtract rgb various sources

subtract_rgb_various_sources

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.

3. About tests

3.1. 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).

3.2. 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 Castle Model Converter. The reference images were generated by Castle Model Viewer (using --screenshot option to make screenshots in batch mode).

3.3. Details about browsers tested

Unless otherwise specified above, we tested on:

  • FreeWRL 1.22.13 (on Debian testing 32-bit, NVidia GeForce GPU)
  • BS Contact 8.101
  • Instant Player 2.1.0
  • Octaga Player 4.0.3
  • view3dscene (version right before 3.13.0) (on Debian testing 32-bit, NVidia GeForce GPU). view3dscene is Michalis' own browser, so the implementation 100% matches the reference images and all proposed clarifications/solutions mentioned on this page.

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!

4. Problems and proposed solutions

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:

  1. 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.

    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.

  2. 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.

  3. 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.

  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.

  5. 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.

  6. 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.

  7. 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.

  8. 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

    1. Browsers don't have to do Gouraud shading (Most browsers now allow both Phong and Gouraud shading).

    2. 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.

  9. 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.

  10. 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,

    1. Grayscale texture is equivalent to an RGB texture with all color components (red, green, blue) equal.
    2. Texture without an alpha channel is equivalent to a texture with alpha channel filled with value 1.0 (completely opaque).

    Status: fixed in X3D 4.0.

5. 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,

  1. Arg1 is the current texture unit,
  2. 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.
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.

6. 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:

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;

7. (FIXED IN X3D 4) Related single-texturing problem: RGB texture color by default modulates material color

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:

  1. It makes Material.diffuseColor and Color useless with RGB textures, which is a shame. GPUs do not have such limitations.
  2. It is inconsistent with MultiTexture behavior, when the modulate mode is the default — regardless if we have RGB or grayscale texture.
  3. In case of our Gouraud shading, the texture color has to be mixed with the whole lighting calculation. Using the "replace" mode by default would mean that shapes are unlit when you use RGB textures. Thus the problem would be very noticeable in Gouraud shading (if only X3D browsers would actually honor this part of X3D specification to the letter).

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.