Texturing component
This component provides extensive textures support.
2D textures may be loaded from image files (ImageTexture),
movie files (MovieTexture) or encoded directly in VRML/X3D files
(PixelTexture, also ImageTexture with data urls).
Multiple textures may be overlayed on a single polygon in a variety
of ways. Texture coordinates may be explicitly provided or automatically
calculated, and may be transformed.
See also X3D specification of Texturing component.
Contents:
Supported nodes:
ImageTexture,
TextureTransform,
TextureCoordinate,
PixelTexture
Note: ImageTexture allows various texture formats,
including JPEG, PNG, BMP, PPM, RGBE. GIF format is supported
by running convert program from
ImageMagick
package "under the hood".
See glViewImage (link to vrmlengine.sf.net) documentation for more detailed list.
Note about alpha channel: alpha channel of the textures
is fully supported, both a simple yes-no transparency (done
by alpha_test in OpenGL) and full range transparency
(done by blending in OpenGL, just like partially transparent materials).
See "override alpha channel detection"
extension description for details.
The bottom line is: everything will magically work fast and look perfect.
Note
about REPLACE vs MODULATE modes: VRML 2 / X3D specifications
say that RGB textures should REPLACE the color (as opposed
to MODULATE the color from lighting calculations, material etc.).
The problem with that is that this makes RGB textures nearly useless
in typical 3D world (when you usually expect textured surfaces to be
properly lit, regardless of RGB or grayscale format).
That's why the default engine behavior contradicts the specification:
it's MODULATE, making an RGB texture modulated by lighting just
like a grayscale texture.
I didn't decide it lightly (noone likes
to deliberately contradict the specification...), but I think this case
is justified --- MODULATE behavior is much more useful and usually
desired, IMO. Feel welcome to send me emails and argument against this.
After all, I'm trying to fit the needs of most people with default
behavior. If many people think that specification is right and I'm dumb,
and the default behavior should follow the spec and be REPLACE,
I'll obey :)
You have menu item in view3dscene RGB Textures Color Mode ->
GL_REPLACE to change this (from code, use
Scene.Attributes.TextureModeRGB := GL_REPLACE;).
MovieTexture
TODO: for now, the sound of the movie is not played.
Notes:
Current implementation keeps the whole encoded video in memory
(images may be discarded after loading (by TVRMLScene.FreeResources
feature), but still the textures for all frames are kept in memory).
The disadvantage is that this makes it impractical to load "real"
movies, normal 2-hour movie will most usually eat all of your memory.
The advantage is that once the movie is loaded, the playback is
super-fast, just like you would display normal nodes with static
textures. Since there's no streaming, decoding etc. in the background
while you browse your models.
In other words, this is quite perfect for movie textures
with game effects, like smoke or flame. But it's not a substitute
for your "real" multimedia movie player.
ffmpeg must be
installed and available on $PATH to actually open any movie format.
See instructions for
installing ffmpeg in view3dscene docs.
Thanks to ffmpeg, we can handle probably any movie format you will
ever need to open.
We can also open movies from images sequence.
This doesn't require ffmpeg, and allows for some tricks
(movie texture with alpha channel).
See "Movies from images sequence"
extension description.
- MultiTexture,
MultiTextureCoordinate,
MultiTextureTransform
TODO: modes
MODULATEALPHA_ADDCOLOR,
MODULATEINVALPHA_ADDCOLOR,
MODULATEINVCOLOR_ADDALPHA
are temporarily not supported.
TODO: source values "DIFFUSE" and "SPECULAR" are treated
the same, as PRIMARY_COLOR (in the sense of OpenGL
ARB_texture_env_combine extension). Primary color contains
material ambient, diffuse and specular factors,
multiplied by lighting properties, summed over all lights.
I don't know of any way to efficiently implement separate
diffuse / specular sources — please report if you do,
otherwise there's no way this can be fixed (note that engine's
multi-texturing must work without shaders too).
TODO: function field is not supported for now.
It's somewhat uncomfortable, corresponding OpenGL settings
(GL_OPERANDx) operate before normal texture unit calculations
are done, while X3D spec requires function to act afterwards.
To implement it generally, I'd have to use 1 more texture unit than
requested (if the last texture unit will use any non-default function).
See clarifications to X3D multi-texturing specification
for more details about multi-texture handling.
- TextureCoordinateGenerator
Supported modes are now "SPHERE", "COORD", "COORD-EYE",
"CAMERASPACEPOSITION", "CAMERASPACENORMAL", "CAMERASPACEREFLECTIONVECTOR".
Note that "CAMERASPACEPOSITION" and
"COORD-EYE" are exactly the same thing. Google confirms it
(e.g.
this source code also treats them as equal and
in this old
bitmanagement spec they mention they are equal).
As an extension, we also allow "WORLDSPACEREFLECTIONVECTOR"
and "WORLDSPACENORMAL" texture generation modes.
TODO: not implemented modes: "SPHERE-LOCAL", "NOISE", "NOISE-EYE",
"SPHERE-REFLECT", "SPHERE-REFLECT-LOCAL".
TextureProperties
minificationFilter, magnificationFilter,
anisotropicDegree are supported. TODO: rest is not.
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.
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) |
Not allowed. |
BLENDTEXTUREALPHA |
Output.RGBA :=
Arg1 * Arg1.A +
Arg2 * (1 - Arg1.A) |
Not allowed. |
BLENDFACTORALPHA |
Output.RGBA :=
Arg1 * MULTI_TEXTURE_CONSTANT.Alpha +
Arg2 * (1 - MULTI_TEXTURE_CONSTANT.Alpha) |
Not allowed. |
BLENDCURRENTALPHA |
Output.RGBA :=
Arg1 * PREVIOUS_STAGE.Alpha +
Arg2 * (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;
Unfortunately, X3D specification is awfully ambiguous
when talking about multi-texturing modes.
Below is my list of spotted problems, and an explanation how we handle it in our
engine (for a short summary, modes table in section above should also
be informative).
I tried to make my implementation following common-sense,
hopefully it will be somewhat compatible to other implementations
and at the same time comfortable to users.
Please report if any other VRML/X3D browser treats it differently,
although it doesn't necessarily mean that I will fix to be compatible
(I know that Octaga seems to revert the order of textures, for starters,
which seems to contradict the spec... No wonder people get this messed
up, since specification is so poor at this point.)
(And if you have any power over the spec, please fix issues mentioned
below in the next version.
It
seems I'm not the only one confused by specs.
I
posted on forum asking for input about this, without any answer so far.)
The mode field may contain an additional blending mode
for the alpha channel. — this is the most troublesome
part of the specification. It contradicts most of the remaining
specification for MultiTexture node — other 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 only 1 mode means that
no mode specific for alpha channel is available.
Doing some smart detection when to expect the next mode to be
for alpha channel seems very risky — since the specification
says absolutely nothing about it. Should I expect separate
mode for alpha channel only when
the texture in current unit has some alpha channel?
This isn't as sensible on the 2nd look, since operating on alpha channel
in multi-texturing makes sense even if current texture unit
doesn't provide any (after all, alpha may come from previous unit,
or constant).
Not to mention that some modes are clearly not possible for
alpha channel.
Our interpretation: One string inside the mode field
always describes behavior for both RGB and alpha channel.
So one string inside mode field always corresponds to one
texture unit, Ok?.
To allow different modes for RGB and alpha channel,
you should just write two mode names inside one string, and separate
them by a comma or slash. Like "MODULATE / REPLACE".
See the modes table in previous section for a list of all possible values.
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.
Our interpretation: "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 tex unit (or mat color
for 1st unit))".
The meaning of "ADDSIGNED" and "ADDSIGNED2X" is unsure.
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,
or each component.
Note that no interpretation results in the output
range of values in -0.5 ... 0.5, so it's not clear what is the "effective"
range they talk about in "ADDSIGNED" spec.
Our interpretation: 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.
Although some modes say explicitly what happens with
alpha channel, some do not. This is especially visible with
"subtract" mode, that will subtract alphas making resulting
alpha = 0 for the most common situation when both textures
have alpha = 1.
Our interpretation: I interpret this all as operating on all
RGBA channels the same way. Comparing with Octaga, results
for "subtract" seem equal this way: with default alphas = 1,
result gets alpha = 0.
If you don't like this behavior, you can specify
separate mode names for RGB and alpha channel, separating them by a slash.
For example, "SUBTRACT / MODULATE" will subtract RGB but modulate alpha.
It's not specified what channels are inverted by function="COMPLEMENT"
value. Only RGB seems most sensible (that's what would seem
usually useful), but it's not written explicitly.
Our interpretation: I plan to treat it as "only RGB",
that is not invert alpha channel. Although for now "function" field
is not handled.
Oh, by the way: the paragraphs for MultiTextureTransform
(texture coordinates for channel 0 are replicated...)
and MultiTextureCoordinate
(identity matrices are assumed...) should be swapped in
the spec :)
Another note: MODULATEINVCOLOR_ADDALPHA mentions
that another mode, somehow forgotten, should exist:
MODULATECOLOR_ADDALPHA (that doesn't invert the color).
DirectDraw Surface (DDS) image format is supported. A number of technical details about DDS implementation are below, but in short: we try to support all formats and all options of DDS in a standard way.
Implementation history:
- DDS support is natively built into the engine. Since I knew that I want to use many of DDS features, like cube maps, 3D textures, mipmaps, compression, I decided the best way to go will be to create my own reader, instead of relying on external tools.
- Other notable open-source implementations of DDS are by GIMP-DDS plugin and new ImageMagick (since 6.3.9).
- While implementing, I was looking at GIMP DDS source code (it's on GNU GPL >= 2 :) ) and MS documentation for DDS.
Cube maps in DDS are supposed to be oriented as usual for DDS:
Which means that they match Direct X "positive/negative x/y/z". For OpenGL rendering we swap positive/negative Y faces (because Direct X has left-handed coordinate system, see here for drawing of DirectX cube map images orientation and compare with OpenGL cube map orientation).
It's also a different orientation then the one of X3D ComposedCubeMap specification (left/right, bottom/top, front/back, with bottom/top on Y axis; X3D orientation needs rotating left,right,front,back images by 180 degrees for OpenGL orientation).
Images in DDS are supposed to be written from top to bottom row, as is the standard in DDS. (One particular tool, AMD CubeMapGen, allows to invert rows of the DDS images to match OpenGL bottom-to-top ordering; don't use this — we expect rows ordered as is standard in DDS, top-to-bottom.) Internally, our engine just inverts the rows for OpenGL (yes, this is doable also for S3TC compressed images.)
Pixel formats supported:
Absolutely all uncompressed non-float pixel formats are supported.
Details:
The formats that are currently loaded optimally are ABGR8, BGR8, AL8, L8. They translate to RGBA8, RGB8 etc. OpenGL formats (reversed order, as DDS color masks are little-endian). Popular ARGB8 and RGB8 are also loaded very fast.
Grayscale (luminance) images are allowed. AL8 and L8 are optimized. Note that grayscale images aren't officially allowed by DDS docs, but at least GIMP-DDS plugin can write it (just sets all R, G and B masks equal, and doesn't set any of DDPF_RGB, DDPF_FOURCC, DDPF_PALETTEINDEXED8).
Also only-alpha images are allowed (another undocumented DDS feature, GIMP-DDS can write it, for now they will result in grayscale(white) with alpha image).
Compressed texture formats handled: DXT1, DXT3, DXT5 are supported.
Texture with DXT1 is always treated like a texture with simple (yes/no)
alpha channel (so it will be rendered with alpha testing) and
DXT3 / DXT5 are always treated like a texture with full range
alpha channel (so they will be rendered with blending).
Both normal (2D) textures and cube maps may be compressed. (There is no compression possible for 3D textures — neighter DDS format allows it, nor do common graphic cards.)
Float textures are for now not supported, so our DDS reader also
doesn't support them.
If DDS file includes mipmaps, and mipmaps are required for texture minification filter, we will use DDS mipmaps (instead of generating mipmaps automatically). Works for all 2D, 3D, cubemap DDS files.
|
|